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 <risto.avila@digia.com>
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@digia.com>
Reviewed-by: Andrew Knight <andrew.knight@digia.com>
This commit is contained in:
Louai Al-Khanji 2014-04-23 14:23:38 +03:00 committed by The Qt Project
parent 42bc626e4e
commit 5611b66c90
4 changed files with 96 additions and 91 deletions

View File

@ -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 <typename T> 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(),

View File

@ -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 <typename T> 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)

View File

@ -52,6 +52,7 @@
#include "qwindowsfontdatabase.h"
#include "qwindowsintegration.h"
#include <QtCore/QStack>
#include <QtGui/private/qpaintengine_p.h>
#include <QtGui/private/qtextengine_p.h>
#include <QtGui/private/qfontengine_p.h>
@ -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<ClipType> pushedClips;
QPointF currentBrushOrigin;
@ -389,30 +395,55 @@ public:
pen.brush->SetOpacity(opacity);
}
void pushClip()
void pushClip(const QVectorPath &path)
{
popClip();
Q_Q(QWindowsDirect2DPaintEngine);
ComPtr<ID2D1PathGeometry1> 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<ID2D1PathGeometry1> 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 &region, 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()

View File

@ -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 &region, 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;