I'm trying to use QPainter to draw items onto a QImage , but since I can't predict the exact size of this QImage , I can't use QImage::save() , it always tell me:
QPainter::begin: Paint device returned engine == 0, type: 3
But if I specify the image height and width when declaring this QImage , it works smoothly:
QImage output = QImage (500 , 500 , QImage::Format_ARGB32);
QImage, QPixmap, etc. require data to be allocated before drawing can begin. Using the default constructor of QImage does not allocate any memory, so image.isNull() == true. So when you call QPainter::begin() (probably indirectly by creating one with the QImage as the paint device) it can't draw into any memory because it isn't there.
From the QPainter::begin() docs:
QPixmap image(0, 0);
painter->begin(&image); // impossible - image.isNull() == true;
So you have to come up with a size before drawing. In your situation, the best thing to do would be to decide on a maximum size (or calculate one if feasible), and then once you do know the exact size - crop the image.
Alternatively, you can draw on a QGraphicsScene which will expand automatically as you add items on it, then save only the painted area given by QGraphicsScene::itemsBoundingRect():
QGraphicsScene scene;
scene.addItem(...);
QImage image(scene.itemsBoundingRect().size(), QImage::Format_ARGB32);
QPainter painter(&image);
scene.render(&painter, image.rect(), scene.itemsBoundingRect());
Related
Some rendering libraries allow to set a 'color mod' (for example, in SDL2 you do it with SDL_SetTextureColorMod) when drawing a texture, which would effectively multiply the colours of the pixels by a given value before drawing. What is the best way to achieve this in Qt5, for example, when drawing a QPixmap with QPainter::drawPixmap? So far the only option I see is to use a temporary pixmap, fill it with the colour by which I want to multiply, then draw on it with QPainter::CompositionMode_Multiply and then draw the result to the target device. Is there a more straightforward way that maybe does not include drawing to a temporary pixmap?
You can do without the temporay pixmap by drawing a rect with size of your pixmap in your target:
QPixmap const src(":/images/img.png");
painter->fillRect(QRect(QPoint(0, 0), src.size()), Qt::red);
painter->setCompositionMode(QPainter::CompositionMode_Multiply);
painter->drawPixmap(QPoint(0, 0), src);
If your pixmap has transparent regions, you can add painter->setClipRegion(src.mask()); before calling fillRect.
How to get the current mouse cursor size measured in pixels? I tried mywidget.cursor().pixmap().size() but it returns (0,0) for the standard arrow cursor.
(I need this to show a special tool tip label which would appear just below the cursor and would follow the cursor and I cannot use the standard QToolTip for certain reasons - delays etc. I already have a nice, working solution but if I display the label exactly at the cursor position, the cursor is painted over it hiding some text on the label. Of course I could move it down using some 'magic' number like 32 pixels, but this would cause me bad stomach feelings.)
You can't do this with the standard cursors. The QCursor methods only work with custom bitmaps or pixmaps. So you will either have to use your own cursors, or estimate the size.
A quick web-search suggests that the standard cursors can vary in size and there is no fixed maximum (although that probably depends on the platform). For example, on X11, the size range usually includes 16, 24, 32, 48, and 64, but other sizes may be possible (even as large as 512). The default is normally 32.
If you need accuracy, it would seem that using custom cursors is the only way to solve this problem.
You could use the code that is used in QTipLabel::placeTip, which offsets tooltips based on cursor size:
const int screenIndex = /*figure out what screen you are on*/;
const QScreen *screen = QGuiApplication::screens().value(screenIndex, QGuiApplication::primaryScreen());
if (const QPlatformScreen *platformScreen = screen ? screen->handle() : nullptr) {
QPlatformCursor *cursor = platformScreen->cursor();
const QSize nativeSize = cursor ? cursor->size() : QSize(16, 16);
const QSize cursorSize = QHighDpi::fromNativePixels(nativeSize, platformScreen);
}
To do this you do need at least one private header:
#include <qpa/qplatformscreen.h>
#include <qpa/qplatformcursor.h>
#include <QtGui/private/qhighdpiscaling_p.h>
If it doesn't have to be portable you can look at the size implementation of the QPlatformCursor implementation for the platform you're targeting (e.g. QWindowsCursor::size()) and use that code.
I'm trying to draw an icon(.png) inside a QWidget with QPainter::drawPixmap()
:
QPixmap _source = "/.../.png";
painter.setRenderHint(QPainter::HighQualityAntialiasing);
painter.drawPixmap(rect(), _source);
but in comparing to QLabel (for example) and in lower size (19*19 in my case) the result isn't perfect.
What can I do?
****Edit****
QLabel with pixmap # size 19*19:
My painting # size 19*19 via SmoothPixmapTransform render type:
You are setting the wrong render hint, you need QPainter::SmoothPixmapTransform to get smooth resizing. By default the nearest neighbor method is used, which is fast but has very low quality and pixelates the result.
QPainter::HighQualityAntialiasing is for when drawing lines and filling paths and such, i.e. when rasterizing geometry, it has no effect on drawing raster graphics.
EDIT: It seems there is only so much SmoothPixmapTransform can do, and when the end result is so tiny, it isn't much:
QPainter p(this);
QPixmap img("e://img.png");
p.drawPixmap(QRect(50, 0, 50, 50), img);
p.setRenderHint(QPainter::SmoothPixmapTransform);
p.drawPixmap(QRect(0, 0, 50, 50), img);
img = img.scaled(50, 50, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
p.drawPixmap(100, 0, img);
This code produces the following result:
There is barely any difference between the second and third image, manually scaling the source image to the desired dimensions and drawing it produces the best result. This is certainly not right, it is expected from SmoothTransformation to produce the same result, but for some reason its scaling is inferior to the scale() method of QPixmap.
Is there an easy way to get rid of tiling when using a QBrush with texture?
QImage* texture = CreateQImage(); // create texture
QBrush* brush = new QBrush(*texture); // create texture brush
QPainter* painter = CreateQPainter(); // create painter
painter->fillRectangle(0, 0, 500, 500, *brush);
Suppose we have a QImage texture with size of 20x20 pixels. The code above will tile this texture all across the rectangle being filled. Is there an easy way to draw only a single instance of this texture? The QBrush usage is crucial.
Theoretically, I could reload every fill and draw method of the QPainter that takes a QBrush as input and use a QPainter.drawImage() method, but I think there must be a simplier way.
Thanks, Tony.
I don't think there is (see Qt::BrushStyle - the only style with a texture tiles it), and it wouldn't really make sens IMO. If you just want one image, use the drawImage functions as you've stated.
(One of the problems with not tiling is: what do you fill the rest of the rectangle with? Nothing? Some default background color? Some other QBrush attribute?)
Any idea on how to erase a portion of a bitmap just like Android's PorterDuff Mode?
I am creating an application like Paint, and I don't know how to erase the drawings I have written using the pen.
Any idea regarding this one?
Thank you!
I suggest you use the QPainter class which can perform various drawing operations on a QBitmap (more precisely: it draws on a QPaintDevice, from which QBitmap derives).
Among the various operations of the painter, there is QPainter::eraseRect() which can erase a portion of a QBitmap.
This is the way you use it:
QBitmap b;
QPainter p( &b );
p.eraseRect( x, y, w, h ); // With x, y, w and h defining the portion
// of your bitmap you want to erase