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 QtGui module 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 "qpaintengineex_p.h"
41 #include "qpainter_p.h"
42 #include "qstroker_p.h"
43 #include "qbezier_p.h"
44 #include <private/qpainterpath_p.h>
45 #include <private/qfontengine_p.h>
46 #include <private/qstatictext_p.h>
47 
48 #include <qvarlengtharray.h>
49 #include <qdebug.h>
50 
51 
52 QT_BEGIN_NAMESPACE
53 
54 #if !defined(QT_MAX_CACHED_GLYPH_SIZE)
55 #  define QT_MAX_CACHED_GLYPH_SIZE 64
56 #endif
57 
58 /*******************************************************************************
59  *
60  * class QVectorPath
61  *
62  */
~QVectorPath()63 QVectorPath::~QVectorPath()
64 {
65     if (m_hints & ShouldUseCacheHint) {
66         CacheEntry *e = m_cache;
67         while (e) {
68             if (e->data)
69                 e->cleanup(e->engine, e->data);
70             CacheEntry *n = e->next;
71             delete e;
72             e = n;
73         }
74     }
75 }
76 
77 
controlPointRect() const78 QRectF QVectorPath::controlPointRect() const
79 {
80     if (m_hints & ControlPointRect)
81         return QRectF(QPointF(m_cp_rect.x1, m_cp_rect.y1), QPointF(m_cp_rect.x2, m_cp_rect.y2));
82 
83     if (m_count == 0) {
84         m_cp_rect.x1 = m_cp_rect.x2 = m_cp_rect.y1 = m_cp_rect.y2 = 0;
85         m_hints |= ControlPointRect;
86         return QRectF(QPointF(m_cp_rect.x1, m_cp_rect.y1), QPointF(m_cp_rect.x2, m_cp_rect.y2));
87     }
88     Q_ASSERT(m_points && m_count > 0);
89 
90     const qreal *pts = m_points;
91     m_cp_rect.x1 = m_cp_rect.x2 = *pts;
92     ++pts;
93     m_cp_rect.y1 = m_cp_rect.y2 = *pts;
94     ++pts;
95 
96     const qreal *epts = m_points + (m_count << 1);
97     while (pts < epts) {
98         qreal x = *pts;
99         if (x < m_cp_rect.x1) m_cp_rect.x1 = x;
100         else if (x > m_cp_rect.x2) m_cp_rect.x2 = x;
101         ++pts;
102 
103         qreal y = *pts;
104         if (y < m_cp_rect.y1) m_cp_rect.y1 = y;
105         else if (y > m_cp_rect.y2) m_cp_rect.y2 = y;
106         ++pts;
107     }
108 
109     m_hints |= ControlPointRect;
110     return QRectF(QPointF(m_cp_rect.x1, m_cp_rect.y1), QPointF(m_cp_rect.x2, m_cp_rect.y2));
111 }
112 
113 
addCacheData(QPaintEngineEx * engine,void * data,qvectorpath_cache_cleanup cleanup) const114 QVectorPath::CacheEntry *QVectorPath::addCacheData(QPaintEngineEx *engine, void *data,
115                                                    qvectorpath_cache_cleanup cleanup) const{
116     Q_ASSERT(!lookupCacheData(engine));
117     if ((m_hints & IsCachedHint) == 0) {
118         m_cache = nullptr;
119         m_hints |= IsCachedHint;
120     }
121     CacheEntry *e = new CacheEntry;
122     e->engine = engine;
123     e->data = data;
124     e->cleanup = cleanup;
125     e->next = m_cache;
126     m_cache = e;
127     return m_cache;
128 }
129 
130 
qtVectorPathForPath(const QPainterPath & path)131 const QVectorPath &qtVectorPathForPath(const QPainterPath &path)
132 {
133     Q_ASSERT(path.d_func());
134     return path.d_func()->vectorPath();
135 }
136 
137 #ifndef QT_NO_DEBUG_STREAM
operator <<(QDebug & s,const QVectorPath & path)138 QDebug Q_GUI_EXPORT &operator<<(QDebug &s, const QVectorPath &path)
139 {
140     QDebugStateSaver saver(s);
141     QRectF rf = path.controlPointRect();
142     s << "QVectorPath(size:" << path.elementCount()
143       << " hints:" << Qt::hex << path.hints()
144       << rf << ')';
145     return s;
146 }
147 #endif
148 
149 /*******************************************************************************
150  *
151  * class QPaintEngineExPrivate:
152  *
153  */
154 
155 
156 struct StrokeHandler {
StrokeHandlerStrokeHandler157     StrokeHandler(int reserve) : pts(reserve), types(reserve) {}
158     QDataBuffer<qreal> pts;
159     QDataBuffer<QPainterPath::ElementType> types;
160 };
161 
162 
QPaintEngineExPrivate()163 QPaintEngineExPrivate::QPaintEngineExPrivate()
164     : dasher(&stroker),
165       strokeHandler(nullptr),
166       activeStroker(nullptr),
167       strokerPen(Qt::NoPen)
168 {
169 }
170 
171 
~QPaintEngineExPrivate()172 QPaintEngineExPrivate::~QPaintEngineExPrivate()
173 {
174     delete strokeHandler;
175 }
176 
177 
replayClipOperations()178 void QPaintEngineExPrivate::replayClipOperations()
179 {
180     Q_Q(QPaintEngineEx);
181 
182     QPainter *p = q->painter();
183     if (!p || !p->d_ptr)
184         return;
185 
186     const QVector<QPainterClipInfo> &clipInfo = p->d_ptr->state->clipInfo;
187 
188     QTransform transform = q->state()->matrix;
189 
190     for (const QPainterClipInfo &info : clipInfo) {
191 
192         if (info.matrix != q->state()->matrix) {
193             q->state()->matrix = info.matrix;
194             q->transformChanged();
195         }
196 
197         switch (info.clipType) {
198         case QPainterClipInfo::RegionClip:
199             q->clip(info.region, info.operation);
200             break;
201         case QPainterClipInfo::PathClip:
202             q->clip(info.path, info.operation);
203             break;
204         case QPainterClipInfo::RectClip:
205             q->clip(info.rect, info.operation);
206             break;
207         case QPainterClipInfo::RectFClip: {
208             qreal right = info.rectf.x() + info.rectf.width();
209             qreal bottom = info.rectf.y() + info.rectf.height();
210             qreal pts[] = { info.rectf.x(), info.rectf.y(),
211                             right, info.rectf.y(),
212                             right, bottom,
213                             info.rectf.x(), bottom };
214             QVectorPath vp(pts, 4, nullptr, QVectorPath::RectangleHint);
215             q->clip(vp, info.operation);
216             break;
217             }
218         }
219     }
220 
221     if (transform != q->state()->matrix) {
222         q->state()->matrix = transform;
223         q->transformChanged();
224     }
225 }
226 
227 
hasClipOperations() const228 bool QPaintEngineExPrivate::hasClipOperations() const
229 {
230     Q_Q(const QPaintEngineEx);
231 
232     QPainter *p = q->painter();
233     if (!p || !p->d_ptr)
234         return false;
235 
236     return !p->d_ptr->state->clipInfo.isEmpty();
237 }
238 
239 /*******************************************************************************
240  *
241  * class QPaintEngineEx:
242  *
243  */
244 
245 static const QPainterPath::ElementType qpaintengineex_ellipse_types[] = {
246     QPainterPath::MoveToElement,
247     QPainterPath::CurveToElement,
248     QPainterPath::CurveToDataElement,
249     QPainterPath::CurveToDataElement,
250 
251     QPainterPath::CurveToElement,
252     QPainterPath::CurveToDataElement,
253     QPainterPath::CurveToDataElement,
254 
255     QPainterPath::CurveToElement,
256     QPainterPath::CurveToDataElement,
257     QPainterPath::CurveToDataElement,
258 
259     QPainterPath::CurveToElement,
260     QPainterPath::CurveToDataElement,
261     QPainterPath::CurveToDataElement
262 };
263 
264 static const QPainterPath::ElementType qpaintengineex_line_types_16[] = {
265     QPainterPath::MoveToElement, QPainterPath::LineToElement,
266     QPainterPath::MoveToElement, QPainterPath::LineToElement,
267     QPainterPath::MoveToElement, QPainterPath::LineToElement,
268     QPainterPath::MoveToElement, QPainterPath::LineToElement,
269     QPainterPath::MoveToElement, QPainterPath::LineToElement,
270     QPainterPath::MoveToElement, QPainterPath::LineToElement,
271     QPainterPath::MoveToElement, QPainterPath::LineToElement,
272     QPainterPath::MoveToElement, QPainterPath::LineToElement,
273     QPainterPath::MoveToElement, QPainterPath::LineToElement,
274     QPainterPath::MoveToElement, QPainterPath::LineToElement,
275     QPainterPath::MoveToElement, QPainterPath::LineToElement,
276     QPainterPath::MoveToElement, QPainterPath::LineToElement,
277     QPainterPath::MoveToElement, QPainterPath::LineToElement,
278     QPainterPath::MoveToElement, QPainterPath::LineToElement,
279     QPainterPath::MoveToElement, QPainterPath::LineToElement,
280     QPainterPath::MoveToElement, QPainterPath::LineToElement
281 };
282 
283 static const QPainterPath::ElementType qpaintengineex_rect4_types_32[] = {
284     QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 1
285     QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 2
286     QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 3
287     QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 4
288     QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 5
289     QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 6
290     QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 7
291     QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 8
292     QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 9
293     QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 10
294     QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 11
295     QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 12
296     QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 13
297     QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 14
298     QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 15
299     QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 16
300     QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 17
301     QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 18
302     QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 19
303     QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 20
304     QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 21
305     QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 22
306     QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 23
307     QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 24
308     QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 25
309     QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 26
310     QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 27
311     QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 28
312     QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 29
313     QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 30
314     QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 31
315     QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 32
316 };
317 
318 
319 static const QPainterPath::ElementType qpaintengineex_roundedrect_types[] = {
320     QPainterPath::MoveToElement,
321     QPainterPath::LineToElement,
322     QPainterPath::CurveToElement,
323     QPainterPath::CurveToDataElement,
324     QPainterPath::CurveToDataElement,
325     QPainterPath::LineToElement,
326     QPainterPath::CurveToElement,
327     QPainterPath::CurveToDataElement,
328     QPainterPath::CurveToDataElement,
329     QPainterPath::LineToElement,
330     QPainterPath::CurveToElement,
331     QPainterPath::CurveToDataElement,
332     QPainterPath::CurveToDataElement,
333     QPainterPath::LineToElement,
334     QPainterPath::CurveToElement,
335     QPainterPath::CurveToDataElement,
336     QPainterPath::CurveToDataElement
337 };
338 
339 
340 
qpaintengineex_moveTo(qreal x,qreal y,void * data)341 static void qpaintengineex_moveTo(qreal x, qreal y, void *data) {
342     ((StrokeHandler *) data)->pts.add(x);
343     ((StrokeHandler *) data)->pts.add(y);
344     ((StrokeHandler *) data)->types.add(QPainterPath::MoveToElement);
345 }
346 
qpaintengineex_lineTo(qreal x,qreal y,void * data)347 static void qpaintengineex_lineTo(qreal x, qreal y, void *data) {
348     ((StrokeHandler *) data)->pts.add(x);
349     ((StrokeHandler *) data)->pts.add(y);
350     ((StrokeHandler *) data)->types.add(QPainterPath::LineToElement);
351 }
352 
qpaintengineex_cubicTo(qreal c1x,qreal c1y,qreal c2x,qreal c2y,qreal ex,qreal ey,void * data)353 static void qpaintengineex_cubicTo(qreal c1x, qreal c1y, qreal c2x, qreal c2y, qreal ex, qreal ey, void *data) {
354     ((StrokeHandler *) data)->pts.add(c1x);
355     ((StrokeHandler *) data)->pts.add(c1y);
356     ((StrokeHandler *) data)->types.add(QPainterPath::CurveToElement);
357 
358     ((StrokeHandler *) data)->pts.add(c2x);
359     ((StrokeHandler *) data)->pts.add(c2y);
360     ((StrokeHandler *) data)->types.add(QPainterPath::CurveToDataElement);
361 
362     ((StrokeHandler *) data)->pts.add(ex);
363     ((StrokeHandler *) data)->pts.add(ey);
364     ((StrokeHandler *) data)->types.add(QPainterPath::CurveToDataElement);
365 }
366 
QPaintEngineEx()367 QPaintEngineEx::QPaintEngineEx()
368     : QPaintEngine(*new QPaintEngineExPrivate, AllFeatures)
369 {
370     extended = true;
371 }
372 
QPaintEngineEx(QPaintEngineExPrivate & data)373 QPaintEngineEx::QPaintEngineEx(QPaintEngineExPrivate &data)
374     : QPaintEngine(data, AllFeatures)
375 {
376     extended = true;
377 }
378 
createState(QPainterState * orig) const379 QPainterState *QPaintEngineEx::createState(QPainterState *orig) const
380 {
381     if (!orig)
382         return new QPainterState;
383     return new QPainterState(orig);
384 }
385 
386 Q_GUI_EXPORT extern bool qt_scaleForTransform(const QTransform &transform, qreal *scale); // qtransform.cpp
387 
stroke(const QVectorPath & path,const QPen & inPen)388 void QPaintEngineEx::stroke(const QVectorPath &path, const QPen &inPen)
389 {
390 #ifdef QT_DEBUG_DRAW
391     qDebug() << "QPaintEngineEx::stroke()" << pen;
392 #endif
393 
394     Q_D(QPaintEngineEx);
395 
396     if (path.isEmpty())
397         return;
398 
399     if (!d->strokeHandler) {
400         d->strokeHandler = new StrokeHandler(path.elementCount()+4);
401         d->stroker.setMoveToHook(qpaintengineex_moveTo);
402         d->stroker.setLineToHook(qpaintengineex_lineTo);
403         d->stroker.setCubicToHook(qpaintengineex_cubicTo);
404     }
405 
406     QRectF clipRect;
407     QPen pen = inPen;
408     if (pen.style() > Qt::SolidLine) {
409         QRectF cpRect = path.controlPointRect();
410         const QTransform &xf = state()->matrix;
411         if (qt_pen_is_cosmetic(pen, state()->renderHints)){
412             clipRect = d->exDeviceRect;
413             cpRect.translate(xf.dx(), xf.dy());
414         } else {
415             clipRect = xf.inverted().mapRect(QRectF(d->exDeviceRect));
416         }
417         // Check to avoid generating unwieldy amount of dashes that will not be visible anyway
418         qreal pw = pen.widthF() ? pen.widthF() : 1;
419         QRectF extentRect = cpRect.adjusted(-pw, -pw, pw, pw) & clipRect;
420         qreal extent = qMax(extentRect.width(), extentRect.height());
421         qreal patternLength = 0;
422         const QVector<qreal> pattern = pen.dashPattern();
423         const int patternSize = qMin(pattern.size(), 32);
424         for (int i = 0; i < patternSize; i++)
425             patternLength += qMax(pattern.at(i), qreal(0));
426         patternLength *= pw;
427         if (qFuzzyIsNull(patternLength)) {
428             pen.setStyle(Qt::NoPen);
429         } else if (extent / patternLength > 10000) {
430             // approximate stream of tiny dashes with semi-transparent solid line
431             pen.setStyle(Qt::SolidLine);
432             QColor color(pen.color());
433             color.setAlpha(color.alpha() / 2);
434             pen.setColor(color);
435         }
436     }
437 
438     if (!qpen_fast_equals(pen, d->strokerPen)) {
439         d->strokerPen = pen;
440         d->stroker.setJoinStyle(pen.joinStyle());
441         d->stroker.setCapStyle(pen.capStyle());
442         d->stroker.setMiterLimit(pen.miterLimit());
443         qreal penWidth = pen.widthF();
444         if (penWidth == 0)
445             d->stroker.setStrokeWidth(1);
446         else
447             d->stroker.setStrokeWidth(penWidth);
448 
449         Qt::PenStyle style = pen.style();
450         if (style == Qt::SolidLine) {
451             d->activeStroker = &d->stroker;
452         } else if (style == Qt::NoPen) {
453             d->activeStroker = nullptr;
454         } else {
455             d->dasher.setDashPattern(pen.dashPattern());
456             d->dasher.setDashOffset(pen.dashOffset());
457             d->activeStroker = &d->dasher;
458         }
459     }
460 
461     if (!d->activeStroker) {
462         return;
463     }
464 
465     if (!clipRect.isNull())
466         d->activeStroker->setClipRect(clipRect);
467 
468     if (d->activeStroker == &d->stroker)
469         d->stroker.setForceOpen(path.hasExplicitOpen());
470 
471     const QPainterPath::ElementType *types = path.elements();
472     const qreal *points = path.points();
473     int pointCount = path.elementCount();
474 
475     const qreal *lastPoint = points + (pointCount<<1);
476 
477     d->strokeHandler->types.reset();
478     d->strokeHandler->pts.reset();
479 
480     // Some engines might decide to optimize for the non-shape hint later on...
481     uint flags = QVectorPath::WindingFill;
482 
483     if (path.elementCount() > 2)
484         flags |= QVectorPath::NonConvexShapeMask;
485 
486     if (d->stroker.capStyle() == Qt::RoundCap || d->stroker.joinStyle() == Qt::RoundJoin)
487         flags |= QVectorPath::CurvedShapeMask;
488 
489     // ### Perspective Xforms are currently not supported...
490     if (!qt_pen_is_cosmetic(pen, state()->renderHints)) {
491         // We include cosmetic pens in this case to avoid having to
492         // change the current transform. Normal transformed,
493         // non-cosmetic pens will be transformed as part of fill
494         // later, so they are also covered here..
495         d->activeStroker->setCurveThresholdFromTransform(state()->matrix);
496         d->activeStroker->begin(d->strokeHandler);
497         if (types) {
498             while (points < lastPoint) {
499                 switch (*types) {
500                 case QPainterPath::MoveToElement:
501                     d->activeStroker->moveTo(points[0], points[1]);
502                     points += 2;
503                     ++types;
504                     break;
505                 case QPainterPath::LineToElement:
506                     d->activeStroker->lineTo(points[0], points[1]);
507                     points += 2;
508                     ++types;
509                     break;
510                 case QPainterPath::CurveToElement:
511                     d->activeStroker->cubicTo(points[0], points[1],
512                                               points[2], points[3],
513                                               points[4], points[5]);
514                     points += 6;
515                     types += 3;
516                     flags |= QVectorPath::CurvedShapeMask;
517                     break;
518                 default:
519                     break;
520                 }
521             }
522             if (path.hasImplicitClose())
523                 d->activeStroker->lineTo(path.points()[0], path.points()[1]);
524 
525         } else {
526             d->activeStroker->moveTo(points[0], points[1]);
527             points += 2;
528             while (points < lastPoint) {
529                 d->activeStroker->lineTo(points[0], points[1]);
530                 points += 2;
531             }
532             if (path.hasImplicitClose())
533                 d->activeStroker->lineTo(path.points()[0], path.points()[1]);
534         }
535         d->activeStroker->end();
536 
537         if (!d->strokeHandler->types.size()) // an empty path...
538             return;
539 
540         QVectorPath strokePath(d->strokeHandler->pts.data(),
541                                d->strokeHandler->types.size(),
542                                d->strokeHandler->types.data(),
543                                flags);
544         fill(strokePath, pen.brush());
545     } else {
546         // For cosmetic pens we need a bit of trickery... We to process xform the input points
547         if (state()->matrix.type() >= QTransform::TxProject) {
548             QPainterPath painterPath = state()->matrix.map(path.convertToPainterPath());
549             d->activeStroker->strokePath(painterPath, d->strokeHandler, QTransform());
550         } else {
551             d->activeStroker->setCurveThresholdFromTransform(QTransform());
552             d->activeStroker->begin(d->strokeHandler);
553             if (types) {
554                 while (points < lastPoint) {
555                     switch (*types) {
556                     case QPainterPath::MoveToElement: {
557                         QPointF pt = (*(const QPointF *) points) * state()->matrix;
558                         d->activeStroker->moveTo(pt.x(), pt.y());
559                         points += 2;
560                         ++types;
561                         break;
562                     }
563                     case QPainterPath::LineToElement: {
564                         QPointF pt = (*(const QPointF *) points) * state()->matrix;
565                         d->activeStroker->lineTo(pt.x(), pt.y());
566                         points += 2;
567                         ++types;
568                         break;
569                     }
570                     case QPainterPath::CurveToElement: {
571                         QPointF c1 = ((const QPointF *) points)[0] * state()->matrix;
572                         QPointF c2 = ((const QPointF *) points)[1] * state()->matrix;
573                         QPointF e =  ((const QPointF *) points)[2] * state()->matrix;
574                         d->activeStroker->cubicTo(c1.x(), c1.y(), c2.x(), c2.y(), e.x(), e.y());
575                         points += 6;
576                         types += 3;
577                         flags |= QVectorPath::CurvedShapeMask;
578                         break;
579                     }
580                     default:
581                         break;
582                     }
583                 }
584                 if (path.hasImplicitClose()) {
585                     QPointF pt = * ((const QPointF *) path.points()) * state()->matrix;
586                     d->activeStroker->lineTo(pt.x(), pt.y());
587                 }
588 
589             } else {
590                 QPointF p = ((const QPointF *)points)[0] * state()->matrix;
591                 d->activeStroker->moveTo(p.x(), p.y());
592                 points += 2;
593                 while (points < lastPoint) {
594                     QPointF p = ((const QPointF *)points)[0] * state()->matrix;
595                     d->activeStroker->lineTo(p.x(), p.y());
596                     points += 2;
597                 }
598                 if (path.hasImplicitClose())
599                     d->activeStroker->lineTo(p.x(), p.y());
600             }
601             d->activeStroker->end();
602         }
603 
604         QVectorPath strokePath(d->strokeHandler->pts.data(),
605                                d->strokeHandler->types.size(),
606                                d->strokeHandler->types.data(),
607                                flags);
608 
609         QTransform xform = state()->matrix;
610         state()->matrix = QTransform();
611         transformChanged();
612 
613         QBrush brush = pen.brush();
614         if (qbrush_style(brush) != Qt::SolidPattern)
615             brush.setTransform(brush.transform() * xform);
616 
617         fill(strokePath, brush);
618 
619         state()->matrix = xform;
620         transformChanged();
621     }
622 }
623 
draw(const QVectorPath & path)624 void QPaintEngineEx::draw(const QVectorPath &path)
625 {
626     const QBrush &brush = state()->brush;
627     if (qbrush_style(brush) != Qt::NoBrush)
628         fill(path, brush);
629 
630     const QPen &pen = state()->pen;
631     if (qpen_style(pen) != Qt::NoPen && qbrush_style(qpen_brush(pen)) != Qt::NoBrush)
632         stroke(path, pen);
633 }
634 
635 
clip(const QRect & r,Qt::ClipOperation op)636 void QPaintEngineEx::clip(const QRect &r, Qt::ClipOperation op)
637 {
638     qreal right = r.x() + r.width();
639     qreal bottom = r.y() + r.height();
640     qreal pts[] = { qreal(r.x()), qreal(r.y()),
641                     right, qreal(r.y()),
642                     right, bottom,
643                     qreal(r.x()), bottom,
644                     qreal(r.x()), qreal(r.y()) };
645     QVectorPath vp(pts, 5, nullptr, QVectorPath::RectangleHint);
646     clip(vp, op);
647 }
648 
clip(const QRegion & region,Qt::ClipOperation op)649 void QPaintEngineEx::clip(const QRegion &region, Qt::ClipOperation op)
650 {
651     const auto rectsInRegion = region.rectCount();
652     if (rectsInRegion == 1) {
653         clip(*region.begin(), op);
654     } else if (rectsInRegion <= 32) {
655         qreal pts[2*32*4];
656         int pos = 0;
657         for (QRect r : region) {
658             qreal x1 = r.x();
659             qreal y1 = r.y();
660             qreal x2 = r.x() + r.width();
661             qreal y2 = r.y() + r.height();
662 
663             pts[pos++] = x1;
664             pts[pos++] = y1;
665 
666             pts[pos++] = x2;
667             pts[pos++] = y1;
668 
669             pts[pos++] = x2;
670             pts[pos++] = y2;
671 
672             pts[pos++] = x1;
673             pts[pos++] = y2;
674         }
675         QVectorPath vp(pts, rectsInRegion * 4, qpaintengineex_rect4_types_32);
676         clip(vp, op);
677     } else {
678         QVarLengthArray<qreal> pts(rectsInRegion * 2 * 4);
679         QVarLengthArray<QPainterPath::ElementType> types(rectsInRegion * 4);
680         int ppos = 0;
681         int tpos = 0;
682 
683         for (QRect r : region) {
684             qreal x1 = r.x();
685             qreal y1 = r.y();
686             qreal x2 = r.x() + r.width();
687             qreal y2 = r.y() + r.height();
688 
689             pts[ppos++] = x1;
690             pts[ppos++] = y1;
691 
692             pts[ppos++] = x2;
693             pts[ppos++] = y1;
694 
695             pts[ppos++] = x2;
696             pts[ppos++] = y2;
697 
698             pts[ppos++] = x1;
699             pts[ppos++] = y2;
700 
701             types[tpos++] = QPainterPath::MoveToElement;
702             types[tpos++] = QPainterPath::LineToElement;
703             types[tpos++] = QPainterPath::LineToElement;
704             types[tpos++] = QPainterPath::LineToElement;
705         }
706 
707         QVectorPath vp(pts.data(), rectsInRegion * 4, types.data());
708         clip(vp, op);
709     }
710 
711 }
712 
clip(const QPainterPath & path,Qt::ClipOperation op)713 void QPaintEngineEx::clip(const QPainterPath &path, Qt::ClipOperation op)
714 {
715     if (path.isEmpty()) {
716         QVectorPath vp(nullptr, 0);
717         clip(vp, op);
718     } else {
719         clip(qtVectorPathForPath(path), op);
720     }
721 }
722 
fillRect(const QRectF & r,const QBrush & brush)723 void QPaintEngineEx::fillRect(const QRectF &r, const QBrush &brush)
724 {
725     qreal pts[] = { r.x(), r.y(), r.x() + r.width(), r.y(),
726                     r.x() + r.width(), r.y() + r.height(), r.x(), r.y() + r.height() };
727     QVectorPath vp(pts, 4, nullptr, QVectorPath::RectangleHint);
728     fill(vp, brush);
729 }
730 
fillRect(const QRectF & r,const QColor & color)731 void QPaintEngineEx::fillRect(const QRectF &r, const QColor &color)
732 {
733     fillRect(r, QBrush(color));
734 }
735 
drawRects(const QRect * rects,int rectCount)736 void QPaintEngineEx::drawRects(const QRect *rects, int rectCount)
737 {
738     for (int i=0; i<rectCount; ++i) {
739         const QRect &r = rects[i];
740         // ### Is there a one off here?
741         qreal right = r.x() + r.width();
742         qreal bottom = r.y() + r.height();
743         qreal pts[] = { qreal(r.x()), qreal(r.y()),
744                         right, qreal(r.y()),
745                         right, bottom,
746                         qreal(r.x()), bottom,
747                         qreal(r.x()), qreal(r.y()) };
748         QVectorPath vp(pts, 5, nullptr, QVectorPath::RectangleHint);
749         draw(vp);
750     }
751 }
752 
drawRects(const QRectF * rects,int rectCount)753 void QPaintEngineEx::drawRects(const QRectF *rects, int rectCount)
754 {
755     for (int i=0; i<rectCount; ++i) {
756         const QRectF &r = rects[i];
757         qreal right = r.x() + r.width();
758         qreal bottom = r.y() + r.height();
759         qreal pts[] = { r.x(), r.y(),
760                         right, r.y(),
761                         right, bottom,
762                         r.x(), bottom,
763                         r.x(), r.y() };
764         QVectorPath vp(pts, 5, nullptr, QVectorPath::RectangleHint);
765         draw(vp);
766     }
767 }
768 
769 
drawRoundedRect(const QRectF & rect,qreal xRadius,qreal yRadius,Qt::SizeMode mode)770 void QPaintEngineEx::drawRoundedRect(const QRectF &rect, qreal xRadius, qreal yRadius,
771                                      Qt::SizeMode mode)
772 {
773     qreal x1 = rect.left();
774     qreal x2 = rect.right();
775     qreal y1 = rect.top();
776     qreal y2 = rect.bottom();
777 
778     if (mode == Qt::RelativeSize) {
779         xRadius = xRadius * rect.width() / 200.;
780         yRadius = yRadius * rect.height() / 200.;
781     }
782 
783     xRadius = qMin(xRadius, rect.width() / 2);
784     yRadius = qMin(yRadius, rect.height() / 2);
785 
786     qreal pts[] = {
787         x1 + xRadius, y1,                   // MoveTo
788         x2 - xRadius, y1,                   // LineTo
789         x2 - (1 - KAPPA) * xRadius, y1,     // CurveTo
790         x2, y1 + (1 - KAPPA) * yRadius,
791         x2, y1 + yRadius,
792         x2, y2 - yRadius,                   // LineTo
793         x2, y2 - (1 - KAPPA) * yRadius,     // CurveTo
794         x2 - (1 - KAPPA) * xRadius, y2,
795         x2 - xRadius, y2,
796         x1 + xRadius, y2,                   // LineTo
797         x1 + (1 - KAPPA) * xRadius, y2,           // CurveTo
798         x1, y2 - (1 - KAPPA) * yRadius,
799         x1, y2 - yRadius,
800         x1, y1 + yRadius,                   // LineTo
801         x1, y1 + (1 - KAPPA) * yRadius,           // CurveTo
802         x1 + (1 - KAPPA) * xRadius, y1,
803         x1 + xRadius, y1
804     };
805 
806     QVectorPath path(pts, 17, qpaintengineex_roundedrect_types, QVectorPath::RoundedRectHint);
807     draw(path);
808 }
809 
810 
811 
drawLines(const QLine * lines,int lineCount)812 void QPaintEngineEx::drawLines(const QLine *lines, int lineCount)
813 {
814     int elementCount = lineCount << 1;
815     while (elementCount > 0) {
816         int count = qMin(elementCount, 32);
817 
818         qreal pts[64];
819         int count2 = count<<1;
820         for (int i=0; i<count2; ++i)
821             pts[i] = ((const int *) lines)[i];
822 
823         QVectorPath path(pts, count, qpaintengineex_line_types_16, QVectorPath::LinesHint);
824         stroke(path, state()->pen);
825 
826         elementCount -= 32;
827         lines += 16;
828     }
829 }
830 
drawLines(const QLineF * lines,int lineCount)831 void QPaintEngineEx::drawLines(const QLineF *lines, int lineCount)
832 {
833     int elementCount = lineCount << 1;
834     while (elementCount > 0) {
835         int count = qMin(elementCount, 32);
836 
837         QVectorPath path((const qreal *) lines, count, qpaintengineex_line_types_16,
838                          QVectorPath::LinesHint);
839         stroke(path, state()->pen);
840 
841         elementCount -= 32;
842         lines += 16;
843     }
844 }
845 
drawEllipse(const QRectF & r)846 void QPaintEngineEx::drawEllipse(const QRectF &r)
847 {
848     qreal pts[26]; // QPointF[13] without constructors...
849     union {
850         qreal *ptr;
851         QPointF *points;
852     } x;
853     x.ptr = pts;
854 
855     int point_count = 0;
856     x.points[0] = qt_curves_for_arc(r, 0, -360, x.points + 1, &point_count);
857     if (point_count == 0)
858         return;
859     QVectorPath vp((qreal *) pts, point_count + 1, qpaintengineex_ellipse_types, QVectorPath::EllipseHint);
860     draw(vp);
861 }
862 
drawEllipse(const QRect & r)863 void QPaintEngineEx::drawEllipse(const QRect &r)
864 {
865     drawEllipse(QRectF(r));
866 }
867 
drawPath(const QPainterPath & path)868 void QPaintEngineEx::drawPath(const QPainterPath &path)
869 {
870     if (!path.isEmpty())
871         draw(qtVectorPathForPath(path));
872 }
873 
874 
drawPoints(const QPointF * points,int pointCount)875 void QPaintEngineEx::drawPoints(const QPointF *points, int pointCount)
876 {
877     QPen pen = state()->pen;
878     if (pen.capStyle() == Qt::FlatCap)
879         pen.setCapStyle(Qt::SquareCap);
880 
881     if (pen.brush().isOpaque()) {
882         while (pointCount > 0) {
883             int count = qMin(pointCount, 16);
884             qreal pts[64];
885             int oset = -1;
886             for (int i=0; i<count; ++i) {
887                 pts[++oset] = points[i].x();
888                 pts[++oset] = points[i].y();
889                 pts[++oset] = points[i].x() + 1/63.;
890                 pts[++oset] = points[i].y();
891             }
892             QVectorPath path(pts, count * 2, qpaintengineex_line_types_16, QVectorPath::LinesHint);
893             stroke(path, pen);
894             pointCount -= 16;
895             points += 16;
896         }
897     } else {
898         for (int i=0; i<pointCount; ++i) {
899             qreal pts[] = { points[i].x(), points[i].y(), points[i].x() + qreal(1/63.), points[i].y() };
900             QVectorPath path(pts, 2, nullptr);
901             stroke(path, pen);
902         }
903     }
904 }
905 
drawPoints(const QPoint * points,int pointCount)906 void QPaintEngineEx::drawPoints(const QPoint *points, int pointCount)
907 {
908     QPen pen = state()->pen;
909     if (pen.capStyle() == Qt::FlatCap)
910         pen.setCapStyle(Qt::SquareCap);
911 
912     if (pen.brush().isOpaque()) {
913         while (pointCount > 0) {
914             int count = qMin(pointCount, 16);
915             qreal pts[64];
916             int oset = -1;
917             for (int i=0; i<count; ++i) {
918                 pts[++oset] = points[i].x();
919                 pts[++oset] = points[i].y();
920                 pts[++oset] = points[i].x() + 1/63.;
921                 pts[++oset] = points[i].y();
922             }
923             QVectorPath path(pts, count * 2, qpaintengineex_line_types_16, QVectorPath::LinesHint);
924             stroke(path, pen);
925             pointCount -= 16;
926             points += 16;
927         }
928     } else {
929         for (int i=0; i<pointCount; ++i) {
930             qreal pts[] = { qreal(points[i].x()), qreal(points[i].y()),
931                             qreal(points[i].x() +1/63.), qreal(points[i].y()) };
932             QVectorPath path(pts, 2, nullptr);
933             stroke(path, pen);
934         }
935     }
936 }
937 
938 
drawPolygon(const QPointF * points,int pointCount,PolygonDrawMode mode)939 void QPaintEngineEx::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
940 {
941     QVectorPath path((const qreal *) points, pointCount, nullptr, QVectorPath::polygonFlags(mode));
942 
943     if (mode == PolylineMode)
944         stroke(path, state()->pen);
945     else
946         draw(path);
947 }
948 
drawPolygon(const QPoint * points,int pointCount,PolygonDrawMode mode)949 void QPaintEngineEx::drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode)
950 {
951     int count = pointCount<<1;
952     QVarLengthArray<qreal> pts(count);
953 
954     for (int i=0; i<count; ++i)
955         pts[i] = ((const int *) points)[i];
956 
957     QVectorPath path(pts.data(), pointCount, nullptr, QVectorPath::polygonFlags(mode));
958 
959     if (mode == PolylineMode)
960         stroke(path, state()->pen);
961     else
962         draw(path);
963 
964 }
965 
drawPixmap(const QPointF & pos,const QPixmap & pm)966 void QPaintEngineEx::drawPixmap(const QPointF &pos, const QPixmap &pm)
967 {
968     drawPixmap(QRectF(pos, pm.size() / pm.devicePixelRatio()), pm, pm.rect());
969 }
970 
drawImage(const QPointF & pos,const QImage & image)971 void QPaintEngineEx::drawImage(const QPointF &pos, const QImage &image)
972 {
973     drawImage(QRectF(pos, image.size() / image.devicePixelRatio()), image, image.rect());
974 }
975 
drawTiledPixmap(const QRectF & r,const QPixmap & pixmap,const QPointF & s)976 void QPaintEngineEx::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &s)
977 {
978     QBrush brush(state()->pen.color(), pixmap);
979     QTransform xform = QTransform::fromTranslate(r.x() - s.x(), r.y() - s.y());
980     if (!qFuzzyCompare(pixmap.devicePixelRatioF(), 1.0))
981         xform.scale(1.0/pixmap.devicePixelRatioF(), 1.0/pixmap.devicePixelRatioF());
982     brush.setTransform(xform);
983 
984     qreal pts[] = { r.x(), r.y(),
985                     r.x() + r.width(), r.y(),
986                     r.x() + r.width(), r.y() + r.height(),
987                     r.x(), r.y() + r.height() };
988 
989     QVectorPath path(pts, 4, nullptr, QVectorPath::RectangleHint);
990     fill(path, brush);
991 }
992 
drawPixmapFragments(const QPainter::PixmapFragment * fragments,int fragmentCount,const QPixmap & pixmap,QPainter::PixmapFragmentHints)993 void QPaintEngineEx::drawPixmapFragments(const QPainter::PixmapFragment *fragments, int fragmentCount,
994                                          const QPixmap &pixmap, QPainter::PixmapFragmentHints /*hints*/)
995 {
996     if (pixmap.isNull())
997         return;
998 
999     qreal oldOpacity = state()->opacity;
1000     QTransform oldTransform = state()->matrix;
1001 
1002     for (int i = 0; i < fragmentCount; ++i) {
1003         QTransform transform = oldTransform;
1004         transform.translate(fragments[i].x, fragments[i].y);
1005         transform.rotate(fragments[i].rotation);
1006         state()->opacity = oldOpacity * fragments[i].opacity;
1007         state()->matrix = transform;
1008         opacityChanged();
1009         transformChanged();
1010 
1011         qreal w = fragments[i].scaleX * fragments[i].width;
1012         qreal h = fragments[i].scaleY * fragments[i].height;
1013         QRectF sourceRect(fragments[i].sourceLeft, fragments[i].sourceTop,
1014                           fragments[i].width, fragments[i].height);
1015         drawPixmap(QRectF(-0.5 * w, -0.5 * h, w, h), pixmap, sourceRect);
1016     }
1017 
1018     state()->opacity = oldOpacity;
1019     state()->matrix = oldTransform;
1020     opacityChanged();
1021     transformChanged();
1022 }
1023 
setState(QPainterState * s)1024 void QPaintEngineEx::setState(QPainterState *s)
1025 {
1026     QPaintEngine::state = s;
1027 }
1028 
1029 
updateState(const QPaintEngineState &)1030 void QPaintEngineEx::updateState(const QPaintEngineState &)
1031 {
1032     // do nothing...
1033 }
1034 
qt_painterPathFromVectorPath(const QVectorPath & path)1035 Q_GUI_EXPORT QPainterPath qt_painterPathFromVectorPath(const QVectorPath &path)
1036 {
1037     const qreal *points = path.points();
1038     const QPainterPath::ElementType *types = path.elements();
1039 
1040     QPainterPath p;
1041     if (types) {
1042         int id = 0;
1043         for (int i=0; i<path.elementCount(); ++i) {
1044             switch(types[i]) {
1045             case QPainterPath::MoveToElement:
1046                 p.moveTo(QPointF(points[id], points[id+1]));
1047                 id+=2;
1048                 break;
1049             case QPainterPath::LineToElement:
1050                 p.lineTo(QPointF(points[id], points[id+1]));
1051                 id+=2;
1052                 break;
1053             case QPainterPath::CurveToElement: {
1054                 QPointF p1(points[id], points[id+1]);
1055                 QPointF p2(points[id+2], points[id+3]);
1056                 QPointF p3(points[id+4], points[id+5]);
1057                 p.cubicTo(p1, p2, p3);
1058                 id+=6;
1059                 break;
1060             }
1061             case QPainterPath::CurveToDataElement:
1062                 ;
1063                 break;
1064             }
1065         }
1066     } else {
1067         p.moveTo(QPointF(points[0], points[1]));
1068         int id = 2;
1069         for (int i=1; i<path.elementCount(); ++i) {
1070             p.lineTo(QPointF(points[id], points[id+1]));
1071             id+=2;
1072         }
1073     }
1074     if (path.hints() & QVectorPath::WindingFill)
1075         p.setFillRule(Qt::WindingFill);
1076 
1077     return p;
1078 }
1079 
drawStaticTextItem(QStaticTextItem * staticTextItem)1080 void QPaintEngineEx::drawStaticTextItem(QStaticTextItem *staticTextItem)
1081 {
1082     QPainterPath path;
1083     path.setFillRule(Qt::WindingFill);
1084 
1085     if (staticTextItem->numGlyphs == 0)
1086         return;
1087 
1088     QFontEngine *fontEngine = staticTextItem->fontEngine();
1089     fontEngine->addGlyphsToPath(staticTextItem->glyphs, staticTextItem->glyphPositions,
1090                                 staticTextItem->numGlyphs, &path, { });
1091     if (!path.isEmpty()) {
1092         QPainterState *s = state();
1093         QPainter::RenderHints oldHints = s->renderHints;
1094         bool changedHints = false;
1095         if (bool(oldHints & QPainter::TextAntialiasing)
1096             && !bool(fontEngine->fontDef.styleStrategy & QFont::NoAntialias)
1097             && !bool(oldHints & QPainter::Antialiasing)) {
1098             s->renderHints |= QPainter::Antialiasing;
1099             renderHintsChanged();
1100             changedHints = true;
1101         }
1102 
1103         fill(qtVectorPathForPath(path), s->pen.brush());
1104 
1105         if (changedHints) {
1106             s->renderHints = oldHints;
1107             renderHintsChanged();
1108         }
1109     }
1110 }
1111 
requiresPretransformedGlyphPositions(QFontEngine *,const QTransform &) const1112 bool QPaintEngineEx::requiresPretransformedGlyphPositions(QFontEngine *, const QTransform &) const
1113 {
1114     return false;
1115 }
1116 
shouldDrawCachedGlyphs(QFontEngine * fontEngine,const QTransform & m) const1117 bool QPaintEngineEx::shouldDrawCachedGlyphs(QFontEngine *fontEngine, const QTransform &m) const
1118 {
1119     if (fontEngine->glyphFormat == QFontEngine::Format_ARGB)
1120         return true;
1121 
1122     static const int maxCachedGlyphSizeSquared = std::pow([]{
1123         if (int env = qEnvironmentVariableIntValue("QT_MAX_CACHED_GLYPH_SIZE"))
1124             return env;
1125         return QT_MAX_CACHED_GLYPH_SIZE;
1126     }(), 2);
1127 
1128     qreal pixelSize = fontEngine->fontDef.pixelSize;
1129     return (pixelSize * pixelSize * qAbs(m.determinant())) <= maxCachedGlyphSizeSquared;
1130 }
1131 
1132 QT_END_NAMESPACE
1133