I want to create an entity with Qt3D that has a custom image as texture. I came across the QPaintedTextureImage (link leads to Qt 5.9 version for details. Here ist doc for 5.8), which can be written with a QPainter but I don't understand how.
First, this is how I imagine the entity could look like:
[EDIT]: code is edited and works now!
planeEntity = new Qt3DCore::QEntity(rootEntity);
planeMesh = new Qt3DExtras::QPlaneMesh;
planeMesh->setWidth(2);
planeMesh->setHeight(2);
image = new TextureImage; //see below
image->setSize(QSize(100,100));
painter = new QPainter;
image->paint(painter)
planeMaterial = new Qt3DExtras::QDiffuseMapMaterial;
planeMaterial->diffuse()->addTextureImage(image);
planeEntity->addComponent(planeMesh);
planeEntity->addComponent(planeMaterial);
TextureImage is the subclassed QPaintedTextureImage with paint function:
class TextureImage : public Qt3DRender::QPaintedTextureImage
{
public:
void paint(QPainter* painter);
};
What does the QPainter, passed to paint function, need to do in the implementation of paint if I just want to draw a big circle to the planeEntity?
[Edit] Implementation:
void TextureImage::paint(QPainter* painter)
{
//hardcoded values because there was no device()->width/heigth
painter->fillRect(0, 0, 100, 100, QColor(255, 255, 255));
/* Set pen and brush to whatever you want. */
painter->setPen(QPen(QBrush(QColor(255, 0, 255)) ,10));
painter->setBrush(QColor(0, 0, 255));
/*
* Draw a circle (or an ellipse -- the outcome depends very much on
* the aspect ratio of the bounding rectangle amongst other things).
*/
painter->drawEllipse(0, 0, 100, 100);
}
The short answer is... use QPainter exactly the same way you would normally.
void TextureImage::paint (QPainter* painter)
{
int w = painter->device()->width();
int h = painter->device()->height();
/* Clear to white. */
painter->fillRect(0, 0, w, h, QColor(255, 255, 255));
/* Set pen and brush to whatever you want. */
painter->setPen(QPen(QBrush(QColor(0, 0, 0)) ,10));
painter->setBrush(QColor(0, 0, 255));
/*
* Draw a circle (or an ellipse -- the outcome depends very much on
* the aspect ratio of the bounding rectangle amongst other things).
*/
painter->drawEllipse(0, 0, w, h);
}
However, note that you really shouldn't invoke the paint method directly. Instead use update which will cause Qt to schedule a repaint, initialize a QPainter and invoke your overridden paint method with a pointer to that painter.
It might be simpler to dynamically load the image you need in QML.
I had to do it not so long ago and opened a question on SO for it:
Qt3D dynamic texture
Related
I made a C++ program that uses SDL for display and sound. I am implementing gtest to test the graphical functions, for example to draw a pixel :
void Interface::draw_pixel(unsigned short x, unsigned short y, bool on) {
if (on) {
SDL_SetRenderDrawColor(renderer, 255, 255, 255, SDL_ALPHA_OPAQUE);
} else {
SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
}
SDL_Rect rect = {normalize_x(x), normalize_y(y), SIZE_MULTIPLIER_, SIZE_MULTIPLIER_};
SDL_RenderFillRect(renderer, &rect);
}
And to check if this pixel is indeed drawn :
bool Interface::is_pixel_on(int x, int y) {
p_ = (uint8_t *) SDL_GetWindowSurface(window)->pixels + normalize_y(y) * SDL_GetWindowSurface(window)->pitch +
normalize_x(x) * bpp_;
SDL_GetRGB(*(uint32_t *) p_, SDL_GetWindowSurface(window)->format, &rgb_.r, &rgb_.g, &rgb_.b);
return rgb_.r != 0;
}
I would like to draw a pixel and check if it is drawn in C.I. tests. I have tried to create the SDL window with the SDL_WINDOW_HIDDEN flag but it doesn't draw and my test fail.
Do you have any idea or hints how it should be done?
Generally, unit tests don't test gui but logics.
Have a layer of abstraction between library and your logic.
Eg:
namespace mygui
{
RenderFillRect(MyRenderer renderer, MyRect* rect);
};
void Interface::draw_pixel(unsigned short x, unsigned short y, bool on) {
if (on) {
MySetRenderDrawColor(renderer, 255, 255, 255, SDL_ALPHA_OPAQUE);
} else {
MySetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
}
MyRect rect = {normalize_x(x), normalize_y(y), SIZE_MULTIPLIER_, SIZE_MULTIPLIER_};
MyRenderFillRect(renderer, &rect);
}
Mock mygui::RenderFillRect and check if it's called with correct arguments after calling Interface::draw_pixel.
After your elaborated in comment about testing for CHIP8 interpreter, my suggestion is still relevant.
I'll suggest you to mock SDL function calls by using the instruction here.
Another way is mocking Interface::draw_pixel() to make sure one instruction is writing to the correct position.
I'm not sure when do you need to test SDL library. The only scenario I came up with is that it might breaks when you upgrade the library version. However most of time it doesn't get upgraded frequently.
I'm stuck with the difference from the book example and my version of it.
Qt version 5.12.0
As it's shown in the example:
As I see from my output:
First, destination and source In/Atop modes have not the same pictures. And, another noticed thing is, that we can see the rectangle as an additional layer between two.
Code to create the label:
QLabel* lblCreate(const QPainter::CompositionMode& mode){
QLabel* lbl = new QLabel;
lbl->setFixedSize(100, 100);
QRect rect(lbl->contentsRect());
QPainter painter;
// create first image
QImage sourceImage(rect.size(), QImage::Format_ARGB32_Premultiplied);
painter.begin(&sourceImage);
painter.setRenderHint(QPainter::Antialiasing, true);
painter.setBrush(QColor(0, 255, 0));
painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
// draw triangle
painter.drawPolygon(QPolygon() << rect.bottomLeft()
<< QPoint(rect.center().x(), 0)
<< rect.bottomRight());
painter.end();
// create second image
QImage resultImage(rect.size(), QImage::Format_ARGB32_Premultiplied);
painter.begin(&resultImage);
painter.setRenderHint(QPainter::Antialiasing, true);
painter.setPen(QPen(QColor(0, 255, 0), 4));
painter.setBrush(QColor(255, 0, 0));
// draw circle
painter.drawEllipse(rect);
painter.setCompositionMode(mode);
painter.drawImage(rect, sourceImage);
painter.end();
lbl->setPixmap(QPixmap::fromImage(resultImage));
return lbl;}
How it creates in main.cpp:
innerLayout_2->addWidget(lblCreate(QPainter::CompositionMode_Source), 0, 0);
innerLayout_2->addWidget(new QLabel("<CENTER>Source</CENTER>"), 1, 0);
My own suspicion is it may be depend on QImage::Format_ARGB32_Premultiplied.
Or it's mine handmade bug.
Anyway, I would be grateful for any ideas.
Thnx in advance!
The composition mode works on transparent backgrounds, in your case it is not, so you must set it before painting, for this you could use the fill() method:
QImage sourceImage(rect.size(), QImage::Format_ARGB32_Premultiplied);
sourceImage.fill(Qt::transparent);
QImage resultImage(rect.size(), QImage::Format_ARGB32_Premultiplied);
resultImage.fill(Qt::transparent);
In QML documentation I found an example of custom type (defined from C++) to draw on it with QPainter:
Header:
#include <QtQuick/QQuickPaintedItem>
class PieChart : public QQuickPaintedItem
{
...
public:
void paint(QPainter *painter);
...
};
Source:
void PieChart::paint(QPainter *painter)
{
QPen pen(m_color, 2);
painter->setPen(pen);
painter->setRenderHints(QPainter::Antialiasing, true);
painter->drawPie(boundingRect().adjusted(1, 1, -1, -1), 90 * 16, 290 * 16);
}
How can I derive a type to draw (e.g. a line) on it asynchronously with QPainter?
Thanks!
You have multiple ways do draw asynchronously:
1) Draw your content into a QImage at some point (maybe even in a seperate thread), and in QQuickPaintedItem::paint(), simply draw that image.
2) Use the QtQuick Canvas. Note that this is drawing in JavaScript, not in C++, but under the hood it is actually QPainter commands. The Canvas supports various render strategies, among others doing the drawing in a dedicated thread or in the render thread
I have a self coded rectangle (not using QRect for educational purposes), which looks like so:
class Block {
private: // also has getters and setters for this stuff
int m_x;
int m_y;
uint m_width;
uint m_height;
QColor m_color;
public:
Block(int x = 0, int y = 0, uint w = 64, uint h = 64);
Block(const QColor &color, int x = 0, int y = 0, uint w = 64, uint h = 64);
void paint(QPainter &painter) const
{
painter.fillRect(m_x, m_y, m_width, m_height, m_color);
}
};
Now I'd like to add a support for images, so the block can either have a color or an image (if both provided, image will be used).
The problem is, there are too many classes to represent images (QPixmap, QImage, QIcon) and I have no idea which one should I use.
What are the differences, which one is best suited for simply drawing a resource image into a rectangle?
If you want to display the image on screen, use QPixmap. If you want to modify image, load or save it to file, use QImage.
QIcon is based on QPixmap and provides ability to choose one of many pixmaps based on requested size and state. QIcon is probably not what you want.
From the documentation:
Qt provides four classes for handling image data: QImage, QPixmap, QBitmap and QPicture. QImage is designed and optimized for I/O, and for direct pixel access and manipulation, while QPixmap is designed and optimized for showing images on screen. QBitmap is only a convenience class that inherits QPixmap, ensuring a depth of 1. Finally, the QPicture class is a paint device that records and replays QPainter commands.
The QIcon class provides scalable icons in different modes and states. A QIcon can generate smaller, larger, active, and disabled pixmaps from the set of pixmaps it is given. Such pixmaps are used by Qt widgets to show an icon representing a particular action.
in a C++ MFC application. using the dc of ( CPaintDC dc(this); )
How do i draw a rectangle ( LPRECT ) with an alpha transparency that i can adjust.?
Following is an example c# code which i need to convert into C++
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
Color color = Color.FromArgb(75,Color.Red); //sets color Red with 75% alpha transparency
Rectangle rectangle = new Rectangle(100,100,400,400);
g.FillRectangle(new SolidBrush(color), rectangle); //draws the rectangle with the color set.
}
You need to look into GDI+. Its a bit of a faff but you can create a "Graphics" object as follows:
Gdiplus::Graphics g( dc.GetSafeHdc() );
Gdiplus::Color color( 192, 255, 0, 0 );
Gdiplus::Rect rectangle( 100, 100, 400, 400 );
Gdiplus::SolidBrush solidBrush( color );
g.FillRectangle( &solidBrush, rectangle );
Don't forget to do
#include <gdiplus.h>
and to call
GdiplusStartup(...);
somewhere :)
You'll notice it's pretty damned similar to your C# code ;)
Its worth noting that the 75 you put in your FromArgb code doesn't set 75% alpha it actually sets 75/255 alpha or ~29% alpha.
GDI (and thus MFC) has no decent support for drawing with an alpha. But GDI+ is available in C++ code as well. Use #include <gdiplus.h> and initialize it with GdiplusStartup(). You can use the Graphics class, create one with its Graphics(HDC) constructor from your CPaintDC. And use its FillRectangle() method. The SDK docs are here.
int StartHoriz,StartVert,BarWidth,BarHeight; // rect start, width and height
StartHoriz=0;
StartVert=100;
width = 100;
height=120;
CDC* pCDC = GetDC(); // Get CDC pointer
CRect Rect(StartHoriz,StartVert,BarWidth,BarHeight); //create rectangle dimensions
pCDC->Rectangle(Rect); //draw rectangle