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

source code of qtbase/src/gui/painting/qpaintengineex.cpp