From 5611b66c901867a2ea2855c90454c2267ef197dd Mon Sep 17 00:00:00 2001 From: Louai Al-Khanji Date: Wed, 23 Apr 2014 14:23:38 +0300 Subject: [PATCH] Direct2D QPA: Optimize Clipping Use axis aligned clips when possible instead of layer-clipping. This can be much faster when a lot of clipping operations take place. Change-Id: I6865d69fc917a7da858033b4c362b307724d9006 Reviewed-by: Risto Avila Reviewed-by: Friedemann Kleint Reviewed-by: Andrew Knight --- src/gui/painting/qpaintengine_raster.cpp | 33 +---- src/gui/painting/qvectorpath_p.h | 28 +++- .../direct2d/qwindowsdirect2dpaintengine.cpp | 120 +++++++++--------- .../direct2d/qwindowsdirect2dpaintengine.h | 6 +- 4 files changed, 96 insertions(+), 91 deletions(-) diff --git a/src/gui/painting/qpaintengine_raster.cpp b/src/gui/painting/qpaintengine_raster.cpp index bfcb24ae3a..ce8c1d1ca7 100644 --- a/src/gui/painting/qpaintengine_raster.cpp +++ b/src/gui/painting/qpaintengine_raster.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtGui module of the Qt Toolkit. @@ -235,21 +235,6 @@ static const QRectF boundingRect(const QPointF *points, int pointCount) } #endif -template static inline bool isRect(const T *pts, int elementCount) { - return (elementCount == 5 // 5-point polygon, check for closed rect - && pts[0] == pts[8] && pts[1] == pts[9] // last point == first point - && pts[0] == pts[6] && pts[2] == pts[4] // x values equal - && pts[1] == pts[3] && pts[5] == pts[7] // y values equal... - && pts[0] < pts[4] && pts[1] < pts[5] - ) || - (elementCount == 4 // 4-point polygon, check for unclosed rect - && pts[0] == pts[6] && pts[2] == pts[4] // x values equal - && pts[1] == pts[3] && pts[5] == pts[7] // y values equal... - && pts[0] < pts[4] && pts[1] < pts[5] - ); -} - - static void qt_ft_outline_move_to(qfixed x, qfixed y, void *data) { ((QOutlineMapper *) data)->moveTo(QPointF(qt_fixed_to_real(x), qt_fixed_to_real(y))); @@ -1193,22 +1178,14 @@ void QRasterPaintEngine::clip(const QVectorPath &path, Qt::ClipOperation op) Q_D(QRasterPaintEngine); QRasterPaintEngineState *s = state(); - const qreal *points = path.points(); - const QPainterPath::ElementType *types = path.elements(); - // There are some cases that are not supported by clip(QRect) if (op != Qt::IntersectClip || !s->clip || s->clip->hasRectClip || s->clip->hasRegionClip) { if (s->matrix.type() <= QTransform::TxScale - && ((path.shape() == QVectorPath::RectangleHint) - || (isRect(points, path.elementCount()) - && (!types || (types[0] == QPainterPath::MoveToElement - && types[1] == QPainterPath::LineToElement - && types[2] == QPainterPath::LineToElement - && types[3] == QPainterPath::LineToElement))))) { + && path.isRect()) { #ifdef QT_DEBUG_DRAW qDebug() << " --- optimizing vector clip to rect clip..."; #endif - + const qreal *points = path.points(); QRectF r(points[0], points[1], points[4]-points[0], points[5]-points[1]); if (setClipRectInDeviceCoords(s->matrix.mapRect(r).toRect(), op)) return; @@ -1939,7 +1916,7 @@ void QRasterPaintEngine::drawPolygon(const QPointF *points, int pointCount, Poly #endif Q_ASSERT(pointCount >= 2); - if (mode != PolylineMode && isRect((qreal *) points, pointCount)) { + if (mode != PolylineMode && QVectorPath::isRect((qreal *) points, pointCount)) { QRectF r(points[0], points[2]); drawRects(&r, 1); return; @@ -1980,7 +1957,7 @@ void QRasterPaintEngine::drawPolygon(const QPoint *points, int pointCount, Polyg qDebug() << " - " << points[i]; #endif Q_ASSERT(pointCount >= 2); - if (mode != PolylineMode && isRect((int *) points, pointCount)) { + if (mode != PolylineMode && QVectorPath::isRect((int *) points, pointCount)) { QRect r(points[0].x(), points[0].y(), points[2].x() - points[0].x(), diff --git a/src/gui/painting/qvectorpath_p.h b/src/gui/painting/qvectorpath_p.h index e97d6e1dce..ca95c32597 100644 --- a/src/gui/painting/qvectorpath_p.h +++ b/src/gui/painting/qvectorpath_p.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtGui module of the Qt Toolkit. @@ -167,6 +167,32 @@ public: return 0; } + template static inline bool isRect(const T *pts, int elementCount) { + return (elementCount == 5 // 5-point polygon, check for closed rect + && pts[0] == pts[8] && pts[1] == pts[9] // last point == first point + && pts[0] == pts[6] && pts[2] == pts[4] // x values equal + && pts[1] == pts[3] && pts[5] == pts[7] // y values equal... + && pts[0] < pts[4] && pts[1] < pts[5] + ) || + (elementCount == 4 // 4-point polygon, check for unclosed rect + && pts[0] == pts[6] && pts[2] == pts[4] // x values equal + && pts[1] == pts[3] && pts[5] == pts[7] // y values equal... + && pts[0] < pts[4] && pts[1] < pts[5] + ); + } + + inline bool isRect() const + { + const QPainterPath::ElementType * const types = elements(); + + return (shape() == QVectorPath::RectangleHint) + || (isRect(points(), elementCount()) + && (!types || (types[0] == QPainterPath::MoveToElement + && types[1] == QPainterPath::LineToElement + && types[2] == QPainterPath::LineToElement + && types[3] == QPainterPath::LineToElement))); + } + private: Q_DISABLE_COPY(QVectorPath) diff --git a/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp b/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp index 86f78a35d5..f162f5e93c 100644 --- a/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp +++ b/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp @@ -52,6 +52,7 @@ #include "qwindowsfontdatabase.h" #include "qwindowsintegration.h" +#include #include #include #include @@ -80,9 +81,14 @@ enum { //Clipping flags enum { - UserClip = 0x1, - SimpleSystemClip = 0x2 + SimpleSystemClip = 0x1 }; + +enum ClipType { + AxisAlignedClip, + LayerClip +}; + #define D2D_TAG(tag) d->dc()->SetTags(tag, tag) Q_GUI_EXPORT QImage qt_imageForBrush(int brushStyle, bool invert); @@ -320,8 +326,8 @@ public: QWindowsDirect2DBitmap *bitmap; - QPainterPath clipPath; unsigned int clipFlags; + QStack pushedClips; QPointF currentBrushOrigin; @@ -389,30 +395,55 @@ public: pen.brush->SetOpacity(opacity); } - void pushClip() + void pushClip(const QVectorPath &path) { - popClip(); + Q_Q(QWindowsDirect2DPaintEngine); - ComPtr geometry = painterPathToID2D1PathGeometry(clipPath, antialiasMode() == D2D1_ANTIALIAS_MODE_ALIASED); - if (!geometry) - return; + if (path.isEmpty()) { + D2D_RECT_F rect = {0, 0, 0, 0}; + dc()->PushAxisAlignedClip(rect, antialiasMode()); + pushedClips.push(AxisAlignedClip); + } else if (path.isRect() && (q->state()->matrix.type() <= QTransform::TxScale)) { + const qreal * const points = path.points(); + D2D_RECT_F rect = { + points[0], // left + points[1], // top + points[2], // right, + points[5] // bottom + }; - dc()->PushLayer(D2D1::LayerParameters1(D2D1::InfiniteRect(), - geometry.Get(), - antialiasMode(), - D2D1::IdentityMatrix(), - 1.0, - NULL, - D2D1_LAYER_OPTIONS1_INITIALIZE_FROM_BACKGROUND), - NULL); - clipFlags |= UserClip; + dc()->PushAxisAlignedClip(rect, antialiasMode()); + pushedClips.push(AxisAlignedClip); + } else { + ComPtr geometry = vectorPathToID2D1PathGeometry(path, antialiasMode() == D2D1_ANTIALIAS_MODE_ALIASED); + if (!geometry) { + qWarning("%s: Could not convert vector path to painter path!", __FUNCTION__); + return; + } + + dc()->PushLayer(D2D1::LayerParameters1(D2D1::InfiniteRect(), + geometry.Get(), + antialiasMode(), + D2D1::IdentityMatrix(), + 1.0, + NULL, + D2D1_LAYER_OPTIONS1_INITIALIZE_FROM_BACKGROUND), + NULL); + pushedClips.push(LayerClip); + } } - void popClip() + void clearClips() { - if (clipFlags & UserClip) { - dc()->PopLayer(); - clipFlags &= ~UserClip; + while (!pushedClips.isEmpty()) { + switch (pushedClips.pop()) { + case AxisAlignedClip: + dc()->PopAxisAlignedClip(); + break; + case LayerClip: + dc()->PopLayer(); + break; + } } } @@ -420,24 +451,23 @@ public: { Q_Q(const QWindowsDirect2DPaintEngine); if (!q->state()->clipEnabled) - popClip(); - else if (!(clipFlags & UserClip)) - pushClip(); + clearClips(); + else if (pushedClips.isEmpty()) + replayClipOperations(); } - void updateClipPath(const QPainterPath &path, Qt::ClipOperation operation) + void clip(const QVectorPath &path, Qt::ClipOperation operation) { switch (operation) { case Qt::NoClip: - popClip(); + clearClips(); break; case Qt::ReplaceClip: - clipPath = path; - pushClip(); + clearClips(); + pushClip(path); break; case Qt::IntersectClip: - clipPath &= path; - pushClip(); + pushClip(path); break; } } @@ -863,7 +893,7 @@ bool QWindowsDirect2DPaintEngine::end() { Q_D(QWindowsDirect2DPaintEngine); // First pop any user-applied clipping - d->popClip(); + d->clearClips(); // Now the system clip from begin() above if (d->clipFlags & SimpleSystemClip) { d->dc()->PopAxisAlignedClip(); @@ -915,7 +945,7 @@ void QWindowsDirect2DPaintEngine::fill(const QVectorPath &path, const QBrush &br if (!d->brush.brush) return; - if (path.hints() & QVectorPath::RectangleShapeMask) { + if (path.isRect()) { const qreal * const points = path.points(); D2D_RECT_F rect = { points[0], // left @@ -936,34 +966,10 @@ void QWindowsDirect2DPaintEngine::fill(const QVectorPath &path, const QBrush &br } } -// For clipping we convert everything to painter paths since it allows -// calculating intersections easily. It might be faster to convert to -// ID2D1Geometry and use its operations, although that needs to measured. -// The implementation would be more complex in any case. - void QWindowsDirect2DPaintEngine::clip(const QVectorPath &path, Qt::ClipOperation op) -{ - clip(path.convertToPainterPath(), op); -} - -void QWindowsDirect2DPaintEngine::clip(const QRect &rect, Qt::ClipOperation op) -{ - QPainterPath p; - p.addRect(rect); - clip(p, op); -} - -void QWindowsDirect2DPaintEngine::clip(const QRegion ®ion, Qt::ClipOperation op) -{ - QPainterPath p; - p.addRegion(region); - clip(p, op); -} - -void QWindowsDirect2DPaintEngine::clip(const QPainterPath &path, Qt::ClipOperation op) { Q_D(QWindowsDirect2DPaintEngine); - d->updateClipPath(path, op); + d->clip(path, op); } void QWindowsDirect2DPaintEngine::clipEnabledChanged() diff --git a/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.h b/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.h index 6c74a07e88..5af4eda163 100644 --- a/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.h +++ b/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the plugins of the Qt Toolkit. @@ -66,11 +66,7 @@ public: Type type() const Q_DECL_OVERRIDE; void fill(const QVectorPath &path, const QBrush &brush) Q_DECL_OVERRIDE; - void clip(const QVectorPath &path, Qt::ClipOperation op) Q_DECL_OVERRIDE; - void clip(const QRect &rect, Qt::ClipOperation op) Q_DECL_OVERRIDE; - void clip(const QRegion ®ion, Qt::ClipOperation op) Q_DECL_OVERRIDE; - void clip(const QPainterPath &path, Qt::ClipOperation op) Q_DECL_OVERRIDE; void clipEnabledChanged() Q_DECL_OVERRIDE; void penChanged() Q_DECL_OVERRIDE;