1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the plugins of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "qwindowsdirect2dpaintengine.h"
41 #include "qwindowsdirect2dplatformpixmap.h"
42 #include "qwindowsdirect2dpaintdevice.h"
43 #include "qwindowsdirect2dcontext.h"
44 #include "qwindowsdirect2dhelpers.h"
45 #include "qwindowsdirect2dbitmap.h"
46 #include "qwindowsdirect2ddevicecontext.h"
47 
48 #include <QtFontDatabaseSupport/private/qwindowsfontdatabase_p.h>
49 #include <QtFontDatabaseSupport/private/qwindowsfontengine_p.h>
50 #include "qwindowsintegration.h"
51 
52 #include <QtCore/qmath.h>
53 #include <QtCore/qstack.h>
54 #include <QtCore/qsettings.h>
55 #include <QtGui/private/qpaintengine_p.h>
56 #include <QtGui/private/qtextengine_p.h>
57 #include <QtGui/private/qfontengine_p.h>
58 #include <QtGui/private/qstatictext_p.h>
59 
60 #include <d2d1_1.h>
61 #include <dwrite_1.h>
62 #include <wrl.h>
63 
64 using Microsoft::WRL::ComPtr;
65 
66 QT_BEGIN_NAMESPACE
67 
68 // The enum values below are set as tags on the device context
69 // in the various draw methods. When EndDraw is called the device context
70 // will report the last set tag number in case of errors
71 // along with an error code
72 
73 // Microsoft keeps a list of d2d error codes here:
74 // http://msdn.microsoft.com/en-us/library/windows/desktop/dd370979(v=vs.85).aspx
75 enum {
76     D2DDebugDrawInitialStateTag = -1,
77     D2DDebugFillTag = 1,
78     D2DDebugFillRectTag,
79     D2DDebugDrawRectsTag,
80     D2DDebugDrawRectFsTag,
81     D2DDebugDrawEllipseTag,
82     D2DDebugDrawEllipseFTag,
83     D2DDebugDrawImageTag,
84     D2DDebugDrawPixmapTag,
85     D2DDebugDrawStaticTextItemTag,
86     D2DDebugDrawTextItemTag
87 };
88 
89 //Clipping flags
90 enum : unsigned {
91     SimpleSystemClip = 0x1
92 };
93 
94 enum ClipType {
95     AxisAlignedClip,
96     LayerClip
97 };
98 
99 // Since d2d is a float-based system we need to be able to snap our drawing to whole pixels.
100 // Applying the magical aliasing offset to coordinates will do so, just make sure that
101 // aliased painting is turned on on the d2d device context.
102 static const qreal MAGICAL_ALIASING_OFFSET = 0.5;
103 
104 #define D2D_TAG(tag) d->dc()->SetTags(tag, tag)
105 
106 Q_GUI_EXPORT QImage qt_imageForBrush(int brushStyle, bool invert);
107 
factory()108 static inline ID2D1Factory1 *factory()
109 {
110     return QWindowsDirect2DContext::instance()->d2dFactory();
111 }
112 
transformFromLine(const QLineF & line,qreal penWidth,qreal dashOffset)113 static inline D2D1_MATRIX_3X2_F transformFromLine(const QLineF &line, qreal penWidth, qreal dashOffset)
114 {
115     const qreal halfWidth = penWidth / 2;
116     const qreal angle = -qDegreesToRadians(line.angle());
117     const qreal sinA = qSin(angle);
118     const qreal cosA = qCos(angle);
119     QTransform transform = QTransform::fromTranslate(line.p1().x() + dashOffset * cosA + sinA * halfWidth,
120                                                      line.p1().y() + dashOffset * sinA - cosA * halfWidth);
121     transform.rotateRadians(angle);
122     return to_d2d_matrix_3x2_f(transform);
123 }
124 
125 static void adjustLine(QPointF *p1, QPointF *p2);
126 static bool isLinePositivelySloped(const QPointF &p1, const QPointF &p2);
127 
qGradientStopsToD2DStops(const QGradientStops & qstops)128 static QVector<D2D1_GRADIENT_STOP> qGradientStopsToD2DStops(const QGradientStops &qstops)
129 {
130     QVector<D2D1_GRADIENT_STOP> stops(qstops.count());
131     for (int i = 0, count =  stops.size(); i < count; ++i) {
132         stops[i].position = FLOAT(qstops.at(i).first);
133         stops[i].color = to_d2d_color_f(qstops.at(i).second);
134     }
135     return stops;
136 }
137 
138 class Direct2DPathGeometryWriter
139 {
140 public:
begin()141     bool begin()
142     {
143         HRESULT hr = factory()->CreatePathGeometry(&m_geometry);
144         if (FAILED(hr)) {
145             qWarning("%s: Could not create path geometry: %#lx", __FUNCTION__, hr);
146             return false;
147         }
148 
149         hr = m_geometry->Open(&m_sink);
150         if (FAILED(hr)) {
151             qWarning("%s: Could not create geometry sink: %#lx", __FUNCTION__, hr);
152             return false;
153         }
154 
155         return true;
156     }
157 
setWindingFillEnabled(bool enable)158     void setWindingFillEnabled(bool enable)
159     {
160         if (enable)
161             m_sink->SetFillMode(D2D1_FILL_MODE_WINDING);
162         else
163             m_sink->SetFillMode(D2D1_FILL_MODE_ALTERNATE);
164     }
165 
setAliasingEnabled(bool enable)166     void setAliasingEnabled(bool enable)
167     {
168         m_roundCoordinates = enable;
169     }
170 
setPositiveSlopeAdjustmentEnabled(bool enable)171     void setPositiveSlopeAdjustmentEnabled(bool enable)
172     {
173         m_adjustPositivelySlopedLines = enable;
174     }
175 
isInFigure() const176     bool isInFigure() const
177     {
178         return m_inFigure;
179     }
180 
moveTo(const QPointF & point)181     void moveTo(const QPointF &point)
182     {
183         if (m_inFigure)
184             m_sink->EndFigure(D2D1_FIGURE_END_OPEN);
185 
186         m_sink->BeginFigure(adjusted(point), D2D1_FIGURE_BEGIN_FILLED);
187         m_inFigure = true;
188         m_previousPoint = point;
189     }
190 
lineTo(const QPointF & point)191     void lineTo(const QPointF &point)
192     {
193         QPointF pt = point;
194         if (m_adjustPositivelySlopedLines && isLinePositivelySloped(m_previousPoint, point)) {
195             moveTo(m_previousPoint - QPointF(0, 1));
196             pt -= QPointF(0, 1);
197         }
198         m_sink->AddLine(adjusted(pt));
199         if (pt != point)
200             moveTo(point);
201         m_previousPoint = point;
202     }
203 
curveTo(const QPointF & p1,const QPointF & p2,const QPointF & p3)204     void curveTo(const QPointF &p1, const QPointF &p2, const QPointF &p3)
205     {
206         D2D1_BEZIER_SEGMENT segment = {
207             adjusted(p1),
208             adjusted(p2),
209             adjusted(p3)
210         };
211 
212         m_sink->AddBezier(segment);
213         m_previousPoint = p3;
214     }
215 
close()216     void close()
217     {
218         if (m_inFigure)
219             m_sink->EndFigure(D2D1_FIGURE_END_OPEN);
220 
221         m_sink->Close();
222     }
223 
geometry() const224     ComPtr<ID2D1PathGeometry1> geometry() const
225     {
226         return m_geometry;
227     }
228 
229 private:
adjusted(const QPointF & point)230     D2D1_POINT_2F adjusted(const QPointF &point)
231     {
232         static const QPointF adjustment(MAGICAL_ALIASING_OFFSET,
233                                         MAGICAL_ALIASING_OFFSET);
234 
235         if (m_roundCoordinates)
236             return to_d2d_point_2f(point + adjustment);
237         else
238             return to_d2d_point_2f(point);
239     }
240 
241     ComPtr<ID2D1PathGeometry1> m_geometry;
242     ComPtr<ID2D1GeometrySink> m_sink;
243 
244     bool m_inFigure = false;
245     bool m_roundCoordinates = false;
246     bool m_adjustPositivelySlopedLines = false;
247     QPointF m_previousPoint;
248 };
249 
250 struct D2DVectorPathCache {
251     ComPtr<ID2D1PathGeometry1> aliased;
252     ComPtr<ID2D1PathGeometry1> antiAliased;
253 
cleanup_funcD2DVectorPathCache254     static void cleanup_func(QPaintEngineEx *engine, void *data) {
255         Q_UNUSED(engine);
256         auto *e = static_cast<D2DVectorPathCache *>(data);
257         delete e;
258     }
259 };
260 
261 class QWindowsDirect2DPaintEnginePrivate : public QPaintEngineExPrivate
262 {
263     Q_DECLARE_PUBLIC(QWindowsDirect2DPaintEngine)
264 public:
QWindowsDirect2DPaintEnginePrivate(QWindowsDirect2DBitmap * bm,QWindowsDirect2DPaintEngine::Flags flags)265     QWindowsDirect2DPaintEnginePrivate(QWindowsDirect2DBitmap *bm, QWindowsDirect2DPaintEngine::Flags flags)
266         : bitmap(bm)
267         , flags(flags)
268     {
269         pen.reset();
270         brush.reset();
271 
272         dc()->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED);
273     }
274 
275     QWindowsDirect2DBitmap *bitmap;
276     QImage fallbackImage;
277 
278     unsigned int clipFlags = 0;
279     QStack<ClipType> pushedClips;
280     QWindowsDirect2DPaintEngine::Flags flags;
281 
282     QPointF currentBrushOrigin;
283 
284     QHash< QFontDef, ComPtr<IDWriteFontFace> > fontCache;
285 
286     struct {
287         bool emulate;
288         QPen qpen;
289         ComPtr<ID2D1Brush> brush;
290         ComPtr<ID2D1StrokeStyle1> strokeStyle;
291         ComPtr<ID2D1BitmapBrush1> dashBrush;
292         int dashLength;
293 
resetQWindowsDirect2DPaintEnginePrivate::__anonfdfb425e0308294         inline void reset() {
295             emulate = false;
296             qpen = QPen();
297             brush.Reset();
298             strokeStyle.Reset();
299             dashBrush.Reset();
300             dashLength = 0;
301         }
302     } pen;
303 
304     struct {
305         bool emulate;
306         QBrush qbrush;
307         ComPtr<ID2D1Brush> brush;
308 
resetQWindowsDirect2DPaintEnginePrivate::__anonfdfb425e0408309         inline void reset() {
310             emulate = false;
311             brush.Reset();
312             qbrush = QBrush();
313         }
314     } brush;
315 
dc() const316     inline ID2D1DeviceContext *dc() const
317     {
318         Q_ASSERT(bitmap);
319         return bitmap->deviceContext()->get();
320     }
321 
interpolationMode() const322     inline D2D1_INTERPOLATION_MODE interpolationMode() const
323     {
324         Q_Q(const QWindowsDirect2DPaintEngine);
325         return (q->state()->renderHints & QPainter::SmoothPixmapTransform) ? D2D1_INTERPOLATION_MODE_LINEAR
326                                                                            : D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR;
327     }
328 
antialiasMode() const329     inline D2D1_ANTIALIAS_MODE antialiasMode() const
330     {
331         Q_Q(const QWindowsDirect2DPaintEngine);
332         return (q->state()->renderHints & QPainter::Antialiasing) ? D2D1_ANTIALIAS_MODE_PER_PRIMITIVE
333                                                                   : D2D1_ANTIALIAS_MODE_ALIASED;
334     }
335 
layerOptions() const336     inline D2D1_LAYER_OPTIONS1 layerOptions() const
337     {
338         if (flags & QWindowsDirect2DPaintEngine::TranslucentTopLevelWindow)
339             return D2D1_LAYER_OPTIONS1_NONE;
340         else
341             return D2D1_LAYER_OPTIONS1_INITIALIZE_FROM_BACKGROUND;
342     }
343 
updateTransform(const QTransform & transform)344     void updateTransform(const QTransform &transform)
345     {
346         dc()->SetTransform(to_d2d_matrix_3x2_f(transform));
347     }
348 
updateOpacity(qreal opacity)349     void updateOpacity(qreal opacity)
350     {
351         if (brush.brush)
352             brush.brush->SetOpacity(FLOAT(opacity));
353         if (pen.brush)
354             pen.brush->SetOpacity(FLOAT(opacity));
355     }
356 
pushClip(const QVectorPath & path)357     void pushClip(const QVectorPath &path)
358     {
359         Q_Q(QWindowsDirect2DPaintEngine);
360 
361         if (path.isEmpty()) {
362             D2D_RECT_F rect = {0, 0, 0, 0};
363             dc()->PushAxisAlignedClip(rect, antialiasMode());
364             pushedClips.push(AxisAlignedClip);
365         } else if (path.isRect() && (q->state()->matrix.type() <= QTransform::TxScale)) {
366             const qreal * const points = path.points();
367             D2D_RECT_F rect = {
368                 FLOAT(points[0]), // left
369                 FLOAT(points[1]), // top
370                 FLOAT(points[2]), // right,
371                 FLOAT(points[5])  // bottom
372             };
373 
374             dc()->PushAxisAlignedClip(rect, antialiasMode());
375             pushedClips.push(AxisAlignedClip);
376         } else {
377             ComPtr<ID2D1PathGeometry1> geometry = vectorPathToID2D1PathGeometry(path);
378             if (!geometry) {
379                 qWarning("%s: Could not convert vector path to painter path!", __FUNCTION__);
380                 return;
381             }
382 
383             dc()->PushLayer(D2D1::LayerParameters1(D2D1::InfiniteRect(),
384                                                    geometry.Get(),
385                                                    antialiasMode(),
386                                                    D2D1::IdentityMatrix(),
387                                                    1.0,
388                                                    nullptr,
389                                                    layerOptions()),
390                             nullptr);
391             pushedClips.push(LayerClip);
392         }
393     }
394 
clearClips()395     void clearClips()
396     {
397         while (!pushedClips.isEmpty()) {
398             switch (pushedClips.pop()) {
399             case AxisAlignedClip:
400                 dc()->PopAxisAlignedClip();
401                 break;
402             case LayerClip:
403                 dc()->PopLayer();
404                 break;
405             }
406         }
407     }
408 
updateClipEnabled(bool enabled)409     void updateClipEnabled(bool enabled)
410     {
411         if (!enabled)
412             clearClips();
413         else if (pushedClips.isEmpty())
414             replayClipOperations();
415     }
416 
clip(const QVectorPath & path,Qt::ClipOperation operation)417     void clip(const QVectorPath &path, Qt::ClipOperation operation)
418     {
419         switch (operation) {
420         case Qt::NoClip:
421             clearClips();
422             break;
423         case Qt::ReplaceClip:
424             clearClips();
425             pushClip(path);
426             break;
427         case Qt::IntersectClip:
428             pushClip(path);
429             break;
430         }
431     }
432 
updateCompositionMode(QPainter::CompositionMode mode)433     void updateCompositionMode(QPainter::CompositionMode mode)
434     {
435         switch (mode) {
436         case QPainter::CompositionMode_Source:
437             dc()->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_COPY);
438             break;
439         case QPainter::CompositionMode_SourceOver:
440             dc()->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_SOURCE_OVER);
441             break;
442 
443         default:
444             // Activating an unsupported mode at any time will cause the QImage
445             // fallback to be used for the remainder of the active paint session
446             dc()->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_COPY);
447             flags |= QWindowsDirect2DPaintEngine::EmulateComposition;
448             break;
449         }
450     }
451 
updateBrush(const QBrush & newBrush)452     void updateBrush(const QBrush &newBrush)
453     {
454         Q_Q(const QWindowsDirect2DPaintEngine);
455 
456         if (qbrush_fast_equals(brush.qbrush, newBrush) && (brush.brush || brush.emulate))
457             return;
458 
459         brush.brush = to_d2d_brush(newBrush, &brush.emulate);
460         brush.qbrush = newBrush;
461 
462         if (brush.brush) {
463             brush.brush->SetOpacity(FLOAT(q->state()->opacity));
464             applyBrushOrigin(currentBrushOrigin);
465         }
466     }
467 
updateBrushOrigin(const QPointF & brushOrigin)468     void updateBrushOrigin(const QPointF &brushOrigin)
469     {
470         negateCurrentBrushOrigin();
471         applyBrushOrigin(brushOrigin);
472     }
473 
negateCurrentBrushOrigin()474     void negateCurrentBrushOrigin()
475     {
476         if (brush.brush && !currentBrushOrigin.isNull()) {
477             D2D1_MATRIX_3X2_F transform;
478             brush.brush->GetTransform(&transform);
479 
480             brush.brush->SetTransform(*(D2D1::Matrix3x2F::ReinterpretBaseType(&transform))
481                                       * D2D1::Matrix3x2F::Translation(FLOAT(-currentBrushOrigin.x()),
482                                                                       FLOAT(-currentBrushOrigin.y())));
483         }
484     }
485 
applyBrushOrigin(const QPointF & origin)486     void applyBrushOrigin(const QPointF &origin)
487     {
488         if (brush.brush && !origin.isNull()) {
489             D2D1_MATRIX_3X2_F transform;
490             brush.brush->GetTransform(&transform);
491 
492             brush.brush->SetTransform(*(D2D1::Matrix3x2F::ReinterpretBaseType(&transform))
493                                       * D2D1::Matrix3x2F::Translation(FLOAT(origin.x()), FLOAT(origin.y())));
494         }
495 
496         currentBrushOrigin = origin;
497     }
498 
updatePen(const QPen & newPen)499     void updatePen(const QPen &newPen)
500     {
501         Q_Q(const QWindowsDirect2DPaintEngine);
502         if (qpen_fast_equals(newPen, pen.qpen) && (pen.brush || pen.emulate))
503             return;
504 
505         pen.reset();
506         pen.qpen = newPen;
507 
508         if (newPen.style() == Qt::NoPen)
509             return;
510 
511         pen.brush = to_d2d_brush(newPen.brush(), &pen.emulate);
512         if (!pen.brush)
513             return;
514 
515         pen.brush->SetOpacity(FLOAT(q->state()->opacity));
516 
517         D2D1_STROKE_STYLE_PROPERTIES1 props = {};
518 
519         switch (newPen.capStyle()) {
520         case Qt::SquareCap:
521             props.startCap = props.endCap = props.dashCap = D2D1_CAP_STYLE_SQUARE;
522             break;
523         case Qt::RoundCap:
524             props.startCap = props.endCap = props.dashCap = D2D1_CAP_STYLE_ROUND;
525             break;
526         case Qt::FlatCap:
527         default:
528             props.startCap = props.endCap = props.dashCap = D2D1_CAP_STYLE_FLAT;
529             break;
530         }
531 
532         switch (newPen.joinStyle()) {
533         case Qt::BevelJoin:
534             props.lineJoin = D2D1_LINE_JOIN_BEVEL;
535             break;
536         case Qt::RoundJoin:
537             props.lineJoin = D2D1_LINE_JOIN_ROUND;
538             break;
539         case Qt::MiterJoin:
540         default:
541             props.lineJoin = D2D1_LINE_JOIN_MITER;
542             break;
543         }
544 
545         props.miterLimit = FLOAT(newPen.miterLimit() * qreal(2.0)); // D2D and Qt miter specs differ
546         props.dashOffset = FLOAT(newPen.dashOffset());
547 
548         if (newPen.widthF() == 0)
549             props.transformType = D2D1_STROKE_TRANSFORM_TYPE_HAIRLINE;
550         else if (qt_pen_is_cosmetic(newPen, q->state()->renderHints))
551             props.transformType = D2D1_STROKE_TRANSFORM_TYPE_FIXED;
552         else
553             props.transformType = D2D1_STROKE_TRANSFORM_TYPE_NORMAL;
554 
555         switch (newPen.style()) {
556         case Qt::SolidLine:
557             props.dashStyle = D2D1_DASH_STYLE_SOLID;
558             break;
559 
560         case Qt::DotLine:
561         case Qt::DashDotLine:
562         case Qt::DashDotDotLine:
563             // Try and match Qt's raster engine in output as closely as possible
564             if (newPen.widthF() <= 1.0)
565                 props.startCap = props.endCap = props.dashCap = D2D1_CAP_STYLE_FLAT;
566 
567             Q_FALLTHROUGH();
568         default:
569             props.dashStyle = D2D1_DASH_STYLE_CUSTOM;
570             break;
571         }
572 
573         HRESULT hr;
574 
575         if (props.dashStyle == D2D1_DASH_STYLE_CUSTOM) {
576             QVector<qreal> dashes = newPen.dashPattern();
577             QVector<FLOAT> converted(dashes.size());
578             qreal penWidth = pen.qpen.widthF();
579             qreal brushWidth = 0;
580             for (int i = 0; i < dashes.size(); i++) {
581                 converted[i] = FLOAT(dashes[i]);
582                 brushWidth += penWidth * dashes[i];
583             }
584 
585             hr = factory()->CreateStrokeStyle(props, converted.constData(), UINT32(converted.size()), &pen.strokeStyle);
586 
587             // Create a combined brush/dash pattern for optimized line drawing
588             QWindowsDirect2DBitmap bitmap;
589             bitmap.resize(int(ceil(brushWidth)), int(ceil(penWidth)));
590             bitmap.deviceContext()->begin();
591             bitmap.deviceContext()->get()->SetAntialiasMode(antialiasMode());
592             bitmap.deviceContext()->get()->SetTransform(D2D1::IdentityMatrix());
593             bitmap.deviceContext()->get()->Clear();
594             const qreal offsetX = (qreal(bitmap.size().width()) - brushWidth) / 2;
595             const qreal offsetY = qreal(bitmap.size().height()) / 2;
596             bitmap.deviceContext()->get()->DrawLine(D2D1::Point2F(FLOAT(offsetX), FLOAT(offsetY)),
597                                                     D2D1::Point2F(FLOAT(brushWidth), FLOAT(offsetY)),
598                                                     pen.brush.Get(), FLOAT(penWidth), pen.strokeStyle.Get());
599             bitmap.deviceContext()->end();
600             D2D1_BITMAP_BRUSH_PROPERTIES1 bitmapBrushProperties = D2D1::BitmapBrushProperties1(
601                         D2D1_EXTEND_MODE_WRAP, D2D1_EXTEND_MODE_CLAMP, D2D1_INTERPOLATION_MODE_LINEAR);
602             hr = dc()->CreateBitmapBrush(bitmap.bitmap(), bitmapBrushProperties, &pen.dashBrush);
603             pen.dashLength = bitmap.size().width();
604         } else {
605             hr = factory()->CreateStrokeStyle(props, nullptr, 0, &pen.strokeStyle);
606         }
607 
608         if (FAILED(hr))
609             qWarning("%s: Could not create stroke style: %#lx", __FUNCTION__, hr);
610     }
611 
to_d2d_brush(const QBrush & newBrush,bool * needsEmulation)612     ComPtr<ID2D1Brush> to_d2d_brush(const QBrush &newBrush, bool *needsEmulation)
613     {
614         HRESULT hr;
615         ComPtr<ID2D1Brush> result;
616 
617         Q_ASSERT(needsEmulation);
618 
619         *needsEmulation = false;
620 
621         switch (newBrush.style()) {
622         case Qt::NoBrush:
623             break;
624 
625         case Qt::SolidPattern:
626         {
627             ComPtr<ID2D1SolidColorBrush> solid;
628 
629             hr = dc()->CreateSolidColorBrush(to_d2d_color_f(newBrush.color()), &solid);
630             if (FAILED(hr)) {
631                 qWarning("%s: Could not create solid color brush: %#lx", __FUNCTION__, hr);
632                 break;
633             }
634 
635             hr = solid.As(&result);
636             if (FAILED(hr))
637                 qWarning("%s: Could not convert solid color brush: %#lx", __FUNCTION__, hr);
638         }
639             break;
640 
641         case Qt::Dense1Pattern:
642         case Qt::Dense2Pattern:
643         case Qt::Dense3Pattern:
644         case Qt::Dense4Pattern:
645         case Qt::Dense5Pattern:
646         case Qt::Dense6Pattern:
647         case Qt::Dense7Pattern:
648         case Qt::HorPattern:
649         case Qt::VerPattern:
650         case Qt::CrossPattern:
651         case Qt::BDiagPattern:
652         case Qt::FDiagPattern:
653         case Qt::DiagCrossPattern:
654         {
655             ComPtr<ID2D1BitmapBrush1> bitmapBrush;
656             D2D1_BITMAP_BRUSH_PROPERTIES1 bitmapBrushProperties = {
657                 D2D1_EXTEND_MODE_WRAP,
658                 D2D1_EXTEND_MODE_WRAP,
659                 interpolationMode()
660             };
661 
662             QImage brushImg = qt_imageForBrush(newBrush.style(), false);
663             brushImg.setColor(0, newBrush.color().rgba());
664             brushImg.setColor(1, qRgba(0, 0, 0, 0));
665 
666             QWindowsDirect2DBitmap bitmap;
667             bool success = bitmap.fromImage(brushImg, Qt::AutoColor);
668             if (!success) {
669                 qWarning("%s: Could not create Direct2D bitmap from Qt pattern brush image", __FUNCTION__);
670                 break;
671             }
672 
673             hr = dc()->CreateBitmapBrush(bitmap.bitmap(),
674                                          bitmapBrushProperties,
675                                          &bitmapBrush);
676             if (FAILED(hr)) {
677                 qWarning("%s: Could not create Direct2D bitmap brush for Qt pattern brush: %#lx", __FUNCTION__, hr);
678                 break;
679             }
680 
681             hr = bitmapBrush.As(&result);
682             if (FAILED(hr))
683                 qWarning("%s: Could not convert Direct2D bitmap brush for Qt pattern brush: %#lx", __FUNCTION__, hr);
684         }
685             break;
686 
687         case Qt::LinearGradientPattern:
688             if (newBrush.gradient()->spread() != QGradient::PadSpread) {
689                 *needsEmulation = true;
690             } else {
691                 ComPtr<ID2D1LinearGradientBrush> linear;
692                 const auto *qlinear = static_cast<const QLinearGradient *>(newBrush.gradient());
693 
694                 D2D1_LINEAR_GRADIENT_BRUSH_PROPERTIES linearGradientBrushProperties;
695                 ComPtr<ID2D1GradientStopCollection> gradientStopCollection;
696 
697                 linearGradientBrushProperties.startPoint = to_d2d_point_2f(qlinear->start());
698                 linearGradientBrushProperties.endPoint = to_d2d_point_2f(qlinear->finalStop());
699 
700                 const QVector<D2D1_GRADIENT_STOP> stops = qGradientStopsToD2DStops(qlinear->stops());
701 
702                 hr = dc()->CreateGradientStopCollection(stops.constData(),
703                                                         UINT32(stops.size()),
704                                                         &gradientStopCollection);
705                 if (FAILED(hr)) {
706                     qWarning("%s: Could not create gradient stop collection for linear gradient: %#lx", __FUNCTION__, hr);
707                     break;
708                 }
709 
710                 hr = dc()->CreateLinearGradientBrush(linearGradientBrushProperties, gradientStopCollection.Get(),
711                                                      &linear);
712                 if (FAILED(hr)) {
713                     qWarning("%s: Could not create Direct2D linear gradient brush: %#lx", __FUNCTION__, hr);
714                     break;
715                 }
716 
717                 hr = linear.As(&result);
718                 if (FAILED(hr)) {
719                     qWarning("%s: Could not convert Direct2D linear gradient brush: %#lx", __FUNCTION__, hr);
720                     break;
721                 }
722             }
723             break;
724 
725         case Qt::RadialGradientPattern:
726             if (newBrush.gradient()->spread() != QGradient::PadSpread) {
727                 *needsEmulation = true;
728             } else {
729                 ComPtr<ID2D1RadialGradientBrush> radial;
730                 const auto *qradial = static_cast<const QRadialGradient *>(newBrush.gradient());
731 
732                 D2D1_RADIAL_GRADIENT_BRUSH_PROPERTIES radialGradientBrushProperties;
733                 ComPtr<ID2D1GradientStopCollection> gradientStopCollection;
734 
735                 radialGradientBrushProperties.center = to_d2d_point_2f(qradial->center());
736                 radialGradientBrushProperties.gradientOriginOffset = to_d2d_point_2f(qradial->focalPoint() - qradial->center());
737                 radialGradientBrushProperties.radiusX = FLOAT(qradial->radius());
738                 radialGradientBrushProperties.radiusY = FLOAT(qradial->radius());
739 
740                 const QVector<D2D1_GRADIENT_STOP> stops = qGradientStopsToD2DStops(qradial->stops());
741 
742                 hr = dc()->CreateGradientStopCollection(stops.constData(), stops.size(), &gradientStopCollection);
743                 if (FAILED(hr)) {
744                     qWarning("%s: Could not create gradient stop collection for radial gradient: %#lx", __FUNCTION__, hr);
745                     break;
746                 }
747 
748                 hr = dc()->CreateRadialGradientBrush(radialGradientBrushProperties, gradientStopCollection.Get(),
749                                                      &radial);
750                 if (FAILED(hr)) {
751                     qWarning("%s: Could not create Direct2D radial gradient brush: %#lx", __FUNCTION__, hr);
752                     break;
753                 }
754 
755                 radial.As(&result);
756                 if (FAILED(hr)) {
757                     qWarning("%s: Could not convert Direct2D radial gradient brush: %#lx", __FUNCTION__, hr);
758                     break;
759                 }
760             }
761             break;
762 
763         case Qt::ConicalGradientPattern:
764             *needsEmulation = true;
765             break;
766 
767         case Qt::TexturePattern:
768         {
769             ComPtr<ID2D1BitmapBrush1> bitmapBrush;
770             D2D1_BITMAP_BRUSH_PROPERTIES1 bitmapBrushProperties = {
771                 D2D1_EXTEND_MODE_WRAP,
772                 D2D1_EXTEND_MODE_WRAP,
773                 interpolationMode()
774             };
775 
776             QWindowsDirect2DPlatformPixmap *pp = static_cast<QWindowsDirect2DPlatformPixmap *>(newBrush.texture().handle());
777             QWindowsDirect2DBitmap *bitmap = pp->bitmap();
778             hr = dc()->CreateBitmapBrush(bitmap->bitmap(),
779                                          bitmapBrushProperties,
780                                          &bitmapBrush);
781 
782             if (FAILED(hr)) {
783                 qWarning("%s: Could not create texture brush: %#lx", __FUNCTION__, hr);
784                 break;
785             }
786 
787             hr = bitmapBrush.As(&result);
788             if (FAILED(hr))
789                 qWarning("%s: Could not convert texture brush: %#lx", __FUNCTION__, hr);
790         }
791             break;
792         }
793 
794         if (result && !newBrush.transform().isIdentity())
795             result->SetTransform(to_d2d_matrix_3x2_f(newBrush.transform()));
796 
797         return result;
798     }
799 
vectorPathToID2D1PathGeometry(const QVectorPath & path)800     ComPtr<ID2D1PathGeometry1> vectorPathToID2D1PathGeometry(const QVectorPath &path)
801     {
802         Q_Q(QWindowsDirect2DPaintEngine);
803 
804         const bool alias = !q->antiAliasingEnabled();
805 
806         QVectorPath::CacheEntry *cacheEntry = path.isCacheable() ? path.lookupCacheData(q)
807                                                                  : nullptr;
808 
809         if (cacheEntry) {
810             auto *e = static_cast<D2DVectorPathCache *>(cacheEntry->data);
811             if (alias && e->aliased)
812                 return e->aliased;
813             else if (!alias && e->antiAliased)
814                 return e->antiAliased;
815         }
816 
817         Direct2DPathGeometryWriter writer;
818         if (!writer.begin())
819             return nullptr;
820 
821         writer.setWindingFillEnabled(path.hasWindingFill());
822         writer.setAliasingEnabled(alias);
823         writer.setPositiveSlopeAdjustmentEnabled(path.shape() == QVectorPath::LinesHint
824                                                  || path.shape() == QVectorPath::PolygonHint);
825 
826         const QPainterPath::ElementType *types = path.elements();
827         const int count = path.elementCount();
828         const qreal *points = path.points();
829 
830         Q_ASSERT(points);
831 
832         if (types) {
833             qreal x, y;
834 
835             for (int i = 0; i < count; i++) {
836                 x = points[i * 2];
837                 y = points[i * 2 + 1];
838 
839                 switch (types[i]) {
840                 case QPainterPath::MoveToElement:
841                     writer.moveTo(QPointF(x, y));
842                     break;
843 
844                 case QPainterPath::LineToElement:
845                     writer.lineTo(QPointF(x, y));
846                     break;
847 
848                 case QPainterPath::CurveToElement:
849                 {
850                     Q_ASSERT((i + 2) < count);
851                     Q_ASSERT(types[i+1] == QPainterPath::CurveToDataElement);
852                     Q_ASSERT(types[i+2] == QPainterPath::CurveToDataElement);
853 
854                     i++;
855                     const qreal x2 = points[i * 2];
856                     const qreal y2 = points[i * 2 + 1];
857 
858                     i++;
859                     const qreal x3 = points[i * 2];
860                     const qreal y3 = points[i * 2 + 1];
861 
862                     writer.curveTo(QPointF(x, y), QPointF(x2, y2), QPointF(x3, y3));
863                 }
864                     break;
865 
866                 case QPainterPath::CurveToDataElement:
867                     qWarning("%s: Unhandled Curve Data Element", __FUNCTION__);
868                     break;
869                 }
870             }
871         } else {
872             writer.moveTo(QPointF(points[0], points[1]));
873             for (int i = 1; i < count; i++)
874                 writer.lineTo(QPointF(points[i * 2], points[i * 2 + 1]));
875         }
876 
877         if (writer.isInFigure())
878             if (path.hasImplicitClose())
879                 writer.lineTo(QPointF(points[0], points[1]));
880 
881         writer.close();
882         ComPtr<ID2D1PathGeometry1> geometry = writer.geometry();
883 
884         if (path.isCacheable()) {
885             if (!cacheEntry)
886                 cacheEntry = path.addCacheData(q, new D2DVectorPathCache, D2DVectorPathCache::cleanup_func);
887 
888             auto *e = static_cast<D2DVectorPathCache *>(cacheEntry->data);
889             if (alias)
890                 e->aliased = geometry;
891             else
892                 e->antiAliased = geometry;
893         } else {
894             path.makeCacheable();
895         }
896 
897         return geometry;
898     }
899 
updateHints()900     void updateHints()
901     {
902         dc()->SetAntialiasMode(antialiasMode());
903     }
904 
drawGlyphRun(const D2D1_POINT_2F & pos,IDWriteFontFace * fontFace,const QFontDef & fontDef,int numGlyphs,const UINT16 * glyphIndices,const FLOAT * glyphAdvances,const DWRITE_GLYPH_OFFSET * glyphOffsets,bool rtl)905     void drawGlyphRun(const D2D1_POINT_2F &pos,
906                       IDWriteFontFace *fontFace,
907                       const QFontDef &fontDef,
908                       int numGlyphs,
909                       const UINT16 *glyphIndices,
910                       const FLOAT *glyphAdvances,
911                       const DWRITE_GLYPH_OFFSET *glyphOffsets,
912                       bool rtl)
913     {
914         Q_Q(QWindowsDirect2DPaintEngine);
915 
916         DWRITE_GLYPH_RUN glyphRun = {
917             fontFace,          //    IDWriteFontFace           *fontFace;
918             FLOAT(fontDef.pixelSize), // FLOAT                 fontEmSize;
919             UINT32(numGlyphs), //    UINT32                    glyphCount;
920             glyphIndices,      //    const UINT16              *glyphIndices;
921             glyphAdvances,     //    const FLOAT               *glyphAdvances;
922             glyphOffsets,      //    const DWRITE_GLYPH_OFFSET *glyphOffsets;
923             FALSE,             //    BOOL                      isSideways;
924             rtl ? 1u : 0u      //    UINT32                    bidiLevel;
925         };
926 
927         const bool antiAlias = bool((q->state()->renderHints & QPainter::TextAntialiasing)
928                                     && !(fontDef.styleStrategy & QFont::NoAntialias));
929         const D2D1_TEXT_ANTIALIAS_MODE antialiasMode = (flags & QWindowsDirect2DPaintEngine::TranslucentTopLevelWindow)
930                 ? D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE : D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE;
931         dc()->SetTextAntialiasMode(antiAlias ? antialiasMode : D2D1_TEXT_ANTIALIAS_MODE_ALIASED);
932 
933         dc()->DrawGlyphRun(pos,
934                            &glyphRun,
935                            nullptr,
936                            pen.brush.Get(),
937                            DWRITE_MEASURING_MODE_GDI_CLASSIC);
938     }
939 
stroke(const QVectorPath & path)940     void stroke(const QVectorPath &path)
941     {
942         Q_Q(QWindowsDirect2DPaintEngine);
943 
944 QT_WARNING_PUSH
945 QT_WARNING_DISABLE_DEPRECATED
946         // Default path (no optimization)
947         if (!(path.shape() == QVectorPath::LinesHint || path.shape() == QVectorPath::PolygonHint)
948                 || !pen.dashBrush
949 #if QT_DEPRECATED_SINCE(5, 14)
950                 || q->state()->renderHints.testFlag(QPainter::HighQualityAntialiasing)
951 #endif
952                 || q->state()->renderHints.testFlag(QPainter::Antialiasing)) {
953 QT_WARNING_POP
954             ComPtr<ID2D1Geometry> geometry = vectorPathToID2D1PathGeometry(path);
955             if (!geometry) {
956                 qWarning("%s: Could not convert path to d2d geometry", __FUNCTION__);
957                 return;
958             }
959             dc()->DrawGeometry(geometry.Get(), pen.brush.Get(),
960                                FLOAT(pen.qpen.widthF()), pen.strokeStyle.Get());
961             return;
962         }
963 
964         // Optimized dash line drawing
965         const bool isPolygon = path.shape() == QVectorPath::PolygonHint && path.elementCount() >= 3;
966         const bool implicitClose = isPolygon && (path.hints() & QVectorPath::ImplicitClose);
967         const bool skipJoin = !isPolygon // Non-polygons don't require joins
968                 || (pen.qpen.joinStyle() == Qt::MiterJoin && qFuzzyIsNull(pen.qpen.miterLimit()));
969         const qreal *points = path.points();
970         const int lastElement = path.elementCount() - (implicitClose ? 1 : 2);
971         qreal dashOffset = 0;
972         QPointF jointStart;
973         ID2D1Brush *brush = pen.dashBrush ? pen.dashBrush.Get() : pen.brush.Get();
974         for (int i = 0; i <= lastElement; ++i) {
975             QPointF p1(points[i * 2], points[i * 2 + 1]);
976             QPointF p2 = implicitClose && i == lastElement ? QPointF(points[0], points[1])
977                                                            : QPointF(points[i * 2 + 2], points[i * 2 + 3]);
978             if (!isPolygon) // Advance the count for lines
979                 ++i;
980 
981             // Match raster engine output
982             if (p1 == p2 && pen.qpen.widthF() <= 1.0) {
983                 q->fillRect(QRectF(p1, QSizeF(pen.qpen.widthF(), pen.qpen.widthF())), pen.qpen.brush());
984                 continue;
985             }
986 
987             if (!q->antiAliasingEnabled())
988                 adjustLine(&p1, &p2);
989 
990             q->adjustForAliasing(&p1);
991             q->adjustForAliasing(&p2);
992 
993             const QLineF line(p1, p2);
994             const qreal lineLength = line.length();
995             if (pen.dashBrush) {
996                 pen.dashBrush->SetTransform(transformFromLine(line, pen.qpen.widthF(), dashOffset));
997                 dashOffset = pen.dashLength - fmod(lineLength - dashOffset, pen.dashLength);
998             }
999             dc()->DrawLine(to_d2d_point_2f(p1), to_d2d_point_2f(p2),
1000                            brush, FLOAT(pen.qpen.widthF()), nullptr);
1001 
1002             if (skipJoin)
1003                 continue;
1004 
1005             // Patch the join with the original brush
1006             const qreal patchSegment = pen.dashBrush ? qBound(0.0, (pen.dashLength - dashOffset) / lineLength, 1.0)
1007                                                      : pen.qpen.widthF();
1008             if (i > 0) {
1009                 Direct2DPathGeometryWriter writer;
1010                 writer.begin();
1011                 writer.moveTo(jointStart);
1012                 writer.lineTo(p1);
1013                 writer.lineTo(line.pointAt(patchSegment));
1014                 writer.close();
1015                 dc()->DrawGeometry(writer.geometry().Get(), pen.brush.Get(),
1016                                    FLOAT(pen.qpen.widthF()), pen.strokeStyle.Get());
1017             }
1018             // Record the start position of the next joint
1019             jointStart = line.pointAt(1 - patchSegment);
1020 
1021             if (implicitClose && i == lastElement) { // Close the polygon
1022                 Direct2DPathGeometryWriter writer;
1023                 writer.begin();
1024                 writer.moveTo(jointStart);
1025                 writer.lineTo(p2);
1026                 writer.lineTo(QLineF(p2, QPointF(points[2], points[3])).pointAt(patchSegment));
1027                 writer.close();
1028                 dc()->DrawGeometry(writer.geometry().Get(), pen.brush.Get(),
1029                                    FLOAT(pen.qpen.widthF()), pen.strokeStyle.Get());
1030             }
1031         }
1032     }
1033 
fontFaceFromFontEngine(QFontEngine * fe)1034     ComPtr<IDWriteFontFace> fontFaceFromFontEngine(QFontEngine *fe)
1035     {
1036         const QFontDef fontDef = fe->fontDef;
1037         ComPtr<IDWriteFontFace> fontFace = fontCache.value(fontDef);
1038         if (fontFace)
1039             return fontFace;
1040 
1041         LOGFONT lf = QWindowsFontDatabase::fontDefToLOGFONT(fontDef, QString());
1042 
1043         // Get substitute name
1044         static const char keyC[] = "HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows NT\\CurrentVersion\\FontSubstitutes";
1045         const QString familyName = QString::fromWCharArray(lf.lfFaceName);
1046         const QString nameSubstitute = QSettings(QLatin1String(keyC), QSettings::NativeFormat).value(familyName, familyName).toString();
1047         if (nameSubstitute != familyName) {
1048             const int nameSubstituteLength = qMin(nameSubstitute.length(), LF_FACESIZE - 1);
1049             memcpy(lf.lfFaceName, nameSubstitute.utf16(), size_t(nameSubstituteLength) * sizeof(wchar_t));
1050             lf.lfFaceName[nameSubstituteLength] = 0;
1051         }
1052 
1053         ComPtr<IDWriteFont> dwriteFont;
1054         HRESULT hr = QWindowsDirect2DContext::instance()->dwriteGdiInterop()->CreateFontFromLOGFONT(&lf, &dwriteFont);
1055         if (FAILED(hr)) {
1056             qDebug("%s: CreateFontFromLOGFONT failed: %#lx", __FUNCTION__, hr);
1057             return fontFace;
1058         }
1059 
1060         hr = dwriteFont->CreateFontFace(&fontFace);
1061         if (FAILED(hr)) {
1062             qDebug("%s: CreateFontFace failed: %#lx", __FUNCTION__, hr);
1063             return fontFace;
1064         }
1065 
1066         if (fontFace)
1067             fontCache.insert(fontDef, fontFace);
1068 
1069         return fontFace;
1070     }
1071 };
1072 
QWindowsDirect2DPaintEngine(QWindowsDirect2DBitmap * bitmap,Flags flags)1073 QWindowsDirect2DPaintEngine::QWindowsDirect2DPaintEngine(QWindowsDirect2DBitmap *bitmap, Flags flags)
1074     : QPaintEngineEx(*(new QWindowsDirect2DPaintEnginePrivate(bitmap, flags)))
1075 {
1076     QPaintEngine::PaintEngineFeatures unsupported =
1077             // As of 1.1 Direct2D does not natively support complex composition modes
1078             // However, using Direct2D effects that implement them should be possible
1079             QPaintEngine::PorterDuff
1080             | QPaintEngine::BlendModes
1081             | QPaintEngine::RasterOpModes
1082 
1083             // As of 1.1 Direct2D does not natively support perspective transforms
1084             // However, writing a custom effect that implements them should be possible
1085             // The built-in 3D transform effect unfortunately changes output image size, making
1086             // it unusable for us.
1087             | QPaintEngine::PerspectiveTransform;
1088 
1089     gccaps &= ~unsupported;
1090 }
1091 
begin(QPaintDevice * pdev)1092 bool QWindowsDirect2DPaintEngine::begin(QPaintDevice * pdev)
1093 {
1094     Q_D(QWindowsDirect2DPaintEngine);
1095 
1096     d->bitmap->deviceContext()->begin();
1097     d->dc()->SetTransform(D2D1::Matrix3x2F::Identity());
1098 
1099     if (systemClip().rectCount() > 1) {
1100         QPainterPath p;
1101         p.addRegion(systemClip());
1102 
1103         ComPtr<ID2D1PathGeometry1> geometry = d->vectorPathToID2D1PathGeometry(qtVectorPathForPath(p));
1104         if (!geometry)
1105             return false;
1106 
1107         d->dc()->PushLayer(D2D1::LayerParameters1(D2D1::InfiniteRect(),
1108                                                geometry.Get(),
1109                                                d->antialiasMode(),
1110                                                D2D1::IdentityMatrix(),
1111                                                1.0,
1112                                                nullptr,
1113                                                d->layerOptions()),
1114                         nullptr);
1115     } else {
1116         QRect clip(0, 0, pdev->width(), pdev->height());
1117         if (!systemClip().isEmpty())
1118             clip &= systemClip().boundingRect();
1119         d->dc()->PushAxisAlignedClip(to_d2d_rect_f(clip), D2D1_ANTIALIAS_MODE_ALIASED);
1120         d->clipFlags |= SimpleSystemClip;
1121     }
1122 
1123     D2D_TAG(D2DDebugDrawInitialStateTag);
1124 
1125     setActive(true);
1126     return true;
1127 }
1128 
end()1129 bool QWindowsDirect2DPaintEngine::end()
1130 {
1131     Q_D(QWindowsDirect2DPaintEngine);
1132 
1133     // Always clear all emulation-related things so we are in a clean state for our next painting run
1134     const bool emulatingComposition = d->flags.testFlag(EmulateComposition);
1135     d->flags &= ~QWindowsDirect2DPaintEngine::EmulateComposition;
1136     if (!d->fallbackImage.isNull()) {
1137         if (emulatingComposition)
1138             drawImage(d->fallbackImage.rect(), d->fallbackImage, d->fallbackImage.rect());
1139         d->fallbackImage = QImage();
1140     }
1141 
1142     // Pop any user-applied clipping
1143     d->clearClips();
1144     // Now the system clip from begin() above
1145     if (d->clipFlags & SimpleSystemClip) {
1146         d->dc()->PopAxisAlignedClip();
1147         d->clipFlags &= ~SimpleSystemClip;
1148     } else {
1149         d->dc()->PopLayer();
1150     }
1151 
1152     return d->bitmap->deviceContext()->end();
1153 }
1154 
type() const1155 QPaintEngine::Type QWindowsDirect2DPaintEngine::type() const
1156 {
1157     return QPaintEngine::Direct2D;
1158 }
1159 
setState(QPainterState * s)1160 void QWindowsDirect2DPaintEngine::setState(QPainterState *s)
1161 {
1162     Q_D(QWindowsDirect2DPaintEngine);
1163 
1164     QPaintEngineEx::setState(s);
1165     d->clearClips();
1166 
1167     clipEnabledChanged();
1168     penChanged();
1169     brushChanged();
1170     brushOriginChanged();
1171     opacityChanged();
1172     compositionModeChanged();
1173     renderHintsChanged();
1174     transformChanged();
1175 }
1176 
draw(const QVectorPath & path)1177 void QWindowsDirect2DPaintEngine::draw(const QVectorPath &path)
1178 {
1179     const QBrush &brush = state()->brush;
1180     if (qbrush_style(brush) != Qt::NoBrush) {
1181         if (emulationRequired(BrushEmulation))
1182             rasterFill(path, brush);
1183         else
1184             fill(path, brush);
1185     }
1186 
1187     const QPen &pen = state()->pen;
1188     if (qpen_style(pen) != Qt::NoPen && qbrush_style(qpen_brush(pen)) != Qt::NoBrush) {
1189         if (emulationRequired(PenEmulation))
1190             QPaintEngineEx::stroke(path, pen);
1191         else
1192             stroke(path, pen);
1193     }
1194 }
1195 
fill(const QVectorPath & path,const QBrush & brush)1196 void QWindowsDirect2DPaintEngine::fill(const QVectorPath &path, const QBrush &brush)
1197 {
1198     Q_D(QWindowsDirect2DPaintEngine);
1199     D2D_TAG(D2DDebugFillTag);
1200 
1201     if (path.isEmpty())
1202         return;
1203 
1204     ensureBrush(brush);
1205     if (emulationRequired(BrushEmulation)) {
1206         rasterFill(path, brush);
1207         return;
1208     }
1209 
1210     if (!d->brush.brush)
1211         return;
1212 
1213     ComPtr<ID2D1Geometry> geometry = d->vectorPathToID2D1PathGeometry(path);
1214     if (!geometry) {
1215         qWarning("%s: Could not convert path to d2d geometry", __FUNCTION__);
1216         return;
1217     }
1218 
1219     d->dc()->FillGeometry(geometry.Get(), d->brush.brush.Get());
1220 }
1221 
stroke(const QVectorPath & path,const QPen & pen)1222 void QWindowsDirect2DPaintEngine::stroke(const QVectorPath &path, const QPen &pen)
1223 {
1224     Q_D(QWindowsDirect2DPaintEngine);
1225     D2D_TAG(D2DDebugFillTag);
1226 
1227     if (path.isEmpty())
1228         return;
1229 
1230     ensurePen(pen);
1231     if (emulationRequired(PenEmulation)) {
1232         QPaintEngineEx::stroke(path, pen);
1233         return;
1234     }
1235 
1236     if (!d->pen.brush)
1237         return;
1238 
1239     d->stroke(path);
1240 }
1241 
clip(const QVectorPath & path,Qt::ClipOperation op)1242 void QWindowsDirect2DPaintEngine::clip(const QVectorPath &path, Qt::ClipOperation op)
1243 {
1244     Q_D(QWindowsDirect2DPaintEngine);
1245     d->clip(path, op);
1246 }
1247 
clipEnabledChanged()1248 void QWindowsDirect2DPaintEngine::clipEnabledChanged()
1249 {
1250     Q_D(QWindowsDirect2DPaintEngine);
1251     d->updateClipEnabled(state()->clipEnabled);
1252 }
1253 
penChanged()1254 void QWindowsDirect2DPaintEngine::penChanged()
1255 {
1256     Q_D(QWindowsDirect2DPaintEngine);
1257     d->updatePen(state()->pen);
1258 }
1259 
brushChanged()1260 void QWindowsDirect2DPaintEngine::brushChanged()
1261 {
1262     Q_D(QWindowsDirect2DPaintEngine);
1263     d->updateBrush(state()->brush);
1264 }
1265 
brushOriginChanged()1266 void QWindowsDirect2DPaintEngine::brushOriginChanged()
1267 {
1268     Q_D(QWindowsDirect2DPaintEngine);
1269     d->updateBrushOrigin(state()->brushOrigin);
1270 }
1271 
opacityChanged()1272 void QWindowsDirect2DPaintEngine::opacityChanged()
1273 {
1274     Q_D(QWindowsDirect2DPaintEngine);
1275     d->updateOpacity(state()->opacity);
1276 }
1277 
compositionModeChanged()1278 void QWindowsDirect2DPaintEngine::compositionModeChanged()
1279 {
1280     Q_D(QWindowsDirect2DPaintEngine);
1281     d->updateCompositionMode(state()->compositionMode());
1282 }
1283 
renderHintsChanged()1284 void QWindowsDirect2DPaintEngine::renderHintsChanged()
1285 {
1286     Q_D(QWindowsDirect2DPaintEngine);
1287     d->updateHints();
1288 }
1289 
transformChanged()1290 void QWindowsDirect2DPaintEngine::transformChanged()
1291 {
1292     Q_D(QWindowsDirect2DPaintEngine);
1293     d->updateTransform(state()->transform());
1294 }
1295 
fillRect(const QRectF & rect,const QBrush & brush)1296 void QWindowsDirect2DPaintEngine::fillRect(const QRectF &rect, const QBrush &brush)
1297 {
1298     Q_D(QWindowsDirect2DPaintEngine);
1299     D2D_TAG(D2DDebugFillRectTag);
1300 
1301     ensureBrush(brush);
1302 
1303     if (emulationRequired(BrushEmulation)) {
1304         QPaintEngineEx::fillRect(rect, brush);
1305     } else {
1306         QRectF r = rect.normalized();
1307         adjustForAliasing(&r);
1308 
1309         if (d->brush.brush)
1310             d->dc()->FillRectangle(to_d2d_rect_f(rect), d->brush.brush.Get());
1311     }
1312 }
1313 
drawRects(const QRect * rects,int rectCount)1314 void QWindowsDirect2DPaintEngine::drawRects(const QRect *rects, int rectCount)
1315 {
1316     Q_D(QWindowsDirect2DPaintEngine);
1317     D2D_TAG(D2DDebugDrawRectsTag);
1318 
1319     ensureBrush();
1320     ensurePen();
1321 
1322     if (emulationRequired(BrushEmulation) || emulationRequired(PenEmulation)) {
1323         QPaintEngineEx::drawRects(rects, rectCount);
1324     } else {
1325         QRectF rect;
1326         for (int i = 0; i < rectCount; i++) {
1327             rect = rects[i].normalized();
1328             adjustForAliasing(&rect);
1329 
1330             D2D1_RECT_F d2d_rect = to_d2d_rect_f(rect);
1331 
1332             if (d->brush.brush)
1333                 d->dc()->FillRectangle(d2d_rect, d->brush.brush.Get());
1334 
1335             if (d->pen.brush)
1336                 d->dc()->DrawRectangle(d2d_rect, d->pen.brush.Get(),
1337                                        FLOAT(d->pen.qpen.widthF()), d->pen.strokeStyle.Get());
1338         }
1339     }
1340 }
1341 
drawRects(const QRectF * rects,int rectCount)1342 void QWindowsDirect2DPaintEngine::drawRects(const QRectF *rects, int rectCount)
1343 {
1344     Q_D(QWindowsDirect2DPaintEngine);
1345     D2D_TAG(D2DDebugDrawRectFsTag);
1346 
1347     ensureBrush();
1348     ensurePen();
1349 
1350     if (emulationRequired(BrushEmulation) || emulationRequired(PenEmulation)) {
1351         QPaintEngineEx::drawRects(rects, rectCount);
1352     } else {
1353         QRectF rect;
1354         for (int i = 0; i < rectCount; i++) {
1355             rect = rects[i].normalized();
1356             adjustForAliasing(&rect);
1357 
1358             D2D1_RECT_F d2d_rect = to_d2d_rect_f(rect);
1359 
1360             if (d->brush.brush)
1361                 d->dc()->FillRectangle(d2d_rect, d->brush.brush.Get());
1362 
1363             if (d->pen.brush)
1364                 d->dc()->DrawRectangle(d2d_rect, d->pen.brush.Get(),
1365                                        FLOAT(d->pen.qpen.widthF()), d->pen.strokeStyle.Get());
1366         }
1367     }
1368 }
1369 
isLinePositivelySloped(const QPointF & p1,const QPointF & p2)1370 static bool isLinePositivelySloped(const QPointF &p1, const QPointF &p2)
1371 {
1372     if (p2.x() > p1.x())
1373         return p2.y() < p1.y();
1374 
1375     if (p1.x() > p2.x())
1376         return p1.y() < p2.y();
1377 
1378     return false;
1379 }
1380 
adjustLine(QPointF * p1,QPointF * p2)1381 static void adjustLine(QPointF *p1, QPointF *p2)
1382 {
1383     if (isLinePositivelySloped(*p1, *p2)) {
1384         p1->ry() -= qreal(1.0);
1385         p2->ry() -= qreal(1.0);
1386     }
1387 }
1388 
drawEllipse(const QRectF & r)1389 void QWindowsDirect2DPaintEngine::drawEllipse(const QRectF &r)
1390 {
1391     Q_D(QWindowsDirect2DPaintEngine);
1392     D2D_TAG(D2DDebugDrawEllipseFTag);
1393 
1394     ensureBrush();
1395     ensurePen();
1396 
1397     if (emulationRequired(BrushEmulation) || emulationRequired(PenEmulation)) {
1398         QPaintEngineEx::drawEllipse(r);
1399     } else {
1400         QPointF p = r.center();
1401         adjustForAliasing(&p);
1402 
1403         D2D1_ELLIPSE ellipse = {
1404             to_d2d_point_2f(p),
1405             FLOAT(r.width() / 2.0),
1406             FLOAT(r.height() / 2.0)
1407         };
1408 
1409         if (d->brush.brush)
1410             d->dc()->FillEllipse(ellipse, d->brush.brush.Get());
1411 
1412         if (d->pen.brush)
1413             d->dc()->DrawEllipse(ellipse, d->pen.brush.Get(),
1414                                  FLOAT(d->pen.qpen.widthF()),
1415                                  d->pen.strokeStyle.Get());
1416     }
1417 }
1418 
drawEllipse(const QRect & r)1419 void QWindowsDirect2DPaintEngine::drawEllipse(const QRect &r)
1420 {
1421     Q_D(QWindowsDirect2DPaintEngine);
1422     D2D_TAG(D2DDebugDrawEllipseTag);
1423 
1424     ensureBrush();
1425     ensurePen();
1426 
1427     if (emulationRequired(BrushEmulation) || emulationRequired(PenEmulation)) {
1428         QPaintEngineEx::drawEllipse(r);
1429     } else {
1430         QPointF p = r.center();
1431         adjustForAliasing(&p);
1432 
1433         D2D1_ELLIPSE ellipse = {
1434             to_d2d_point_2f(p),
1435             FLOAT(r.width() / 2.0),
1436             FLOAT(r.height() / 2.0)
1437         };
1438 
1439         if (d->brush.brush)
1440             d->dc()->FillEllipse(ellipse, d->brush.brush.Get());
1441 
1442         if (d->pen.brush)
1443             d->dc()->DrawEllipse(ellipse, d->pen.brush.Get(),
1444                                  FLOAT(d->pen.qpen.widthF()),
1445                                  d->pen.strokeStyle.Get());
1446     }
1447 }
1448 
drawImage(const QRectF & rectangle,const QImage & image,const QRectF & sr,Qt::ImageConversionFlags flags)1449 void QWindowsDirect2DPaintEngine::drawImage(const QRectF &rectangle, const QImage &image,
1450                                             const QRectF &sr, Qt::ImageConversionFlags flags)
1451 {
1452     Q_D(QWindowsDirect2DPaintEngine);
1453     D2D_TAG(D2DDebugDrawImageTag);
1454 
1455     QPixmap pixmap = QPixmap::fromImage(image, flags);
1456     drawPixmap(rectangle, pixmap, sr);
1457 }
1458 
drawPixmap(const QRectF & r,const QPixmap & pm,const QRectF & sr)1459 void QWindowsDirect2DPaintEngine::drawPixmap(const QRectF &r,
1460                                              const QPixmap &pm,
1461                                              const QRectF &sr)
1462 {
1463     Q_D(QWindowsDirect2DPaintEngine);
1464     D2D_TAG(D2DDebugDrawPixmapTag);
1465 
1466     if (pm.isNull())
1467         return;
1468 
1469     if (pm.handle()->pixelType() == QPlatformPixmap::BitmapType) {
1470         QImage i = pm.toImage();
1471         i.setColor(0, qRgba(0, 0, 0, 0));
1472         i.setColor(1, d->pen.qpen.color().rgba());
1473         drawImage(r, i, sr);
1474         return;
1475     }
1476 
1477     if (d->flags.testFlag(EmulateComposition)) {
1478         const qreal points[] = {
1479             r.x(), r.y(),
1480             r.x() + r.width(), r.y(),
1481             r.x() + r.width(), r.y() + r.height(),
1482             r.x(), r.y() + r.height()
1483         };
1484         const QVectorPath vp(points, 4, nullptr, QVectorPath::RectangleHint);
1485         QBrush brush(sr.isValid() ? pm.copy(sr.toRect()) : pm);
1486         brush.setTransform(QTransform::fromTranslate(r.x(), r.y()));
1487         rasterFill(vp, brush);
1488         return;
1489     }
1490 
1491     auto *pp = static_cast<QWindowsDirect2DPlatformPixmap *>(pm.handle());
1492     QWindowsDirect2DBitmap *bitmap = pp->bitmap();
1493 
1494     ensurePen();
1495 
1496     if (bitmap->bitmap() != d->bitmap->bitmap()) {
1497         // Good, src bitmap != dst bitmap
1498         if (sr.isValid())
1499             d->dc()->DrawBitmap(bitmap->bitmap(),
1500                                 to_d2d_rect_f(r), FLOAT(state()->opacity),
1501                                 d->interpolationMode(),
1502                                 to_d2d_rect_f(sr));
1503         else
1504             d->dc()->DrawBitmap(bitmap->bitmap(),
1505                                 to_d2d_rect_f(r), FLOAT(state()->opacity),
1506                                 d->interpolationMode());
1507     } else {
1508         // Ok, so the source pixmap and destination pixmap is the same.
1509         // D2D is not fond of this scenario, deal with it through
1510         // an intermediate bitmap
1511         QWindowsDirect2DBitmap intermediate;
1512 
1513         if (sr.isValid()) {
1514             bool r = intermediate.resize(int(sr.width()), int(sr.height()));
1515             if (!r) {
1516                 qWarning("%s: Could not resize intermediate bitmap to source rect size", __FUNCTION__);
1517                 return;
1518             }
1519 
1520             D2D1_RECT_U d2d_sr =  to_d2d_rect_u(sr.toRect());
1521             HRESULT hr = intermediate.bitmap()->CopyFromBitmap(nullptr,
1522                                                                bitmap->bitmap(),
1523                                                                &d2d_sr);
1524             if (FAILED(hr)) {
1525                 qWarning("%s: Could not copy source rect area from source bitmap to intermediate bitmap: %#lx", __FUNCTION__, hr);
1526                 return;
1527             }
1528         } else {
1529             bool r = intermediate.resize(bitmap->size().width(),
1530                                          bitmap->size().height());
1531             if (!r) {
1532                 qWarning("%s: Could not resize intermediate bitmap to source bitmap size", __FUNCTION__);
1533                 return;
1534             }
1535 
1536             HRESULT hr = intermediate.bitmap()->CopyFromBitmap(nullptr,
1537                                                                bitmap->bitmap(),
1538                                                                nullptr);
1539             if (FAILED(hr)) {
1540                 qWarning("%s: Could not copy source bitmap to intermediate bitmap: %#lx", __FUNCTION__, hr);
1541                 return;
1542             }
1543         }
1544 
1545         d->dc()->DrawBitmap(intermediate.bitmap(),
1546                             to_d2d_rect_f(r), FLOAT(state()->opacity),
1547                             d->interpolationMode());
1548     }
1549 }
1550 
drawStaticTextItem(QStaticTextItem * staticTextItem)1551 void QWindowsDirect2DPaintEngine::drawStaticTextItem(QStaticTextItem *staticTextItem)
1552 {
1553     Q_D(QWindowsDirect2DPaintEngine);
1554     D2D_TAG(D2DDebugDrawStaticTextItemTag);
1555 
1556     if (staticTextItem->numGlyphs == 0)
1557         return;
1558 
1559     ensurePen();
1560 
1561     // If we can't support the current configuration with Direct2D, fall back to slow path
1562     if (emulationRequired(PenEmulation)) {
1563         QPaintEngineEx::drawStaticTextItem(staticTextItem);
1564         return;
1565     }
1566 
1567     ComPtr<IDWriteFontFace> fontFace = d->fontFaceFromFontEngine(staticTextItem->fontEngine());
1568     if (!fontFace) {
1569         qWarning("%s: Could not find font - falling back to slow text rendering path.", __FUNCTION__);
1570         QPaintEngineEx::drawStaticTextItem(staticTextItem);
1571         return;
1572     }
1573 
1574     QVarLengthArray<UINT16> glyphIndices(staticTextItem->numGlyphs);
1575     QVarLengthArray<FLOAT> glyphAdvances(staticTextItem->numGlyphs);
1576     QVarLengthArray<DWRITE_GLYPH_OFFSET> glyphOffsets(staticTextItem->numGlyphs);
1577 
1578     for (int i = 0; i < staticTextItem->numGlyphs; i++) {
1579         glyphIndices[i] = UINT16(staticTextItem->glyphs[i]); // Imperfect conversion here
1580 
1581         // This looks  a little funky because the positions are precalculated
1582         glyphAdvances[i] = 0;
1583         glyphOffsets[i].advanceOffset = FLOAT(staticTextItem->glyphPositions[i].x.toReal());
1584         // Qt and Direct2D seem to disagree on the direction of the ascender offset...
1585         glyphOffsets[i].ascenderOffset = FLOAT(staticTextItem->glyphPositions[i].y.toReal() * -1);
1586     }
1587 
1588     d->drawGlyphRun(D2D1::Point2F(0, 0),
1589                     fontFace.Get(),
1590                     staticTextItem->fontEngine()->fontDef,
1591                     staticTextItem->numGlyphs,
1592                     glyphIndices.constData(),
1593                     glyphAdvances.constData(),
1594                     glyphOffsets.constData(),
1595                     false);
1596 }
1597 
drawTextItem(const QPointF & p,const QTextItem & textItem)1598 void QWindowsDirect2DPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
1599 {
1600     Q_D(QWindowsDirect2DPaintEngine);
1601     D2D_TAG(D2DDebugDrawTextItemTag);
1602 
1603     const auto &ti = static_cast<const QTextItemInt &>(textItem);
1604     if (ti.glyphs.numGlyphs == 0)
1605         return;
1606 
1607     ensurePen();
1608 
1609     // If we can't support the current configuration with Direct2D, fall back to slow path
1610     if (emulationRequired(PenEmulation)) {
1611         QPaintEngine::drawTextItem(p, textItem);
1612         return;
1613     }
1614 
1615     ComPtr<IDWriteFontFace> fontFace = d->fontFaceFromFontEngine(ti.fontEngine);
1616     if (!fontFace) {
1617         qWarning("%s: Could not find font - falling back to slow text rendering path.", __FUNCTION__);
1618         QPaintEngine::drawTextItem(p, textItem);
1619         return;
1620     }
1621 
1622     QVarLengthArray<UINT16> glyphIndices(ti.glyphs.numGlyphs);
1623     QVarLengthArray<FLOAT> glyphAdvances(ti.glyphs.numGlyphs);
1624     QVarLengthArray<DWRITE_GLYPH_OFFSET> glyphOffsets(ti.glyphs.numGlyphs);
1625 
1626     for (int i = 0; i < ti.glyphs.numGlyphs; i++) {
1627         glyphIndices[i] = UINT16(ti.glyphs.glyphs[i]); // Imperfect conversion here
1628         glyphAdvances[i] = FLOAT(ti.glyphs.effectiveAdvance(i).toReal());
1629         glyphOffsets[i].advanceOffset = FLOAT(ti.glyphs.offsets[i].x.toReal());
1630 
1631         // XXX Should we negate the y value like for static text items?
1632         glyphOffsets[i].ascenderOffset = FLOAT(ti.glyphs.offsets[i].y.toReal());
1633     }
1634 
1635     const bool rtl = (ti.flags & QTextItem::RightToLeft);
1636     const QPointF offset(rtl ? ti.width.toReal() : 0, 0);
1637 
1638     d->drawGlyphRun(to_d2d_point_2f(p + offset),
1639                     fontFace.Get(),
1640                     ti.fontEngine->fontDef,
1641                     ti.glyphs.numGlyphs,
1642                     glyphIndices.constData(),
1643                     glyphAdvances.constData(),
1644                     glyphOffsets.constData(),
1645                     rtl);
1646 }
1647 
ensureBrush()1648 void QWindowsDirect2DPaintEngine::ensureBrush()
1649 {
1650     ensureBrush(state()->brush);
1651 }
1652 
ensureBrush(const QBrush & brush)1653 void QWindowsDirect2DPaintEngine::ensureBrush(const QBrush &brush)
1654 {
1655     Q_D(QWindowsDirect2DPaintEngine);
1656     d->updateBrush(brush);
1657 }
1658 
ensurePen()1659 void QWindowsDirect2DPaintEngine::ensurePen()
1660 {
1661     ensurePen(state()->pen);
1662 }
1663 
ensurePen(const QPen & pen)1664 void QWindowsDirect2DPaintEngine::ensurePen(const QPen &pen)
1665 {
1666     Q_D(QWindowsDirect2DPaintEngine);
1667     d->updatePen(pen);
1668 }
1669 
rasterFill(const QVectorPath & path,const QBrush & brush)1670 void QWindowsDirect2DPaintEngine::rasterFill(const QVectorPath &path, const QBrush &brush)
1671 {
1672     Q_D(QWindowsDirect2DPaintEngine);
1673 
1674     if (d->fallbackImage.isNull()) {
1675         if (d->flags.testFlag(EmulateComposition)) {
1676             QWindowsDirect2DPaintEngineSuspender suspender(this);
1677             d->fallbackImage = d->bitmap->toImage();
1678         } else {
1679             d->fallbackImage = QImage(d->bitmap->size(), QImage::Format_ARGB32_Premultiplied);
1680             d->fallbackImage.fill(Qt::transparent);
1681         }
1682     }
1683 
1684     QImage &img = d->fallbackImage;
1685     QPainter p;
1686     QPaintEngine *engine = img.paintEngine();
1687 
1688     if (engine->isExtended() && p.begin(&img)) {
1689         p.setRenderHints(state()->renderHints);
1690         p.setCompositionMode(state()->compositionMode());
1691         p.setOpacity(state()->opacity);
1692         p.setBrushOrigin(state()->brushOrigin);
1693         p.setBrush(state()->brush);
1694         p.setPen(state()->pen);
1695 
1696         auto *extended = static_cast<QPaintEngineEx *>(engine);
1697         for (const QPainterClipInfo &info : qAsConst(state()->clipInfo)) {
1698             extended->state()->matrix = info.matrix;
1699             extended->transformChanged();
1700 
1701             switch (info.clipType) {
1702             case QPainterClipInfo::RegionClip:
1703                 extended->clip(info.region, info.operation);
1704                 break;
1705             case QPainterClipInfo::PathClip:
1706                 extended->clip(info.path, info.operation);
1707                 break;
1708             case QPainterClipInfo::RectClip:
1709                 extended->clip(info.rect, info.operation);
1710                 break;
1711             case QPainterClipInfo::RectFClip:
1712                 qreal right = info.rectf.x() + info.rectf.width();
1713                 qreal bottom = info.rectf.y() + info.rectf.height();
1714                 qreal pts[] = { info.rectf.x(), info.rectf.y(),
1715                                 right, info.rectf.y(),
1716                                 right, bottom,
1717                                 info.rectf.x(), bottom };
1718                 QVectorPath vp(pts, 4, nullptr, QVectorPath::RectangleHint);
1719                 extended->clip(vp, info.operation);
1720                 break;
1721             }
1722         }
1723 
1724         extended->state()->matrix = state()->matrix;
1725         extended->transformChanged();
1726 
1727         extended->fill(path, brush);
1728         if (!p.end())
1729             qWarning("%s: Paint Engine end returned false", __FUNCTION__);
1730 
1731         if (!d->flags.testFlag(EmulateComposition)) { // Emulated fallback will be flattened in end()
1732             d->updateClipEnabled(false);
1733             d->updateTransform(QTransform());
1734             drawImage(img.rect(), img, img.rect());
1735             d->fallbackImage = QImage();
1736             transformChanged();
1737             clipEnabledChanged();
1738         }
1739     } else {
1740         qWarning("%s: Could not fall back to QImage", __FUNCTION__);
1741     }
1742 }
1743 
emulationRequired(EmulationType type) const1744 bool QWindowsDirect2DPaintEngine::emulationRequired(EmulationType type) const
1745 {
1746     Q_D(const QWindowsDirect2DPaintEngine);
1747 
1748     if (d->flags.testFlag(EmulateComposition))
1749         return true;
1750 
1751     if (!state()->matrix.isAffine())
1752         return true;
1753 
1754     switch (type) {
1755     case PenEmulation:
1756         return d->pen.emulate;
1757         break;
1758     case BrushEmulation:
1759         return d->brush.emulate;
1760         break;
1761     }
1762 
1763     return false;
1764 }
1765 
antiAliasingEnabled() const1766 bool QWindowsDirect2DPaintEngine::antiAliasingEnabled() const
1767 {
1768     return state()->renderHints & QPainter::Antialiasing;
1769 }
1770 
adjustForAliasing(QRectF * rect)1771 void QWindowsDirect2DPaintEngine::adjustForAliasing(QRectF *rect)
1772 {
1773    if (!antiAliasingEnabled()) {
1774        rect->adjust(MAGICAL_ALIASING_OFFSET,
1775                     MAGICAL_ALIASING_OFFSET,
1776                     MAGICAL_ALIASING_OFFSET,
1777                     MAGICAL_ALIASING_OFFSET);
1778    }
1779 }
1780 
adjustForAliasing(QPointF * point)1781 void QWindowsDirect2DPaintEngine::adjustForAliasing(QPointF *point)
1782 {
1783     static const QPointF adjustment(MAGICAL_ALIASING_OFFSET,
1784                                     MAGICAL_ALIASING_OFFSET);
1785 
1786     if (!antiAliasingEnabled())
1787         (*point) += adjustment;
1788 }
1789 
suspend()1790 void QWindowsDirect2DPaintEngine::suspend()
1791 {
1792     end();
1793 }
1794 
resume()1795 void QWindowsDirect2DPaintEngine::resume()
1796 {
1797     begin(paintDevice());
1798     clipEnabledChanged();
1799     penChanged();
1800     brushChanged();
1801     brushOriginChanged();
1802     opacityChanged();
1803     compositionModeChanged();
1804     renderHintsChanged();
1805     transformChanged();
1806 }
1807 
1808 class QWindowsDirect2DPaintEngineSuspenderImpl
1809 {
1810     Q_DISABLE_COPY_MOVE(QWindowsDirect2DPaintEngineSuspenderImpl)
1811     QWindowsDirect2DPaintEngine *m_engine;
1812     bool m_active;
1813 public:
QWindowsDirect2DPaintEngineSuspenderImpl(QWindowsDirect2DPaintEngine * engine)1814     QWindowsDirect2DPaintEngineSuspenderImpl(QWindowsDirect2DPaintEngine *engine)
1815         : m_engine(engine)
1816         , m_active(engine->isActive())
1817     {
1818         if (m_active)
1819             m_engine->suspend();
1820     }
1821 
~QWindowsDirect2DPaintEngineSuspenderImpl()1822     ~QWindowsDirect2DPaintEngineSuspenderImpl()
1823     {
1824         if (m_active)
1825             m_engine->resume();
1826     }
1827 };
1828 
1829 class QWindowsDirect2DPaintEngineSuspenderPrivate
1830 {
1831 public:
QWindowsDirect2DPaintEngineSuspenderPrivate(QWindowsDirect2DPaintEngine * engine)1832     QWindowsDirect2DPaintEngineSuspenderPrivate(QWindowsDirect2DPaintEngine *engine)
1833         : engineSuspender(engine)
1834         , dcSuspender(static_cast<QWindowsDirect2DPaintEnginePrivate *>(engine->d_ptr.data())->bitmap->deviceContext())
1835     {
1836     }
1837 
1838     QWindowsDirect2DPaintEngineSuspenderImpl engineSuspender;
1839     QWindowsDirect2DDeviceContextSuspender dcSuspender;
1840 };
1841 
QWindowsDirect2DPaintEngineSuspender(QWindowsDirect2DPaintEngine * engine)1842 QWindowsDirect2DPaintEngineSuspender::QWindowsDirect2DPaintEngineSuspender(QWindowsDirect2DPaintEngine *engine)
1843     : d_ptr(new QWindowsDirect2DPaintEngineSuspenderPrivate(engine))
1844 {
1845 
1846 }
1847 
~QWindowsDirect2DPaintEngineSuspender()1848 QWindowsDirect2DPaintEngineSuspender::~QWindowsDirect2DPaintEngineSuspender()
1849 {
1850 }
1851 
resume()1852 void QWindowsDirect2DPaintEngineSuspender::resume()
1853 {
1854     d_ptr.reset();
1855 }
1856 
1857 QT_END_NAMESPACE
1858