From f961425256f2be8034029912239b331b25dca1c5 Mon Sep 17 00:00:00 2001 From: Louai Al-Khanji Date: Mon, 7 Apr 2014 14:11:27 +0300 Subject: Direct2D QPA: Improve gradient support This change adds support for those gradients which can be expressed using Direct2D. At the moment this means linear and radial gradient with pad spread only. Change-Id: Ib1b1bc38a793dd826a259bbf8a7b31c25906dd59 Reviewed-by: Friedemann Kleint --- .../direct2d/qwindowsdirect2dpaintengine.cpp | 97 +++++++++++++++++++--- 1 file changed, 87 insertions(+), 10 deletions(-) (limited to 'src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp') diff --git a/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp b/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp index d8f34fc3ed..1f2321e885 100644 --- a/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp +++ b/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.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 plugins of the Qt Toolkit. @@ -658,7 +658,91 @@ public: break; case Qt::LinearGradientPattern: + if (newBrush.gradient()->spread() != QGradient::PadSpread) { + *needsEmulation = true; + } else { + ComPtr linear; + const QLinearGradient *qlinear = static_cast(newBrush.gradient()); + + D2D1_LINEAR_GRADIENT_BRUSH_PROPERTIES linearGradientBrushProperties; + ComPtr gradientStopCollection; + + const QGradientStops &qstops = qlinear->stops(); + QVector stops(qstops.count()); + + linearGradientBrushProperties.startPoint = to_d2d_point_2f(qlinear->start()); + linearGradientBrushProperties.endPoint = to_d2d_point_2f(qlinear->finalStop()); + + for (int i = 0; i < stops.size(); i++) { + stops[i].position = qstops[i].first; + stops[i].color = to_d2d_color_f(qstops[i].second); + } + + hr = dc()->CreateGradientStopCollection(stops.constData(), stops.size(), &gradientStopCollection); + if (FAILED(hr)) { + qWarning("%s: Could not create gradient stop collection for linear gradient: %#x", __FUNCTION__, hr); + break; + } + + hr = dc()->CreateLinearGradientBrush(linearGradientBrushProperties, gradientStopCollection.Get(), + &linear); + if (FAILED(hr)) { + qWarning("%s: Could not create Direct2D linear gradient brush: %#x", __FUNCTION__, hr); + break; + } + + hr = linear.As(&result); + if (FAILED(hr)) { + qWarning("%s: Could not convert Direct2D linear gradient brush: %#x", __FUNCTION__, hr); + break; + } + } + break; + case Qt::RadialGradientPattern: + if (newBrush.gradient()->spread() != QGradient::PadSpread) { + *needsEmulation = true; + } else { + ComPtr radial; + const QRadialGradient *qradial = static_cast(newBrush.gradient()); + + D2D1_RADIAL_GRADIENT_BRUSH_PROPERTIES radialGradientBrushProperties; + ComPtr gradientStopCollection; + + const QGradientStops &qstops = qradial->stops(); + QVector stops(qstops.count()); + + radialGradientBrushProperties.center = to_d2d_point_2f(qradial->center()); + radialGradientBrushProperties.gradientOriginOffset = to_d2d_point_2f(qradial->focalPoint() - qradial->center()); + radialGradientBrushProperties.radiusX = qradial->radius(); + radialGradientBrushProperties.radiusY = qradial->radius(); + + for (int i = 0; i < stops.size(); i++) { + stops[i].position = qstops[i].first; + stops[i].color = to_d2d_color_f(qstops[i].second); + } + + hr = dc()->CreateGradientStopCollection(stops.constData(), stops.size(), &gradientStopCollection); + if (FAILED(hr)) { + qWarning("%s: Could not create gradient stop collection for radial gradient: %#x", __FUNCTION__, hr); + break; + } + + hr = dc()->CreateRadialGradientBrush(radialGradientBrushProperties, gradientStopCollection.Get(), + &radial); + if (FAILED(hr)) { + qWarning("%s: Could not create Direct2D radial gradient brush: %#x", __FUNCTION__, hr); + break; + } + + radial.As(&result); + if (FAILED(hr)) { + qWarning("%s: Could not convert Direct2D radial gradient brush: %#x", __FUNCTION__, hr); + break; + } + } + break; + case Qt::ConicalGradientPattern: *needsEmulation = true; break; @@ -706,16 +790,9 @@ QWindowsDirect2DPaintEngine::QWindowsDirect2DPaintEngine(QWindowsDirect2DBitmap : QPaintEngineEx(*(new QWindowsDirect2DPaintEnginePrivate(bitmap))) { QPaintEngine::PaintEngineFeatures unsupported = - // As of 1.1 Direct2D gradient support is deficient for linear and radial gradients - QPaintEngine::LinearGradientFill - | QPaintEngine::RadialGradientFill - - // As of 1.1 Direct2D does not support conical gradients at all - | QPaintEngine::ConicalGradientFill - // As of 1.1 Direct2D does not natively support complex composition modes // However, using Direct2D effects that implement them should be possible - | QPaintEngine::PorterDuff + QPaintEngine::PorterDuff | QPaintEngine::BlendModes | QPaintEngine::RasterOpModes @@ -796,7 +873,7 @@ void QWindowsDirect2DPaintEngine::fill(const QVectorPath &path, const QBrush &br if (d->brush.emulate) { // We mostly (only?) get here when gradients are required. - // We could probably natively support linear and radial gradients that have pad reflect + // We natively support only linear and radial gradients that have pad reflect due to D2D limitations QImage img(d->bitmap->size(), QImage::Format_ARGB32); img.fill(Qt::transparent); -- cgit v1.2.3 From 7d0f4dde0688ec65c34e7a5309257c840409a607 Mon Sep 17 00:00:00 2001 From: Louai Al-Khanji Date: Tue, 15 Apr 2014 09:57:58 +0300 Subject: Direct2D QPA: Optimize rectangle fills. Detect rectangle hints in the QVectorPath and react accordingly. Change-Id: Ic72ce0c46d10e995c0824972854e2d88162eae45 Reviewed-by: Friedemann Kleint --- .../direct2d/qwindowsdirect2dpaintengine.cpp | 24 ++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) (limited to 'src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp') diff --git a/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp b/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp index 1f2321e885..cabcda70ee 100644 --- a/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp +++ b/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp @@ -897,13 +897,25 @@ void QWindowsDirect2DPaintEngine::fill(const QVectorPath &path, const QBrush &br if (!d->brush.brush) return; - ComPtr geometry = vectorPathToID2D1PathGeometry(path, d->antialiasMode() == D2D1_ANTIALIAS_MODE_ALIASED); - if (!geometry) { - qWarning("%s: Could not convert path to d2d geometry", __FUNCTION__); - return; - } + if (path.hints() & QVectorPath::RectangleShapeMask) { + const qreal * const points = path.points(); + D2D_RECT_F rect = { + points[0], // left + points[1], // top + points[2], // right, + points[5] // bottom + }; + + d->dc()->FillRectangle(rect, d->brush.brush.Get()); + } else { + ComPtr geometry = vectorPathToID2D1PathGeometry(path, d->antialiasMode() == D2D1_ANTIALIAS_MODE_ALIASED); + if (!geometry) { + qWarning("%s: Could not convert path to d2d geometry", __FUNCTION__); + return; + } - d->dc()->FillGeometry(geometry.Get(), d->brush.brush.Get()); + d->dc()->FillGeometry(geometry.Get(), d->brush.brush.Get()); + } } // For clipping we convert everything to painter paths since it allows -- cgit v1.2.3 From 0c95332f8fb2da910d43bc8d0e28ebf7e3f725d4 Mon Sep 17 00:00:00 2001 From: Louai Al-Khanji Date: Tue, 15 Apr 2014 16:45:53 +0300 Subject: Direct2D QPA: Refactor code to reduce code duplication Refactor duplicate logic in painterPathToPathGeometry and vectorPathToID2D1PathGeometry into one utility class. At the same time make the naming of the two functions consistent with each other. Change-Id: I03c8fc183863473b7337223e51835cf080914a41 Reviewed-by: Friedemann Kleint --- .../direct2d/qwindowsdirect2dpaintengine.cpp | 222 +++++++++++---------- 1 file changed, 120 insertions(+), 102 deletions(-) (limited to 'src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp') diff --git a/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp b/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp index cabcda70ee..d0076fa4de 100644 --- a/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp +++ b/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp @@ -92,51 +92,124 @@ static inline ID2D1Factory1 *factory() return QWindowsDirect2DContext::instance()->d2dFactory(); } -// XXX reduce code duplication between painterPathToPathGeometry and -// vectorPathToID2D1PathGeometry, the two are quite similar - -static ComPtr painterPathToPathGeometry(const QPainterPath &path) +class Direct2DPathGeometryWriter { - ComPtr geometry; - ComPtr sink; +public: + Direct2DPathGeometryWriter() + : m_inFigure(false) + , m_roundCoordinates(false) + { - HRESULT hr = factory()->CreatePathGeometry(&geometry); - if (FAILED(hr)) { - qWarning("%s: Could not create path geometry: %#x", __FUNCTION__, hr); - return NULL; } - hr = geometry->Open(&sink); - if (FAILED(hr)) { - qWarning("%s: Could not create geometry sink: %#x", __FUNCTION__, hr); - return NULL; + bool begin() + { + HRESULT hr = factory()->CreatePathGeometry(&m_geometry); + if (FAILED(hr)) { + qWarning("%s: Could not create path geometry: %#x", __FUNCTION__, hr); + return false; + } + + hr = m_geometry->Open(&m_sink); + if (FAILED(hr)) { + qWarning("%s: Could not create geometry sink: %#x", __FUNCTION__, hr); + return false; + } + + return true; } - switch (path.fillRule()) { - case Qt::WindingFill: - sink->SetFillMode(D2D1_FILL_MODE_WINDING); - break; - case Qt::OddEvenFill: - sink->SetFillMode(D2D1_FILL_MODE_ALTERNATE); - break; + void setWindingFillEnabled(bool enable) + { + if (enable) + m_sink->SetFillMode(D2D1_FILL_MODE_WINDING); + else + m_sink->SetFillMode(D2D1_FILL_MODE_ALTERNATE); + } + + void setAliasingEnabled(bool enable) + { + m_roundCoordinates = enable; + } + + bool isInFigure() const + { + return m_inFigure; + } + + void moveTo(const QPointF &point) + { + if (m_inFigure) + m_sink->EndFigure(D2D1_FIGURE_END_OPEN); + + m_sink->BeginFigure(adjusted(point), D2D1_FIGURE_BEGIN_FILLED); + m_inFigure = true; + } + + void lineTo(const QPointF &point) + { + m_sink->AddLine(adjusted(point)); + } + + void curveTo(const QPointF &p1, const QPointF &p2, const QPointF &p3) + { + D2D1_BEZIER_SEGMENT segment = { + adjusted(p1), + adjusted(p2), + adjusted(p3) + }; + + m_sink->AddBezier(segment); + } + + void close() + { + if (m_inFigure) + m_sink->EndFigure(D2D1_FIGURE_END_OPEN); + + m_sink->Close(); + } + + ComPtr geometry() const + { + return m_geometry; + } + +private: + D2D1_POINT_2F adjusted(const QPointF &point) + { + if (m_roundCoordinates) + return to_d2d_point_2f(point.toPoint()); + else + return to_d2d_point_2f(point); } - bool inFigure = false; + ComPtr m_geometry; + ComPtr m_sink; + + bool m_inFigure; + bool m_roundCoordinates; +}; + +static ComPtr painterPathToID2D1PathGeometry(const QPainterPath &path, bool alias) +{ + Direct2DPathGeometryWriter writer; + if (!writer.begin()) + return NULL; + + writer.setWindingFillEnabled(path.fillRule() == Qt::WindingFill); + writer.setAliasingEnabled(alias); for (int i = 0; i < path.elementCount(); i++) { const QPainterPath::Element element = path.elementAt(i); switch (element.type) { case QPainterPath::MoveToElement: - if (inFigure) - sink->EndFigure(D2D1_FIGURE_END_OPEN); - - sink->BeginFigure(to_d2d_point_2f(element), D2D1_FIGURE_BEGIN_FILLED); - inFigure = true; + writer.moveTo(element); break; case QPainterPath::LineToElement: - sink->AddLine(to_d2d_point_2f(element)); + writer.lineTo(element); break; case QPainterPath::CurveToElement: @@ -149,13 +222,7 @@ static ComPtr painterPathToPathGeometry(const QPainterPath & Q_ASSERT(data1.type == QPainterPath::CurveToDataElement); Q_ASSERT(data2.type == QPainterPath::CurveToDataElement); - D2D1_BEZIER_SEGMENT segment; - - segment.point1 = to_d2d_point_2f(element); - segment.point2 = to_d2d_point_2f(data1); - segment.point3 = to_d2d_point_2f(data2); - - sink->AddBezier(segment); + writer.curveTo(element, data1, data2); } break; @@ -165,55 +232,22 @@ static ComPtr painterPathToPathGeometry(const QPainterPath & } } - if (inFigure) - sink->EndFigure(D2D1_FIGURE_END_OPEN); - - sink->Close(); - - return geometry; + writer.close(); + return writer.geometry(); } static ComPtr vectorPathToID2D1PathGeometry(const QVectorPath &path, bool alias) { - ComPtr pathGeometry; - HRESULT hr = factory()->CreatePathGeometry(pathGeometry.GetAddressOf()); - if (FAILED(hr)) { - qWarning("%s: Could not create path geometry: %#x", __FUNCTION__, hr); + Direct2DPathGeometryWriter writer; + if (!writer.begin()) return NULL; - } - - if (path.isEmpty()) - return pathGeometry; - ComPtr sink; - hr = pathGeometry->Open(sink.GetAddressOf()); - if (FAILED(hr)) { - qWarning("%s: Could not create geometry sink: %#x", __FUNCTION__, hr); - return NULL; - } - - sink->SetFillMode(path.hasWindingFill() ? D2D1_FILL_MODE_WINDING - : D2D1_FILL_MODE_ALTERNATE); - - bool inFigure = false; + writer.setWindingFillEnabled(path.hasWindingFill()); + writer.setAliasingEnabled(alias); const QPainterPath::ElementType *types = path.elements(); const int count = path.elementCount(); - const qreal *points = 0; - - QScopedArrayPointer rounded_points; - - if (alias) { - // Aliased painting, round to whole numbers - rounded_points.reset(new qreal[count * 2]); - points = rounded_points.data(); - - for (int i = 0; i < (count * 2); i++) - rounded_points[i] = qRound(path.points()[i]); - } else { - // Antialiased painting, keep original numbers - points = path.points(); - } + const qreal *points = path.points(); Q_ASSERT(points); @@ -226,15 +260,11 @@ static ComPtr vectorPathToID2D1PathGeometry(const QVectorPat switch (types[i]) { case QPainterPath::MoveToElement: - if (inFigure) - sink->EndFigure(D2D1_FIGURE_END_OPEN); - - sink->BeginFigure(D2D1::Point2F(x, y), D2D1_FIGURE_BEGIN_FILLED); - inFigure = true; + writer.moveTo(QPointF(x, y)); break; case QPainterPath::LineToElement: - sink->AddLine(D2D1::Point2F(x, y)); + writer.lineTo(QPointF(x, y)); break; case QPainterPath::CurveToElement: @@ -251,13 +281,7 @@ static ComPtr vectorPathToID2D1PathGeometry(const QVectorPat const qreal x3 = points[i * 2]; const qreal y3 = points[i * 2 + 1]; - D2D1_BEZIER_SEGMENT segment = { - D2D1::Point2F(x, y), - D2D1::Point2F(x2, y2), - D2D1::Point2F(x3, y3) - }; - - sink->AddBezier(segment); + writer.curveTo(QPointF(x, y), QPointF(x2, y2), QPointF(x3, y3)); } break; @@ -267,23 +291,17 @@ static ComPtr vectorPathToID2D1PathGeometry(const QVectorPat } } } else { - sink->BeginFigure(D2D1::Point2F(points[0], points[1]), D2D1_FIGURE_BEGIN_FILLED); - inFigure = true; - + writer.moveTo(QPointF(points[0], points[1])); for (int i = 1; i < count; i++) - sink->AddLine(D2D1::Point2F(points[i * 2], points[i * 2 + 1])); + writer.lineTo(QPointF(points[i * 2], points[i * 2 + 1])); } - if (inFigure) { + if (writer.isInFigure()) if (path.hasImplicitClose()) - sink->AddLine(D2D1::Point2F(points[0], points[1])); - - sink->EndFigure(D2D1_FIGURE_END_OPEN); - } - - sink->Close(); + writer.lineTo(QPointF(points[0], points[1])); - return pathGeometry; + writer.close(); + return writer.geometry(); } class QWindowsDirect2DPaintEnginePrivate : public QPaintEngineExPrivate @@ -375,7 +393,7 @@ public: { popClip(); - ComPtr geometry = painterPathToPathGeometry(clipPath); + ComPtr geometry = painterPathToID2D1PathGeometry(clipPath, antialiasMode() == D2D1_ANTIALIAS_MODE_ALIASED); if (!geometry) return; @@ -816,7 +834,7 @@ bool QWindowsDirect2DPaintEngine::begin(QPaintDevice * pdev) QPainterPath p; p.addRegion(systemClip()); - ComPtr geometry = painterPathToPathGeometry(p); + ComPtr geometry = painterPathToID2D1PathGeometry(p, d->antialiasMode() == D2D1_ANTIALIAS_MODE_ALIASED); if (!geometry) return false; -- cgit v1.2.3 From d0cf69eaff95e5c15573b5673deda8fd85a61dcd Mon Sep 17 00:00:00 2001 From: Louai Al-Khanji Date: Tue, 15 Apr 2014 16:50:10 +0300 Subject: Direct2D QPA: Fix text drawing with brush but no pen We were incorrectly bailing out early in the text drawing code when there was no pen. This is incorrect as drawing with only a brush should be possible. Change-Id: I94eaadd3cf6c4d82033b5d74d7ca47a05601083f Reviewed-by: Friedemann Kleint --- src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp') diff --git a/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp b/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp index d0076fa4de..86f78a35d5 100644 --- a/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp +++ b/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp @@ -1143,7 +1143,7 @@ void QWindowsDirect2DPaintEngine::drawStaticTextItem(QStaticTextItem *staticText Q_D(QWindowsDirect2DPaintEngine); D2D_TAG(D2DDebugDrawStaticTextItemTag); - if (qpen_style(d->pen.qpen) == Qt::NoPen) + if (qpen_style(d->pen.qpen) == Qt::NoPen && qbrush_style(d->brush.qbrush) == Qt::NoBrush) return; if (staticTextItem->numGlyphs == 0) @@ -1193,7 +1193,7 @@ void QWindowsDirect2DPaintEngine::drawTextItem(const QPointF &p, const QTextItem Q_D(QWindowsDirect2DPaintEngine); D2D_TAG(D2DDebugDrawTextItemTag); - if (qpen_style(d->pen.qpen) == Qt::NoPen) + if (qpen_style(d->pen.qpen) == Qt::NoPen && qbrush_style(d->brush.qbrush) == Qt::NoBrush) return; const QTextItemInt &ti = static_cast(textItem); -- cgit v1.2.3 From 5611b66c901867a2ea2855c90454c2267ef197dd Mon Sep 17 00:00:00 2001 From: Louai Al-Khanji Date: Wed, 23 Apr 2014 14:23:38 +0300 Subject: 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 --- .../direct2d/qwindowsdirect2dpaintengine.cpp | 120 +++++++++++---------- 1 file changed, 63 insertions(+), 57 deletions(-) (limited to 'src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp') 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); + + 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 + }; - ComPtr geometry = painterPathToID2D1PathGeometry(clipPath, antialiasMode() == D2D1_ANTIALIAS_MODE_ALIASED); - if (!geometry) - return; + 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); - clipFlags |= UserClip; + 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() -- cgit v1.2.3 From 1467b63b0644573cfafa9ec358365660f3883f8d Mon Sep 17 00:00:00 2001 From: Louai Al-Khanji Date: Wed, 23 Apr 2014 15:30:58 +0300 Subject: Direct2D QPA: Fix paint engine state handling Fix the paint handling to support QPaintEngineEx style state updates. This fixes most of the outstanding issues, as QPainter save and restore were essentially broken before Change-Id: I477d8acfd71bba32dfac4c491bc5bbaad1804ec5 Reviewed-by: Friedemann Kleint Reviewed-by: Risto Avila Reviewed-by: Andrew Knight --- .../direct2d/qwindowsdirect2dpaintengine.cpp | 63 +++++++++++++++++----- 1 file changed, 50 insertions(+), 13 deletions(-) (limited to 'src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp') diff --git a/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp b/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp index f162f5e93c..7c4ba6cb3f 100644 --- a/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp +++ b/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp @@ -495,7 +495,7 @@ public: { Q_Q(const QWindowsDirect2DPaintEngine); - if (qbrush_fast_equals(brush.qbrush, newBrush)) + if (qbrush_fast_equals(brush.qbrush, newBrush) && (brush.brush || brush.emulate)) return; brush.brush = to_d2d_brush(newBrush, &brush.emulate); @@ -540,12 +540,10 @@ public: currentBrushOrigin = origin; } - void updatePen() + void updatePen(const QPen &newPen) { Q_Q(const QWindowsDirect2DPaintEngine); - const QPen &newPen = q->state()->pen; - - if (qpen_fast_equals(newPen, pen.qpen)) + if (qpen_fast_equals(newPen, pen.qpen) && (pen.brush || pen.emulate)) return; pen.reset(); @@ -909,6 +907,23 @@ QPaintEngine::Type QWindowsDirect2DPaintEngine::type() const return QPaintEngine::Direct2D; } +void QWindowsDirect2DPaintEngine::setState(QPainterState *s) +{ + Q_D(QWindowsDirect2DPaintEngine); + + QPaintEngineEx::setState(s); + d->clearClips(); + + clipEnabledChanged(); + penChanged(); + brushChanged(); + brushOriginChanged(); + opacityChanged(); + compositionModeChanged(); + renderHintsChanged(); + transformChanged(); +} + void QWindowsDirect2DPaintEngine::fill(const QVectorPath &path, const QBrush &brush) { Q_D(QWindowsDirect2DPaintEngine); @@ -917,7 +932,7 @@ void QWindowsDirect2DPaintEngine::fill(const QVectorPath &path, const QBrush &br if (path.isEmpty()) return; - d->updateBrush(brush); + ensureBrush(brush); if (d->brush.emulate) { // We mostly (only?) get here when gradients are required. @@ -981,7 +996,7 @@ void QWindowsDirect2DPaintEngine::clipEnabledChanged() void QWindowsDirect2DPaintEngine::penChanged() { Q_D(QWindowsDirect2DPaintEngine); - d->updatePen(); + d->updatePen(state()->pen); } void QWindowsDirect2DPaintEngine::brushChanged() @@ -1051,6 +1066,8 @@ void QWindowsDirect2DPaintEngine::drawPixmap(const QRectF &r, QWindowsDirect2DPlatformPixmap *pp = static_cast(pm.handle()); QWindowsDirect2DBitmap *bitmap = pp->bitmap(); + ensurePen(); + if (bitmap->bitmap() != d->bitmap->bitmap()) { // Good, src bitmap != dst bitmap if (sr.isValid()) @@ -1149,12 +1166,11 @@ void QWindowsDirect2DPaintEngine::drawStaticTextItem(QStaticTextItem *staticText Q_D(QWindowsDirect2DPaintEngine); D2D_TAG(D2DDebugDrawStaticTextItemTag); - if (qpen_style(d->pen.qpen) == Qt::NoPen && qbrush_style(d->brush.qbrush) == Qt::NoBrush) - return; - if (staticTextItem->numGlyphs == 0) return; + ensurePen(); + // If we can't support the current configuration with Direct2D, fall back to slow path // Most common cases are perspective transform and gradient brush as pen if ((state()->transform().isAffine() == false) || d->pen.emulate) { @@ -1199,13 +1215,12 @@ void QWindowsDirect2DPaintEngine::drawTextItem(const QPointF &p, const QTextItem Q_D(QWindowsDirect2DPaintEngine); D2D_TAG(D2DDebugDrawTextItemTag); - if (qpen_style(d->pen.qpen) == Qt::NoPen && qbrush_style(d->brush.qbrush) == Qt::NoBrush) - return; - const QTextItemInt &ti = static_cast(textItem); if (ti.glyphs.numGlyphs == 0) return; + ensurePen(); + // If we can't support the current configuration with Direct2D, fall back to slow path // Most common cases are perspective transform and gradient brush as pen if ((state()->transform().isAffine() == false) || d->pen.emulate) { @@ -1307,4 +1322,26 @@ void QWindowsDirect2DPaintEngine::drawGlyphRun(const D2D1_POINT_2F &pos, DWRITE_MEASURING_MODE_GDI_CLASSIC); } +void QWindowsDirect2DPaintEngine::ensureBrush() +{ + ensureBrush(state()->brush); +} + +void QWindowsDirect2DPaintEngine::ensureBrush(const QBrush &brush) +{ + Q_D(QWindowsDirect2DPaintEngine); + d->updateBrush(brush); +} + +void QWindowsDirect2DPaintEngine::ensurePen() +{ + ensurePen(state()->pen); +} + +void QWindowsDirect2DPaintEngine::ensurePen(const QPen &pen) +{ + Q_D(QWindowsDirect2DPaintEngine); + d->updatePen(pen); +} + QT_END_NAMESPACE -- cgit v1.2.3 From cc15a20d03fb81762680c19eb3c34a4e628c27a7 Mon Sep 17 00:00:00 2001 From: Louai Al-Khanji Date: Wed, 23 Apr 2014 16:07:05 +0300 Subject: Direct2D QPA: Improve software fallback mechanism Improve the way we fall back to the raster engine by forwarding painting state. Amongst other things this makes perspective transforms appear correct. Change-Id: I729de56ef3112bbc01516fc11c295f33a2aada0d Reviewed-by: Risto Avila Reviewed-by: Friedemann Kleint Reviewed-by: Andrew Knight --- .../direct2d/qwindowsdirect2dpaintengine.cpp | 121 +++++++++++++-------- 1 file changed, 78 insertions(+), 43 deletions(-) (limited to 'src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp') diff --git a/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp b/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp index 7c4ba6cb3f..1e7c175252 100644 --- a/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp +++ b/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp @@ -378,17 +378,13 @@ public: : D2D1_ANTIALIAS_MODE_ALIASED; } - void updateTransform() + void updateTransform(const QTransform &transform) { - Q_Q(const QWindowsDirect2DPaintEngine); - // Note the loss of info going from 3x3 to 3x2 matrix here - dc()->SetTransform(to_d2d_matrix_3x2_f(q->state()->transform())); + dc()->SetTransform(to_d2d_matrix_3x2_f(transform)); } - void updateOpacity() + void updateOpacity(qreal opacity) { - Q_Q(const QWindowsDirect2DPaintEngine); - qreal opacity = q->state()->opacity; if (brush.brush) brush.brush->SetOpacity(opacity); if (pen.brush) @@ -447,10 +443,9 @@ public: } } - void updateClipEnabled() + void updateClipEnabled(bool enabled) { - Q_Q(const QWindowsDirect2DPaintEngine); - if (!q->state()->clipEnabled) + if (!enabled) clearClips(); else if (pushedClips.isEmpty()) replayClipOperations(); @@ -472,11 +467,8 @@ public: } } - void updateCompositionMode() + void updateCompositionMode(QPainter::CompositionMode mode) { - Q_Q(const QWindowsDirect2DPaintEngine); - QPainter::CompositionMode mode = q->state()->compositionMode(); - switch (mode) { case QPainter::CompositionMode_Source: dc()->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_COPY); @@ -507,12 +499,10 @@ public: } } - void updateBrushOrigin() + void updateBrushOrigin(const QPointF &brushOrigin) { - Q_Q(const QWindowsDirect2DPaintEngine); - negateCurrentBrushOrigin(); - applyBrushOrigin(q->state()->brushOrigin); + applyBrushOrigin(brushOrigin); } void negateCurrentBrushOrigin() @@ -934,26 +924,8 @@ void QWindowsDirect2DPaintEngine::fill(const QVectorPath &path, const QBrush &br ensureBrush(brush); - if (d->brush.emulate) { - // We mostly (only?) get here when gradients are required. - // We natively support only linear and radial gradients that have pad reflect due to D2D limitations - - QImage img(d->bitmap->size(), QImage::Format_ARGB32); - img.fill(Qt::transparent); - - QPainter p; - QPaintEngine *engine = img.paintEngine(); - if (engine->isExtended() && p.begin(&img)) { - QPaintEngineEx *extended = static_cast(engine); - extended->fill(path, brush); - if (!p.end()) - qWarning("%s: Paint Engine end returned false", __FUNCTION__); - - drawImage(img.rect(), img, img.rect()); - } else { - qWarning("%s: Could not fall back to QImage", __FUNCTION__); - } - + if (!state()->matrix.isAffine() || d->brush.emulate) { + rasterFill(path, brush); return; } @@ -990,7 +962,7 @@ void QWindowsDirect2DPaintEngine::clip(const QVectorPath &path, Qt::ClipOperatio void QWindowsDirect2DPaintEngine::clipEnabledChanged() { Q_D(QWindowsDirect2DPaintEngine); - d->updateClipEnabled(); + d->updateClipEnabled(state()->clipEnabled); } void QWindowsDirect2DPaintEngine::penChanged() @@ -1008,19 +980,19 @@ void QWindowsDirect2DPaintEngine::brushChanged() void QWindowsDirect2DPaintEngine::brushOriginChanged() { Q_D(QWindowsDirect2DPaintEngine); - d->updateBrushOrigin(); + d->updateBrushOrigin(state()->brushOrigin); } void QWindowsDirect2DPaintEngine::opacityChanged() { Q_D(QWindowsDirect2DPaintEngine); - d->updateOpacity(); + d->updateOpacity(state()->opacity); } void QWindowsDirect2DPaintEngine::compositionModeChanged() { Q_D(QWindowsDirect2DPaintEngine); - d->updateCompositionMode(); + d->updateCompositionMode(state()->compositionMode()); } void QWindowsDirect2DPaintEngine::renderHintsChanged() @@ -1032,7 +1004,7 @@ void QWindowsDirect2DPaintEngine::renderHintsChanged() void QWindowsDirect2DPaintEngine::transformChanged() { Q_D(QWindowsDirect2DPaintEngine); - d->updateTransform(); + d->updateTransform(state()->transform()); } void QWindowsDirect2DPaintEngine::drawImage(const QRectF &rectangle, const QImage &image, @@ -1344,4 +1316,67 @@ void QWindowsDirect2DPaintEngine::ensurePen(const QPen &pen) d->updatePen(pen); } +void QWindowsDirect2DPaintEngine::rasterFill(const QVectorPath &path, const QBrush &brush) +{ + Q_D(QWindowsDirect2DPaintEngine); + + QImage img(d->bitmap->size(), QImage::Format_ARGB32); + img.fill(Qt::transparent); + + QPainter p; + QPaintEngine *engine = img.paintEngine(); + + if (engine->isExtended() && p.begin(&img)) { + p.setRenderHints(state()->renderHints); + p.setCompositionMode(state()->compositionMode()); + p.setOpacity(state()->opacity); + p.setBrushOrigin(state()->brushOrigin); + p.setBrush(state()->brush); + p.setPen(state()->pen); + + QPaintEngineEx *extended = static_cast(engine); + foreach (const QPainterClipInfo &info, state()->clipInfo) { + extended->state()->matrix = info.matrix; + extended->transformChanged(); + + switch (info.clipType) { + case QPainterClipInfo::RegionClip: + extended->clip(info.region, info.operation); + break; + case QPainterClipInfo::PathClip: + extended->clip(info.path, info.operation); + break; + case QPainterClipInfo::RectClip: + extended->clip(info.rect, info.operation); + break; + case QPainterClipInfo::RectFClip: + qreal right = info.rectf.x() + info.rectf.width(); + qreal bottom = info.rectf.y() + info.rectf.height(); + qreal pts[] = { info.rectf.x(), info.rectf.y(), + right, info.rectf.y(), + right, bottom, + info.rectf.x(), bottom }; + QVectorPath vp(pts, 4, 0, QVectorPath::RectangleHint); + extended->clip(vp, info.operation); + break; + } + } + + extended->state()->matrix = state()->matrix; + extended->transformChanged(); + + extended->fill(path, brush); + if (!p.end()) + qWarning("%s: Paint Engine end returned false", __FUNCTION__); + + d->updateClipEnabled(false); + d->updateTransform(QTransform()); + drawImage(img.rect(), img, img.rect()); + transformChanged(); + clipEnabledChanged(); + } else { + qWarning("%s: Could not fall back to QImage", __FUNCTION__); + } +} + QT_END_NAMESPACE -- cgit v1.2.3 From 195c070adf22f829697976ec8dbbfb3b5ec93305 Mon Sep 17 00:00:00 2001 From: Louai Al-Khanji Date: Mon, 28 Apr 2014 12:59:46 +0300 Subject: Direct2D QPA: Correctly set active state in paint engine Change-Id: I4eea073ad11ba8684212258b344c1f237598a9c5 Reviewed-by: Risto Avila Reviewed-by: Friedemann Kleint --- src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp') diff --git a/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp b/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp index 1e7c175252..8d775e5a9d 100644 --- a/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp +++ b/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp @@ -874,6 +874,7 @@ bool QWindowsDirect2DPaintEngine::begin(QPaintDevice * pdev) D2D_TAG(D2DDebugDrawInitialStateTag); + setActive(true); return true; } -- cgit v1.2.3 From 0edb21df154cd2469ba91ea95c8c6464d1140c8d Mon Sep 17 00:00:00 2001 From: Louai Al-Khanji Date: Tue, 29 Apr 2014 16:14:19 +0300 Subject: Direct2D QPA: Pixel-align aliased drawing Aliased drawing has so far not been perfectly pixel aligned, resulting in less than stellar output in some instances. Although a little hacky, adding 0.5 to all coordinates when in aliased mode fixes things up nicely. There doesn't appear to be a better way to get d2d to cooperate as we would like it to. Change-Id: I10ee494d2f576bfd0eca6d4429095a3726c0bf14 Reviewed-by: Risto Avila Reviewed-by: Andrew Knight --- src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp') diff --git a/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp b/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp index 8d775e5a9d..0c3618132d 100644 --- a/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp +++ b/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp @@ -89,6 +89,11 @@ enum ClipType { LayerClip }; +// Since d2d is a float-based system we need to be able to snap our drawing to whole pixels. +// Applying the magical aliasing offset to coordinates will do so, just make sure that +// aliased painting is turned on on the d2d device context. +static const qreal MAGICAL_ALIASING_OFFSET = 0.5; + #define D2D_TAG(tag) d->dc()->SetTags(tag, tag) Q_GUI_EXPORT QImage qt_imageForBrush(int brushStyle, bool invert); @@ -184,8 +189,11 @@ public: private: D2D1_POINT_2F adjusted(const QPointF &point) { + static const QPointF adjustment(MAGICAL_ALIASING_OFFSET, + MAGICAL_ALIASING_OFFSET); + if (m_roundCoordinates) - return to_d2d_point_2f(point.toPoint()); + return to_d2d_point_2f(point + adjustment); else return to_d2d_point_2f(point); } -- cgit v1.2.3 From 948fa572de1c6398df03246b7a6c124af312e967 Mon Sep 17 00:00:00 2001 From: Louai Al-Khanji Date: Tue, 29 Apr 2014 16:15:55 +0300 Subject: Direct2D QPA: Add handlers for individual primitives Directly handling primitives is faster than using the catch-all fill function which converts arbitrary paths into a direct2d geometry. So do so. Change-Id: I71ce73dbe75aa9b61e741c358d8787d0ea48ee46 Reviewed-by: Risto Avila Reviewed-by: Friedemann Kleint Reviewed-by: Andrew Knight --- .../direct2d/qwindowsdirect2dpaintengine.cpp | 272 +++++++++++++++++++-- 1 file changed, 251 insertions(+), 21 deletions(-) (limited to 'src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp') diff --git a/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp b/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp index 0c3618132d..58c30b6eeb 100644 --- a/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp +++ b/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp @@ -72,8 +72,15 @@ QT_BEGIN_NAMESPACE // http://msdn.microsoft.com/en-us/library/windows/desktop/dd370979(v=vs.85).aspx enum { D2DDebugDrawInitialStateTag = -1, - D2DDebugDrawImageTag = 1, - D2DDebugFillTag, + D2DDebugFillTag = 1, + D2DDebugFillRectTag, + D2DDebugDrawRectsTag, + D2DDebugDrawRectFsTag, + D2DDebugDrawLinesTag, + D2DDebugDrawLineFsTag, + D2DDebugDrawEllipseTag, + D2DDebugDrawEllipseFTag, + D2DDebugDrawImageTag, D2DDebugDrawPixmapTag, D2DDebugDrawStaticTextItemTag, D2DDebugDrawTextItemTag @@ -933,7 +940,7 @@ void QWindowsDirect2DPaintEngine::fill(const QVectorPath &path, const QBrush &br ensureBrush(brush); - if (!state()->matrix.isAffine() || d->brush.emulate) { + if (emulationRequired(BrushEmulation)) { rasterFill(path, brush); return; } @@ -941,25 +948,13 @@ void QWindowsDirect2DPaintEngine::fill(const QVectorPath &path, const QBrush &br if (!d->brush.brush) return; - if (path.isRect()) { - const qreal * const points = path.points(); - D2D_RECT_F rect = { - points[0], // left - points[1], // top - points[2], // right, - points[5] // bottom - }; - - d->dc()->FillRectangle(rect, d->brush.brush.Get()); - } else { - ComPtr geometry = vectorPathToID2D1PathGeometry(path, d->antialiasMode() == D2D1_ANTIALIAS_MODE_ALIASED); - if (!geometry) { - qWarning("%s: Could not convert path to d2d geometry", __FUNCTION__); - return; - } - - d->dc()->FillGeometry(geometry.Get(), d->brush.brush.Get()); + ComPtr geometry = vectorPathToID2D1PathGeometry(path, d->antialiasMode() == D2D1_ANTIALIAS_MODE_ALIASED); + if (!geometry) { + qWarning("%s: Could not convert path to d2d geometry", __FUNCTION__); + return; } + + d->dc()->FillGeometry(geometry.Get(), d->brush.brush.Get()); } void QWindowsDirect2DPaintEngine::clip(const QVectorPath &path, Qt::ClipOperation op) @@ -1016,6 +1011,198 @@ void QWindowsDirect2DPaintEngine::transformChanged() d->updateTransform(state()->transform()); } +void QWindowsDirect2DPaintEngine::fillRect(const QRectF &rect, const QBrush &brush) +{ + Q_D(QWindowsDirect2DPaintEngine); + D2D_TAG(D2DDebugFillRectTag); + + ensureBrush(brush); + + if (emulationRequired(BrushEmulation)) { + QPaintEngineEx::fillRect(rect, brush); + } else { + QRectF r = rect.normalized(); + adjustForAliasing(&r); + + if (d->brush.brush) + d->dc()->FillRectangle(to_d2d_rect_f(rect), d->brush.brush.Get()); + } +} + +void QWindowsDirect2DPaintEngine::drawRects(const QRect *rects, int rectCount) +{ + Q_D(QWindowsDirect2DPaintEngine); + D2D_TAG(D2DDebugDrawRectsTag); + + ensureBrush(); + ensurePen(); + + if (emulationRequired(BrushEmulation) || emulationRequired(PenEmulation)) { + QPaintEngineEx::drawRects(rects, rectCount); + } else { + QRectF rect; + for (int i = 0; i < rectCount; i++) { + rect = rects[i].normalized(); + adjustForAliasing(&rect); + + D2D1_RECT_F d2d_rect = to_d2d_rect_f(rect); + + if (d->brush.brush) + d->dc()->FillRectangle(d2d_rect, d->brush.brush.Get()); + + if (d->pen.brush) + d->dc()->DrawRectangle(d2d_rect, d->pen.brush.Get(), d->pen.qpen.widthF(), d->pen.strokeStyle.Get()); + } + } +} + +void QWindowsDirect2DPaintEngine::drawRects(const QRectF *rects, int rectCount) +{ + Q_D(QWindowsDirect2DPaintEngine); + D2D_TAG(D2DDebugDrawRectFsTag); + + ensureBrush(); + ensurePen(); + + if (emulationRequired(BrushEmulation) || emulationRequired(PenEmulation)) { + QPaintEngineEx::drawRects(rects, rectCount); + } else { + QRectF rect; + for (int i = 0; i < rectCount; i++) { + rect = rects[i].normalized(); + adjustForAliasing(&rect); + + D2D1_RECT_F d2d_rect = to_d2d_rect_f(rect); + + if (d->brush.brush) + d->dc()->FillRectangle(d2d_rect, d->brush.brush.Get()); + + if (d->pen.brush) + d->dc()->DrawRectangle(d2d_rect, d->pen.brush.Get(), d->pen.qpen.widthF(), d->pen.strokeStyle.Get()); + } + } +} + +void QWindowsDirect2DPaintEngine::drawLines(const QLine *lines, int lineCount) +{ + Q_D(QWindowsDirect2DPaintEngine); + D2D_TAG(D2DDebugDrawLinesTag); + + ensurePen(); + + if (emulationRequired(PenEmulation)) { + QPaintEngineEx::drawLines(lines, lineCount); + } else if (d->pen.brush) { + for (int i = 0; i < lineCount; i++) { + QPointF p1 = lines[i].p1(); + QPointF p2 = lines[i].p2(); + + // Match raster engine output + if (p1 == p2 && d->pen.qpen.widthF() <= 1.0) { + fillRect(QRectF(p1, QSizeF(d->pen.qpen.widthF(), d->pen.qpen.widthF())), + d->pen.qpen.brush()); + continue; + } + + adjustForAliasing(&p1); + adjustForAliasing(&p2); + + D2D1_POINT_2F d2d_p1 = to_d2d_point_2f(p1); + D2D1_POINT_2F d2d_p2 = to_d2d_point_2f(p2); + + d->dc()->DrawLine(d2d_p1, d2d_p2, d->pen.brush.Get(), d->pen.qpen.widthF(), d->pen.strokeStyle.Get()); + } + } +} + +void QWindowsDirect2DPaintEngine::drawLines(const QLineF *lines, int lineCount) +{ + Q_D(QWindowsDirect2DPaintEngine); + D2D_TAG(D2DDebugDrawLineFsTag); + + ensurePen(); + + if (emulationRequired(PenEmulation)) { + QPaintEngineEx::drawLines(lines, lineCount); + } else if (d->pen.brush) { + for (int i = 0; i < lineCount; i++) { + QPointF p1 = lines[i].p1(); + QPointF p2 = lines[i].p2(); + + // Match raster engine output + if (p1 == p2 && d->pen.qpen.widthF() <= 1.0) { + fillRect(QRectF(p1, QSizeF(d->pen.qpen.widthF(), d->pen.qpen.widthF())), + d->pen.qpen.brush()); + continue; + } + + adjustForAliasing(&p1); + adjustForAliasing(&p2); + + D2D1_POINT_2F d2d_p1 = to_d2d_point_2f(p1); + D2D1_POINT_2F d2d_p2 = to_d2d_point_2f(p2); + + d->dc()->DrawLine(d2d_p1, d2d_p2, d->pen.brush.Get(), d->pen.qpen.widthF(), d->pen.strokeStyle.Get()); + } + } +} + +void QWindowsDirect2DPaintEngine::drawEllipse(const QRectF &r) +{ + Q_D(QWindowsDirect2DPaintEngine); + D2D_TAG(D2DDebugDrawEllipseFTag); + + ensureBrush(); + ensurePen(); + + if (emulationRequired(BrushEmulation) || emulationRequired(PenEmulation)) { + QPaintEngineEx::drawEllipse(r); + } else { + QPointF p = r.center(); + adjustForAliasing(&p); + + D2D1_ELLIPSE ellipse = { + to_d2d_point_2f(p), + r.width() / 2.0, + r.height() / 2.0 + }; + + if (d->brush.brush) + d->dc()->FillEllipse(ellipse, d->brush.brush.Get()); + + if (d->pen.brush) + d->dc()->DrawEllipse(ellipse, d->pen.brush.Get(), d->pen.qpen.widthF(), d->pen.strokeStyle.Get()); + } +} + +void QWindowsDirect2DPaintEngine::drawEllipse(const QRect &r) +{ + Q_D(QWindowsDirect2DPaintEngine); + D2D_TAG(D2DDebugDrawEllipseTag); + + ensureBrush(); + ensurePen(); + + if (emulationRequired(BrushEmulation) || emulationRequired(PenEmulation)) { + QPaintEngineEx::drawEllipse(r); + } else { + QPointF p = r.center(); + adjustForAliasing(&p); + + D2D1_ELLIPSE ellipse = { + to_d2d_point_2f(p), + r.width() / 2.0, + r.height() / 2.0 + }; + + if (d->brush.brush) + d->dc()->FillEllipse(ellipse, d->brush.brush.Get()); + + if (d->pen.brush) + d->dc()->DrawEllipse(ellipse, d->pen.brush.Get(), d->pen.qpen.widthF(), d->pen.strokeStyle.Get()); + } +} + void QWindowsDirect2DPaintEngine::drawImage(const QRectF &rectangle, const QImage &image, const QRectF &sr, Qt::ImageConversionFlags flags) { @@ -1388,4 +1575,47 @@ void QWindowsDirect2DPaintEngine::rasterFill(const QVectorPath &path, const QBru } } +bool QWindowsDirect2DPaintEngine::emulationRequired(EmulationType type) const +{ + Q_D(const QWindowsDirect2DPaintEngine); + + if (!state()->matrix.isAffine()) + return true; + + switch (type) { + case PenEmulation: + return d->pen.emulate; + break; + case BrushEmulation: + return d->brush.emulate; + break; + } + + return false; +} + +bool QWindowsDirect2DPaintEngine::antiAliasingEnabled() const +{ + return state()->renderHints & QPainter::Antialiasing; +} + +void QWindowsDirect2DPaintEngine::adjustForAliasing(QRectF *rect) +{ + if (!antiAliasingEnabled()) { + rect->adjust(MAGICAL_ALIASING_OFFSET, + MAGICAL_ALIASING_OFFSET, + MAGICAL_ALIASING_OFFSET, + MAGICAL_ALIASING_OFFSET); + } +} + +void QWindowsDirect2DPaintEngine::adjustForAliasing(QPointF *point) +{ + static const QPointF adjustment(MAGICAL_ALIASING_OFFSET, + MAGICAL_ALIASING_OFFSET); + + if (!antiAliasingEnabled()) + (*point) += adjustment; +} + QT_END_NAMESPACE -- cgit v1.2.3