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::__anoncba439e90308294 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::__anoncba439e90408309 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