How to draw pixmap with same anti alias quality as QPushButton::icon?
-
It appears as though
QPushButton::icon
painted viaQPushButton::paintEvent
draws with higher quality antialiasing thanQPainter::drawPixmap
. How is that so? What I can do to callQPainter:: drawPixmap
with higher quality? The lines are pretty jagged whendevicePixelRatio == 1
. Unfortunately, these example pixmaps are withdevicePixelRatio == 2
.With
QPainter::drawPixmap
:With
QPushButton::icon
:Here is the code for the
QPainter::drawPixmap
example:class PixmapButtonHelper: PADDING = 12 RADIUS = 2 def paintPixmapButton(self, e): p = QPainter(self) p.setRenderHint(QPainter.Antialiasing, True) if not self.isEnabled(): # hack p.setOpacity(.5) if self.isDown() or self.isChecked(): if self.isDown(): p.setBrush(util.CONTROL_BG) if self.isChecked(): p.setPen(QPen(QColor('#5a9adb'), 1)) borderRect = self.rect() borderRect.setX(1) borderRect.setY(1) borderRect.setWidth(borderRect.width() - 2) borderRect.setHeight(borderRect.height() - 2) p.drawRoundedRect(borderRect, self.RADIUS, self.RADIUS) iconRect = QRect(self.PADDING, self.PADDING, self.width() - self.PADDING, self.height() - self.PADDING) iconRect.moveCenter(self.rect().center()) if self.isChecked() and self._checkedPixmap: pixmap = self._checkedPixmap else: pixmap = self._uncheckedPixmap p.drawPixmap(iconRect, pixmap, pixmap.rect()) p.end() p = None class PixmapPushButton(QPushButton, PixmapButtonHelper): def __init__(self, parent=None, **kwargs): super(QPushButton, self).__init__(parent) PixmapButtonHelper.__init__(self, **kwargs) def paintEvent(self, e): self.paintPixmapButton(e)
But then, the graininess seems to switch sometimes:
Here is the code for the last example:
class Widget(QWidget): def __init__(self, parent=None): super().__init__(parent) self._pixmap = QPixmap('/Users/patrick/dev/familydiagram/resources/away.png') def paintEvent(self, e): p = QPainter(self) p.drawPixmap(QRect(100, 100, 28, 28), self._pixmap, self._pixmap.rect()) p = None app = QApplication(sys.argv) w = Widget() w.show() w.resize(300, 300) b = QPushButton(QIcon(QPixmap('/Users/patrick/dev/familydiagram/resources/away.png')), '', w) b.setIconSize(QSize(28, 28)) b.setFlat(True) b.resize(28, 28) b.move(150, 100) b.show() app.exec()
This behavior seems unpredictable. Am I missing something in the pixmap scaling somewhere? How can I do this predictably?
-
Hi
ok i could reproduce it using your c++ codeAnd you are right - it seems that drawPixmap somehow not using any render hints.
p.setRenderHint(QPainter::Antialiasing,true); p.setRenderHint(QPainter::SmoothPixmapTransform,true); p.setRenderHint(QPainter::LosslessImageRendering,true); p.drawPixmap(QRect(100, 100, iconsize, iconsize), *m_pixmap, m_pixmap->rect());
makes no difference. I thought it was the scaling to fit rect but even with no scaling, it still not as smooth.
You can make it paint nicely with
QIcon ico; ico.addPixmap(*m_pixmap); ico.paint(&p,QRect(100, 100, iconsize, iconsize));
Runnable test project if others want to check it out.
https://we.tl/t-Q6PXROE2Le -
Hi
Im wondering if you see this on a 1440p or 4k ?
I tried on a 1920x1200 screen but both icons look 100% the same regardless
of button/painter. -
@mrjj said in How to draw pixmap with same anti alias quality as QPushButton::icon?:
Hi
Im wondering if you see this on a 1440p or 4k ?
I tried on a 1920x1200 screen but both icons look 100% the same regardless
of button/painter.I'm not sure how that is relevant because I am pointing out different painting behavior on the same display. The pinging behavior should be the same on the same display.
-
@patrickkidd
well im asking since you talk about devicePixelRatio (will be the same for same display so ?)
and i cannot reproduce it here so i wonder if you are changing something
or its related to HI DPI scaling.I have only see pixelation like that using SVG files with a very
small viewbox (the page) and QIcon would render the pixmap at max that resulution and when drawn it would be blown up to iconsize and look bad.you could try rendering with QIcon paint and see if that makes a differnce.
-
@patrickkidd said in How to draw pixmap with same anti alias quality as QPushButton::icon?:
I'm not sure how that is relevant because I am pointing out different painting behavior on the same display
It is relevant because when you don't set the correct device pixel ratio it will look similar to your problem.
Did you try reproducing with the full code example?
When you provide a c++ example we could test it out.
-
@Christian-Ehrlicher I thought that you cannot set the devicePixelRatio on a QWidget paint device?
-
@patrickkidd
did you see the painters renderhints?
https://doc.qt.io/qt-5/qpainter.html#RenderHint-enum -
@raven-worx said in How to draw pixmap with same anti alias quality as QPushButton::icon?:
@patrickkidd
did you see the painters renderhints?
https://doc.qt.io/qt-5/qpainter.html#RenderHint-enumThose don’t apply to drawing pixmaps
-
@patrickkidd
QPainter::SmoothPixmapTransform
?? -
@Christian-Ehrlicher said in How to draw pixmap with same anti alias quality as QPushButton::icon?:
When you provide a c++ example we could test it out.
#include <QtWidgets> class Widget : public QWidget { public: QPixmap *m_pixmap; Widget(QWidget *parent=nullptr) : QWidget(parent) { m_pixmap = new QPixmap("away.png"); } ~Widget() { delete m_pixmap; } void paintEvent(QPaintEvent *) { QPainter p(this); p.drawPixmap(QRect(100, 100, 28, 28), *m_pixmap, m_pixmap->rect()); } }; int main(int argc, char **argv) { QApplication app(argc, argv); Widget w; w.resize(300, 300); w.show(); QPushButton b(QIcon(QPixmap("away.png")), "", &w); b.setIconSize(QSize(28, 28)); b.setFlat(true); b.resize(28, 28); b.move(150, 100); b.show(); app.exec(); }
@raven-worx said in How to draw pixmap with same anti alias quality as QPushButton::icon?:
@patrickkidd
QPainter::SmoothPixmapTransform
??This has no effect.
-
Here are two screenshots from my app on a display with
devicePixelRatio == 1
. These show the reverse behavior from my code example.
UsingQPushButton::icon
inQPushButton::paintEvent
:Using
QPainter::drawPixmap
Again, notice how the
QIcon
painting viaQPushButton
is better thatQPainter::drawPixmap
in these examples, but the opposite is true in both my python/c++ code examples.The end goal is to get behavior like the first screenshot here when calling
QPainter::drawPixmap
. -
@patrickkidd said in How to draw pixmap with same anti alias quality as QPushButton::icon?:
Again, notice how the QIcon painting via QPushButton is better that QPainter::drawPixmap in these examples, but the opposite is true in both my python/c++ code examples.
So I first would try to change the example to behave the same as your application.
-
@Christian-Ehrlicher said in How to draw pixmap with same anti alias quality as QPushButton::icon?:
@patrickkidd said in How to draw pixmap with same anti alias quality as QPushButton::icon?:
Again, notice how the QIcon painting via QPushButton is better that QPainter::drawPixmap in these examples, but the opposite is true in both my python/c++ code examples.
So I first would try to change the example to behave the same as your application.
That is the original question in this post that I am stuck on.
-
But what's the question - how to create a minimal reproducer? Simplify your program until it either does no longer have to problem or it's really minimal - but this is basic stuff which has to be done for every bug you're hunting.
-
@Christian-Ehrlicher I have provided a basic example that reproduces the problem, and in two different languages. The example clearly shows unpredictable behavior in the way pixmaps are painted.
-
Hi
ok i could reproduce it using your c++ codeAnd you are right - it seems that drawPixmap somehow not using any render hints.
p.setRenderHint(QPainter::Antialiasing,true); p.setRenderHint(QPainter::SmoothPixmapTransform,true); p.setRenderHint(QPainter::LosslessImageRendering,true); p.drawPixmap(QRect(100, 100, iconsize, iconsize), *m_pixmap, m_pixmap->rect());
makes no difference. I thought it was the scaling to fit rect but even with no scaling, it still not as smooth.
You can make it paint nicely with
QIcon ico; ico.addPixmap(*m_pixmap); ico.paint(&p,QRect(100, 100, iconsize, iconsize));
Runnable test project if others want to check it out.
https://we.tl/t-Q6PXROE2Le -
@mrjj said in How to draw pixmap with same anti alias quality as QPushButton::icon?:
QIcon ico; ico.addPixmap(*m_pixmap); ico.paint(&p,QRect(100, 100, iconsize, iconsize));
That certainly solves it. Wow. I looked into the QIcon paint code and found it TLDR;
-
@patrickkidd what exactly have you found?