Screen-space painting with a QQuickPaintedItem at a fractional device pixel ratio
-
I have a QQuickPaintedItem based class, and I want to be able to draw in raw screen pixels. For example, if my device pixel ratio is 1.5, and in my QQuickPaintedItem::paint override I call painter->drawPoint(...), I want to look at the output on screen and get a single pixel, but instead what I see is a single pixel scaled up by 1.5. Initially I thought this might be because the underlying paint device (QImage) has a DPR of 1.0 so I tried to set its DPR to match the screen with the clumsy method of:
auto* image = dynamic_cast<QImage*>(painter->device()); image->setDevicePixelRatio(window()->devicePixelRatio());
But this doesn't seem to have any effect. Can anyone explain how to get what I'm after or what I've misunderstood here? For what it's worth, my ultimate goal is to render a pixmap in screen pixel space, with no scaling applied. I'm only drawing points in order to try and understand what's going on, and where the scale(s) are applied.
-
To partially answer my own question, the primary culprit appears to be the QPainter transform. If I set this to the identity transform (painter->setTransform(QTransform()) it works (kind of) as expected. Having said that, there is clearly still some scaling going on. As an example, the source pixmap is...
...but when it's rendered by Qt with a DPR of 1.5, it looks like this...
As you can see that while dimensionally correct, it's a little blurry compared to the source. It looks to me like it's been scaled by the DPR, then scaled again by 1/DPR, but I can't see where this is happening, or why. I guess I can live with this, as it looks passable, but it would be good to understand what's happening here.
The relevant code, for what it's worth:
void IconItem::paint(QPainter* painter) { // When the DPR is not 1.0, by default it gets employed in the painter transform, // but that means that our icons get scaled by it, which we don't want to happen, // instead preferring to render them at a 1-to-1 logical-to-screen ratio, so use // the identity transform painter->setTransform(QTransform()); auto mode = _selected ? QIcon::Selected : QIcon::Normal; auto size = painter->viewport().size(); auto pixmap = _icon.pixmap(size, 1.0, isEnabled() ? mode : QIcon::Disabled, _on ? QIcon::On : QIcon::Off); auto x = (size.width() - pixmap.width()) / 2; auto y = (size.height() - pixmap.height()) / 2; painter->drawPixmap(x, y, pixmap); }