High-dpi drawTiledPixmap (raster paint engine)

Implement more consistent behavior for drawTiledPixmap(),
which should produce the same visual tiling pattern
independent of display devicePixelRatio

Consider the following pixmaps and draw calls:

  QPixmap px32; // 32x32
  QPixmap px64; // 64x64
  drawTiledPixmap(QRect(0, 0, 128, 128), px32);
  drawTiledPixmap(QRect(0, 0, 128, 128), px64);

On 1x displays this will produce 4x4 and 2x2 tiles,
respectively.

On 2x displays this would previously produce a different
tiling pattern, where the paint engine would tile in
the device pixel coordinate system. Change this to
tile in the device independent coordinate system,
producing the same visual tiling pattern as the 1x case.

It is possible to produce a 4x4 tiling pattern with
high-resolution output from the 64x64 pixmap by setting
the devicePixelRatio:

  QPixmap px64;
  px64.setDevicePixelRatio(2);
  drawTiledPixmap(QRect(0, 0, 128, 128), px64);

This change adds an inverse scale to the image filler
transform that accounts for the pixmap devicePixelRatio.

[ChangeLog][QtGui] QPainter::drawTiledPixmap() now
tiles in the device independent coordinate system.

Change-Id: I4918d274192967f222f181b374571c7c597dcd76
Reviewed-by: Jonathan Courtois <jonathan.courtois@gmail.com>
Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io>
Reviewed-by: 石博文 <sbw@sbw.so>
Reviewed-by: Eirik Aavitsland <eirik.aavitsland@qt.io>
This commit is contained in:
Morten Johan Sørvig 2015-02-24 11:13:31 +01:00 committed by Morten Johan Sørvig
parent a9fc91466c
commit 7160df3a15
3 changed files with 68 additions and 1 deletions

View File

@ -2502,10 +2502,13 @@ void QRasterPaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap,
if (image.depth() == 1)
image = d->rasterBuffer->colorizeBitmap(image, s->pen.color());
if (s->matrix.type() > QTransform::TxTranslate) {
const qreal pixmapDevicePixelRatio = pixmap.devicePixelRatio();
if (s->matrix.type() > QTransform::TxTranslate || pixmapDevicePixelRatio > qreal(1.0)) {
QTransform copy = s->matrix;
copy.translate(r.x(), r.y());
copy.translate(-sr.x(), -sr.y());
const qreal inverseDpr = qreal(1.0) / pixmapDevicePixelRatio;
copy.scale(inverseDpr, inverseDpr);
d->image_filler_xform.clip = d->clip();
d->image_filler_xform.initTexture(&image, s->intOpacity, QTextureData::Tiled);
if (!d->image_filler_xform.blend)

View File

@ -6664,6 +6664,16 @@ QRectF QPainter::boundingRect(const QRectF &r, const QString &text, const QTextO
potentially much more efficient depending on the underlying window
system.
drawTiledPixmap() will produce the same visual tiling pattern on
high-dpi displays (with devicePixelRatio > 1), compared to normal-
dpi displays. Set the devicePixelRatio on the \a pixmap to control
the tile size. For example, setting it to 2 halves the tile width
and height (on both 1x and 2x displays), and produces high-resolution
output on 2x displays.
The \a position offset is always in the painter coordinate system,
indepentent of display devicePixelRatio.
\sa drawPixmap()
*/
void QPainter::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &sp)

View File

@ -334,6 +334,59 @@ void PixmapPainter::paintEvent(QPaintEvent *)
x+=dx * 2; p.drawImage(QRect(x, y, pixmapPointSize * 2, pixmapPointSize * 2), imageLarge);
}
class TiledPixmapPainter : public QWidget
{
public:
QPixmap pixmap1X;
QPixmap pixmap2X;
QPixmap pixmapLarge;
TiledPixmapPainter();
void paintEvent(QPaintEvent *event);
};
TiledPixmapPainter::TiledPixmapPainter()
{
pixmap1X = QPixmap(":/qticon32.png");
pixmap2X = QPixmap(":/qticon32@2x.png");
pixmapLarge = QPixmap(":/qticon64.png");
}
void TiledPixmapPainter::paintEvent(QPaintEvent *event)
{
QPainter p(this);
int xoff = 10;
int yoff = 10;
int tiles = 4;
int pixmapEdge = 32;
int tileAreaEdge = pixmapEdge * tiles;
// Expected behavior for both 1x and 2x dislays:
// 1x pixmap : 4 x 4 tiles
// large pixmap: 2 x 2 tiles
// 2x pixmap : 4 x 4 tiles
//
// On a 2x display the 2x pimxap tiles
// will be drawn in high resolution.
p.drawTiledPixmap(QRect(xoff, yoff, tileAreaEdge, tileAreaEdge), pixmap1X);
yoff += tiles * pixmapEdge + 10;
p.drawTiledPixmap(QRect(xoff, yoff, tileAreaEdge, tileAreaEdge), pixmapLarge);
yoff += tiles * pixmapEdge + 10;
p.drawTiledPixmap(QRect(xoff, yoff, tileAreaEdge, tileAreaEdge), pixmap2X);
// Again, with an offset. The offset is in
// device-independent pixels.
QPoint offset(40, 40); // larger than the pixmap edge size to exercise that code path
yoff = 10;
xoff = 20 + tiles * pixmapEdge ;
p.drawTiledPixmap(QRect(xoff, yoff, tileAreaEdge, tileAreaEdge), pixmap1X, offset);
yoff += tiles * pixmapEdge + 10;
p.drawTiledPixmap(QRect(xoff, yoff, tileAreaEdge, tileAreaEdge), pixmapLarge, offset);
yoff += tiles * pixmapEdge + 10;
p.drawTiledPixmap(QRect(xoff, yoff, tileAreaEdge, tileAreaEdge), pixmap2X, offset);
}
class Labels : public QWidget
{
public:
@ -1119,6 +1172,7 @@ int main(int argc, char **argv)
DemoContainerList demoList;
demoList << new DemoContainer<PixmapPainter>("pixmap", "Test pixmap painter");
demoList << new DemoContainer<TiledPixmapPainter>("tiledpixmap", "Test tiled pixmap painter");
demoList << new DemoContainer<Labels>("label", "Test Labels");
demoList << new DemoContainer<MainWindow>("mainwindow", "Test QMainWindow");
demoList << new DemoContainer<StandardIcons>("standard-icons", "Test standard icons");