summaryrefslogtreecommitdiffstats
path: root/src/gui/painting
diff options
context:
space:
mode:
authorMorten Johan Sørvig <morten.sorvig@digia.com>2012-11-20 11:34:52 +0100
committerThe Qt Project <gerrit-noreply@qt-project.org>2012-12-01 08:33:20 +0100
commit5e61bbe586519c3d9bc636153d32e810da4e59a3 (patch)
tree67d67ef644be72ee5b3d685c9a22538d7ec1e01d /src/gui/painting
parentc8dc41bacdc30026cb79d0d6c72255312084bfe3 (diff)
Basic high-dpi "retina" support for Qt 5.
Bring Qt 5 on par with Qt 4, prepare for more comprehensive support later on. Introduce device independent pixels (dips), device pixels, and devicePixelRatio. Add high-dpi support to QPainter, QGLWidget, the cocoa platform plugin, mac and fusion styles. Dips are similar to CSS pixels, Apple points and Android density-independent pixels. Device pixels are pixels in the backing store/physical pixels on screen. devicePixelRatio is the ratio between them, which is 1.0 on standard displays and 2.0 on "retina" displays. New API: QImage::devicePixelRatio() and setDevicePixelRatio() QPixmap::devicePixelRatio() and setDevicePixelRatio() QWindow::devicePixelRatio() QScreen::devicePixelRatio() QGuiApplicaiton::devicePixelRatio() Change-Id: If98c3ca9bfdf0e1bdbcf7574cd5b912c9ff63856 Reviewed-by: Morten Johan Sørvig <morten.sorvig@digia.com> Reviewed-by: Gunnar Sletta <gunnar.sletta@digia.com>
Diffstat (limited to 'src/gui/painting')
-rw-r--r--src/gui/painting/qpaintengine_raster.cpp29
-rw-r--r--src/gui/painting/qpaintengineex.cpp4
-rw-r--r--src/gui/painting/qpainter.cpp48
-rw-r--r--src/gui/painting/qpainter_p.h1
4 files changed, 65 insertions, 17 deletions
diff --git a/src/gui/painting/qpaintengine_raster.cpp b/src/gui/painting/qpaintengine_raster.cpp
index 2841a583d5..46648fe297 100644
--- a/src/gui/painting/qpaintengine_raster.cpp
+++ b/src/gui/painting/qpaintengine_raster.cpp
@@ -1072,9 +1072,11 @@ void QRasterPaintEnginePrivate::systemStateChanged()
exDeviceRect = deviceRect;
Q_Q(QRasterPaintEngine);
- q->state()->strokeFlags |= QPaintEngine::DirtyClipRegion;
- q->state()->fillFlags |= QPaintEngine::DirtyClipRegion;
- q->state()->pixmapFlags |= QPaintEngine::DirtyClipRegion;
+ if (q->state()) {
+ q->state()->strokeFlags |= QPaintEngine::DirtyClipRegion;
+ q->state()->fillFlags |= QPaintEngine::DirtyClipRegion;
+ q->state()->pixmapFlags |= QPaintEngine::DirtyClipRegion;
+ }
}
void QRasterPaintEnginePrivate::updateMatrixData(QSpanData *spanData, const QBrush &b, const QTransform &m)
@@ -2143,9 +2145,10 @@ void QRasterPaintEngine::drawImage(const QPointF &p, const QImage &img)
Q_D(QRasterPaintEngine);
QRasterPaintEngineState *s = state();
+ qreal scale = img.devicePixelRatio();
- if (s->matrix.type() > QTransform::TxTranslate) {
- drawImage(QRectF(p.x(), p.y(), img.width(), img.height()),
+ if (scale > 1.0 || s->matrix.type() > QTransform::TxTranslate) {
+ drawImage(QRectF(p.x(), p.y(), img.width() / scale, img.height() / scale),
img,
QRectF(0, 0, img.width(), img.height()));
} else {
@@ -2349,6 +2352,22 @@ void QRasterPaintEngine::drawImage(const QRectF &r, const QImage &img, const QRe
return;
}
} else {
+ // Test for optimized high-dpi case: 2x source on 2x target. (Could be generalized to nX.)
+ bool sourceRect2x = r.width() * 2 == sr.width() && r.height() * 2 == sr.height();
+ bool scale2x = (s->matrix.m11() == qreal(2)) && (s->matrix.m22() == qreal(2));
+ if (s->matrix.type() == QTransform::TxScale && sourceRect2x && scale2x) {
+ SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
+ if (func) {
+ QPointF pt(r.x() * 2 + s->matrix.dx(), r.y() * 2 + s->matrix.dy());
+ if (!clip) {
+ d->drawImage(pt, img, func, d->deviceRect, s->intOpacity, sr.toRect());
+ return;
+ } else if (clip->hasRectClip) {
+ d->drawImage(pt, img, func, clip->clipRect, s->intOpacity, sr.toRect());
+ return;
+ }
+ }
+ }
SrcOverScaleFunc func = qScaleFunctions[d->rasterBuffer->format][img.format()];
if (func && (!clip || clip->hasRectClip)) {
func(d->rasterBuffer->buffer(), d->rasterBuffer->bytesPerLine(),
diff --git a/src/gui/painting/qpaintengineex.cpp b/src/gui/painting/qpaintengineex.cpp
index 113cbd8a8e..2c41ab9ff2 100644
--- a/src/gui/painting/qpaintengineex.cpp
+++ b/src/gui/painting/qpaintengineex.cpp
@@ -939,12 +939,12 @@ void QPaintEngineEx::drawPolygon(const QPoint *points, int pointCount, PolygonDr
void QPaintEngineEx::drawPixmap(const QPointF &pos, const QPixmap &pm)
{
- drawPixmap(QRectF(pos, pm.size()), pm, pm.rect());
+ drawPixmap(QRectF(pos, pm.size() / pm.devicePixelRatio()), pm, pm.rect());
}
void QPaintEngineEx::drawImage(const QPointF &pos, const QImage &image)
{
- drawImage(QRectF(pos, image.size()), image, image.rect());
+ drawImage(QRectF(pos, image.size() / image.devicePixelRatio()), image, image.rect());
}
void QPaintEngineEx::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &s)
diff --git a/src/gui/painting/qpainter.cpp b/src/gui/painting/qpainter.cpp
index 0cfe953e43..8ec9c1648f 100644
--- a/src/gui/painting/qpainter.cpp
+++ b/src/gui/painting/qpainter.cpp
@@ -225,6 +225,18 @@ QTransform QPainterPrivate::viewTransform() const
return QTransform();
}
+QTransform QPainterPrivate::hidpiScaleTransform() const
+{
+#ifdef Q_OS_MAC
+ // Limited feature introduction for Qt 5.0.0, remove ifdef in a later release.
+ if (device->physicalDpiX() == 0 || device->logicalDpiX() == 0)
+ return QTransform();
+ const qreal deviceScale = (device->physicalDpiX() / device->logicalDpiX());
+ if (deviceScale > 1.0)
+ return QTransform::fromScale(deviceScale, deviceScale);
+#endif
+ return QTransform();
+}
/*
\internal
@@ -641,6 +653,8 @@ void QPainterPrivate::updateMatrix()
else
state->dirtyFlags |= QPaintEngine::DirtyTransform;
+ state->matrix *= hidpiScaleTransform();
+
// printf("VxF=%d, WxF=%d\n", state->VxF, state->WxF);
// qDebug() << " --- using matrix" << state->matrix << redirection_offset;
}
@@ -1827,7 +1841,14 @@ bool QPainter::begin(QPaintDevice *pd)
Q_ASSERT(d->engine->isActive());
- if (!d->state->redirectionMatrix.isIdentity())
+#ifdef Q_OS_MAC
+ // Limited feature introduction for Qt 5.0.0, remove ifdef in a later release.
+ const bool isHighDpi = (d->device->physicalDpiX() == 0 || d->device->logicalDpiX() == 0) ?
+ false : (d->device->physicalDpiX() / d->device->logicalDpiX() > 1);
+#else
+ const bool isHighDpi = false;
+#endif
+ if (!d->state->redirectionMatrix.isIdentity() || isHighDpi)
d->updateMatrix();
Q_ASSERT(d->engine->isActive());
@@ -5092,7 +5113,8 @@ void QPainter::drawPixmap(const QPointF &p, const QPixmap &pm)
x += d->state->matrix.dx();
y += d->state->matrix.dy();
}
- d->engine->drawPixmap(QRectF(x, y, w, h), pm, QRectF(0, 0, w, h));
+ int scale = pm.devicePixelRatio();
+ d->engine->drawPixmap(QRectF(x, y, w / scale, h / scale), pm, QRectF(0, 0, w, h));
}
}
@@ -5122,6 +5144,11 @@ void QPainter::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr)
qreal sw = sr.width();
qreal sh = sr.height();
+ // Get pixmap scale. Use it when calculating the target
+ // rect size from pixmap size. For example, a 2X 64x64 pixel
+ // pixmap should result in a 32x32 point target rect.
+ const qreal pmscale = pm.devicePixelRatio();
+
// Sanity-check clipping
if (sw <= 0)
sw = pm.width() - sx;
@@ -5130,9 +5157,9 @@ void QPainter::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr)
sh = pm.height() - sy;
if (w < 0)
- w = sw;
+ w = sw / pmscale;
if (h < 0)
- h = sh;
+ h = sh / pmscale;
if (sx < 0) {
qreal w_ratio = sx * w/sw;
@@ -5345,6 +5372,7 @@ void QPainter::drawImage(const QPointF &p, const QImage &image)
int w = image.width();
int h = image.height();
+ qreal scale = image.devicePixelRatio();
d->updateState(d->state);
@@ -5368,8 +5396,7 @@ void QPainter::drawImage(const QPointF &p, const QImage &image)
setBrush(brush);
setPen(Qt::NoPen);
setBrushOrigin(QPointF(0, 0));
-
- drawRect(image.rect());
+ drawRect(QRect(QPoint(0, 0), image.size() / scale));
restore();
return;
}
@@ -5380,7 +5407,7 @@ void QPainter::drawImage(const QPointF &p, const QImage &image)
y += d->state->matrix.dy();
}
- d->engine->drawImage(QRectF(x, y, w, h), image, QRectF(0, 0, w, h), Qt::AutoColor);
+ d->engine->drawImage(QRectF(x, y, w / scale, h / scale), image, QRectF(0, 0, w, h), Qt::AutoColor);
}
void QPainter::drawImage(const QRectF &targetRect, const QImage &image, const QRectF &sourceRect,
@@ -5399,6 +5426,7 @@ void QPainter::drawImage(const QRectF &targetRect, const QImage &image, const QR
qreal sy = sourceRect.y();
qreal sw = sourceRect.width();
qreal sh = sourceRect.height();
+ qreal imageScale = image.devicePixelRatio();
// Sanity-check clipping
if (sw <= 0)
@@ -5408,9 +5436,9 @@ void QPainter::drawImage(const QRectF &targetRect, const QImage &image, const QR
sh = image.height() - sy;
if (w < 0)
- w = sw;
+ w = sw / imageScale;
if (h < 0)
- h = sh;
+ h = sh / imageScale;
if (sx < 0) {
qreal w_ratio = sx * w/sw;
@@ -8235,7 +8263,7 @@ QTransform QPainter::combinedTransform() const
qWarning("QPainter::combinedTransform: Painter not active");
return QTransform();
}
- return d->state->worldMatrix * d->viewTransform();
+ return d->state->worldMatrix * d->viewTransform() * d->hidpiScaleTransform();
}
/*!
diff --git a/src/gui/painting/qpainter_p.h b/src/gui/painting/qpainter_p.h
index 0e46cee4b5..fd5d560141 100644
--- a/src/gui/painting/qpainter_p.h
+++ b/src/gui/painting/qpainter_p.h
@@ -249,6 +249,7 @@ public:
}
QTransform viewTransform() const;
+ QTransform hidpiScaleTransform() const;
static bool attachPainterPrivate(QPainter *q, QPaintDevice *pdev);
void detachPainterPrivate(QPainter *q);