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// QtCore
5#include <memory>
6#include <qdebug.h>
7#include <qmath.h>
8#include <qmutex.h>
9
10// QtGui
11#include "qbitmap.h"
12#include "qimage.h"
13#include "qpaintdevice.h"
14#include "qpaintengine.h"
15#include "qpainter.h"
16#include "qpainter_p.h"
17#include "qpainterpath.h"
18#include "qpicture.h"
19#include "qpixmapcache.h"
20#include "qpolygon.h"
21#include "qtextlayout.h"
22#include "qthread.h"
23#include "qvarlengtharray.h"
24#include "qstatictext.h"
25#include "qglyphrun.h"
26
27#include <qpa/qplatformtheme.h>
28#include <qpa/qplatformintegration.h>
29
30#include <private/qfontengine_p.h>
31#include <private/qpaintengine_p.h>
32#include <private/qemulationpaintengine_p.h>
33#include <private/qpainterpath_p.h>
34#include <private/qtextengine_p.h>
35#include <private/qpaintengine_raster_p.h>
36#include <private/qmath_p.h>
37#include <private/qstatictext_p.h>
38#include <private/qglyphrun_p.h>
39#include <private/qhexstring_p.h>
40#include <private/qguiapplication_p.h>
41#include <private/qrawfont_p.h>
42#include <private/qfont_p.h>
43
44QT_BEGIN_NAMESPACE
45
46using namespace Qt::StringLiterals;
47
48// We changed the type from QScopedPointer to unique_ptr, make sure it's binary compatible:
49static_assert(sizeof(QScopedPointer<QPainterPrivate>) == sizeof(std::unique_ptr<QPainterPrivate>));
50
51#define QGradient_StretchToDevice 0x10000000
52#define QPaintEngine_OpaqueBackground 0x40000000
53
54// #define QT_DEBUG_DRAW
55#ifdef QT_DEBUG_DRAW
56bool qt_show_painter_debug_output = true;
57#endif
58
59extern QPixmap qt_pixmapForBrush(int style, bool invert);
60
61void qt_format_text(const QFont &font,
62 const QRectF &_r, int tf, const QTextOption *option, const QString& str, QRectF *brect,
63 int tabstops, int* tabarray, int tabarraylen,
64 QPainter *painter);
65static void drawTextItemDecoration(QPainter *painter, const QPointF &pos, const QFontEngine *fe, QTextEngine *textEngine,
66 QTextCharFormat::UnderlineStyle underlineStyle,
67 QTextItem::RenderFlags flags, qreal width,
68 const QTextCharFormat &charFormat);
69// Helper function to calculate left most position, width and flags for decoration drawing
70static void qt_draw_decoration_for_glyphs(QPainter *painter,
71 const QPointF &decorationPosition,
72 const glyph_t *glyphArray,
73 const QFixedPoint *positions,
74 int glyphCount,
75 QFontEngine *fontEngine,
76 bool underline,
77 bool overline,
78 bool strikeOut);
79
80static inline QGradient::CoordinateMode coordinateMode(const QBrush &brush)
81{
82 switch (brush.style()) {
83 case Qt::LinearGradientPattern:
84 case Qt::RadialGradientPattern:
85 case Qt::ConicalGradientPattern:
86 return brush.gradient()->coordinateMode();
87 default:
88 ;
89 }
90 return QGradient::LogicalMode;
91}
92
93extern bool qHasPixmapTexture(const QBrush &);
94
95static inline bool is_brush_transparent(const QBrush &brush) {
96 Qt::BrushStyle s = brush.style();
97 if (s != Qt::TexturePattern)
98 return s >= Qt::Dense1Pattern && s <= Qt::DiagCrossPattern;
99 if (qHasPixmapTexture(brush))
100 return brush.texture().isQBitmap() || brush.texture().hasAlphaChannel();
101 else {
102 const QImage texture = brush.textureImage();
103 return texture.hasAlphaChannel() || (texture.depth() == 1 && texture.colorCount() == 0);
104 }
105}
106
107static inline bool is_pen_transparent(const QPen &pen) {
108 return pen.style() > Qt::SolidLine || is_brush_transparent(brush: pen.brush());
109}
110
111/* Discards the emulation flags that are not relevant for line drawing
112 and returns the result
113*/
114static inline uint line_emulation(uint emulation)
115{
116 return emulation & (QPaintEngine::PrimitiveTransform
117 | QPaintEngine::AlphaBlend
118 | QPaintEngine::Antialiasing
119 | QPaintEngine::BrushStroke
120 | QPaintEngine::ConstantOpacity
121 | QGradient_StretchToDevice
122 | QPaintEngine::ObjectBoundingModeGradients
123 | QPaintEngine_OpaqueBackground);
124}
125
126#ifndef QT_NO_DEBUG
127static bool qt_painter_thread_test(int devType, int engineType, const char *what)
128{
129 const QPlatformIntegration *platformIntegration = QGuiApplicationPrivate::platformIntegration();
130 switch (devType) {
131 case QInternal::Image:
132 case QInternal::Printer:
133 case QInternal::Picture:
134 // can be drawn onto these devices safely from any thread
135 break;
136 default:
137 if (QThread::currentThread() != qApp->thread()
138 // pixmaps cannot be targets unless threaded pixmaps are supported
139 && (devType != QInternal::Pixmap || !platformIntegration->hasCapability(cap: QPlatformIntegration::ThreadedPixmaps))
140 // framebuffer objects and such cannot be targets unless threaded GL is supported
141 && (devType != QInternal::OpenGL || !platformIntegration->hasCapability(cap: QPlatformIntegration::ThreadedOpenGL))
142 // widgets cannot be targets except for QGLWidget
143 && (devType != QInternal::Widget || !platformIntegration->hasCapability(cap: QPlatformIntegration::ThreadedOpenGL)
144 || (engineType != QPaintEngine::OpenGL && engineType != QPaintEngine::OpenGL2))) {
145 qWarning(msg: "QPainter: It is not safe to use %s outside the GUI thread", what);
146 return false;
147 }
148 break;
149 }
150 return true;
151}
152#endif
153
154static bool needsEmulation(const QBrush &brush)
155{
156 bool res = false;
157
158 const QGradient *bg = brush.gradient();
159 if (bg) {
160 res = (bg->coordinateMode() > QGradient::LogicalMode);
161 } else if (brush.style() == Qt::TexturePattern) {
162 if (qHasPixmapTexture(brush))
163 res = !qFuzzyCompare(p1: brush.texture().devicePixelRatio(), p2: qreal(1.0));
164 else
165 res = !qFuzzyCompare(p1: brush.textureImage().devicePixelRatio(), p2: qreal(1.0));
166 }
167
168 return res;
169}
170
171void QPainterPrivate::checkEmulation()
172{
173 Q_ASSERT(extended);
174 bool doEmulation = false;
175 if (state->bgMode == Qt::OpaqueMode)
176 doEmulation = true;
177
178 if (needsEmulation(brush: state->brush))
179 doEmulation = true;
180
181 if (needsEmulation(brush: qpen_brush(p: state->pen)))
182 doEmulation = true;
183
184 if (doEmulation && extended->flags() & QPaintEngineEx::DoNotEmulate)
185 return;
186
187 if (doEmulation) {
188 if (extended != emulationEngine.get()) {
189 if (!emulationEngine)
190 emulationEngine = std::make_unique<QEmulationPaintEngine>(args&: extended);
191 extended = emulationEngine.get();
192 extended->setState(state.get());
193 }
194 } else if (emulationEngine.get() == extended) {
195 extended = emulationEngine->real_engine;
196 }
197}
198
199QPainterPrivate::QPainterPrivate(QPainter *painter)
200 : q_ptr(painter), txinv(0), inDestructor(false)
201{
202}
203
204QPainterPrivate::~QPainterPrivate()
205 = default;
206
207QTransform QPainterPrivate::viewTransform() const
208{
209 if (state->VxF) {
210 qreal scaleW = qreal(state->vw)/qreal(state->ww);
211 qreal scaleH = qreal(state->vh)/qreal(state->wh);
212 return QTransform(scaleW, 0, 0, scaleH,
213 state->vx - state->wx*scaleW, state->vy - state->wy*scaleH);
214 }
215 return QTransform();
216}
217
218qreal QPainterPrivate::effectiveDevicePixelRatio() const
219{
220 // Special cases for devices that does not support PdmDevicePixelRatio go here:
221 if (device->devType() == QInternal::Printer)
222 return qreal(1);
223
224 return device->devicePixelRatio();
225}
226
227QTransform QPainterPrivate::hidpiScaleTransform() const
228{
229 const qreal devicePixelRatio = effectiveDevicePixelRatio();
230 return QTransform::fromScale(dx: devicePixelRatio, dy: devicePixelRatio);
231}
232
233/*
234 \internal
235 Returns \c true if using a shared painter; otherwise false.
236*/
237bool QPainterPrivate::attachPainterPrivate(QPainter *q, QPaintDevice *pdev)
238{
239 Q_ASSERT(q);
240 Q_ASSERT(pdev);
241
242 QPainter *sp = pdev->sharedPainter();
243 if (!sp)
244 return false;
245
246 // Save the current state of the shared painter and assign
247 // the current d_ptr to the shared painter's d_ptr.
248 sp->save();
249 ++sp->d_ptr->refcount;
250 sp->d_ptr->d_ptrs.push_back(t: q->d_ptr.get());
251 Q_UNUSED(q->d_ptr.release());
252 q->d_ptr.reset(p: sp->d_ptr.get());
253
254 Q_ASSERT(q->d_ptr->state);
255
256 // Now initialize the painter with correct widget properties.
257 q->d_ptr->initFrom(device: pdev);
258 QPoint offset;
259 pdev->redirected(offset: &offset);
260 offset += q->d_ptr->engine->coordinateOffset();
261
262 // Update system rect.
263 q->d_ptr->state->ww = q->d_ptr->state->vw = pdev->width();
264 q->d_ptr->state->wh = q->d_ptr->state->vh = pdev->height();
265
266 // Update matrix.
267 if (q->d_ptr->state->WxF) {
268 q->d_ptr->state->redirectionMatrix = q->d_ptr->state->matrix;
269 q->d_ptr->state->redirectionMatrix *= q->d_ptr->hidpiScaleTransform().inverted();
270 q->d_ptr->state->redirectionMatrix.translate(dx: -offset.x(), dy: -offset.y());
271 q->d_ptr->state->worldMatrix = QTransform();
272 q->d_ptr->state->WxF = false;
273 } else {
274 q->d_ptr->state->redirectionMatrix = QTransform::fromTranslate(dx: -offset.x(), dy: -offset.y());
275 }
276 q->d_ptr->updateMatrix();
277
278 QPaintEnginePrivate *enginePrivate = q->d_ptr->engine->d_func();
279 if (enginePrivate->currentClipDevice == pdev) {
280 enginePrivate->systemStateChanged();
281 return true;
282 }
283
284 // Update system transform and clip.
285 enginePrivate->currentClipDevice = pdev;
286 enginePrivate->setSystemTransform(q->d_ptr->state->matrix);
287 return true;
288}
289
290void QPainterPrivate::detachPainterPrivate(QPainter *q)
291{
292 Q_ASSERT(refcount > 1);
293 Q_ASSERT(q);
294
295 --refcount;
296 QPainterPrivate *original = d_ptrs.back();
297 d_ptrs.pop_back();
298 if (inDestructor) {
299 inDestructor = false;
300 if (original)
301 original->inDestructor = true;
302 } else if (!original) {
303 original = new QPainterPrivate(q);
304 }
305
306 q->restore();
307 Q_UNUSED(q->d_ptr.release());
308 q->d_ptr.reset(p: original);
309
310 if (emulationEngine) {
311 extended = emulationEngine->real_engine;
312 emulationEngine = nullptr;
313 }
314}
315
316
317void QPainterPrivate::draw_helper(const QPainterPath &originalPath, DrawOperation op)
318{
319#ifdef QT_DEBUG_DRAW
320 if (qt_show_painter_debug_output) {
321 printf("QPainter::drawHelper\n");
322 }
323#endif
324
325 if (originalPath.isEmpty())
326 return;
327
328 QPaintEngine::PaintEngineFeatures gradientStretch =
329 QPaintEngine::PaintEngineFeatures(QGradient_StretchToDevice
330 | QPaintEngine::ObjectBoundingModeGradients);
331
332 const bool mustEmulateObjectBoundingModeGradients = extended
333 || ((state->emulationSpecifier & QPaintEngine::ObjectBoundingModeGradients)
334 && !engine->hasFeature(feature: QPaintEngine::PatternTransform));
335
336 if (!(state->emulationSpecifier & ~gradientStretch)
337 && !mustEmulateObjectBoundingModeGradients) {
338 drawStretchedGradient(path: originalPath, operation: op);
339 return;
340 } else if (state->emulationSpecifier & QPaintEngine_OpaqueBackground) {
341 drawOpaqueBackground(path: originalPath, operation: op);
342 return;
343 }
344
345 Q_Q(QPainter);
346
347 qreal strokeOffsetX = 0, strokeOffsetY = 0;
348
349 QPainterPath path = originalPath * state->matrix;
350 QRectF pathBounds = path.boundingRect();
351 QRectF strokeBounds;
352 bool doStroke = (op & StrokeDraw) && (state->pen.style() != Qt::NoPen);
353 if (doStroke) {
354 qreal penWidth = state->pen.widthF();
355 if (penWidth == 0) {
356 strokeOffsetX = 1;
357 strokeOffsetY = 1;
358 } else {
359 // In case of complex xform
360 if (state->matrix.type() > QTransform::TxScale) {
361 QPainterPathStroker stroker;
362 stroker.setWidth(penWidth);
363 stroker.setJoinStyle(state->pen.joinStyle());
364 stroker.setCapStyle(state->pen.capStyle());
365 QPainterPath stroke = stroker.createStroke(path: originalPath);
366 strokeBounds = (stroke * state->matrix).boundingRect();
367 } else {
368 strokeOffsetX = qAbs(t: penWidth * state->matrix.m11() / 2.0);
369 strokeOffsetY = qAbs(t: penWidth * state->matrix.m22() / 2.0);
370 }
371 }
372 }
373
374 QRect absPathRect;
375 if (!strokeBounds.isEmpty()) {
376 absPathRect = strokeBounds.intersected(r: QRectF(0, 0, device->width(), device->height())).toAlignedRect();
377 } else {
378 absPathRect = pathBounds.adjusted(xp1: -strokeOffsetX, yp1: -strokeOffsetY, xp2: strokeOffsetX, yp2: strokeOffsetY)
379 .intersected(r: QRectF(0, 0, device->width(), device->height())).toAlignedRect();
380 }
381
382 if (q->hasClipping()) {
383 bool hasPerspectiveTransform = false;
384 for (const QPainterClipInfo &info : std::as_const(t&: state->clipInfo)) {
385 if (info.matrix.type() == QTransform::TxProject) {
386 hasPerspectiveTransform = true;
387 break;
388 }
389 }
390 // avoid mapping QRegions with perspective transforms
391 if (!hasPerspectiveTransform) {
392 // The trick with txinv and invMatrix is done in order to
393 // avoid transforming the clip to logical coordinates, and
394 // then back to device coordinates. This is a problem with
395 // QRegion/QRect based clips, since they use integer
396 // coordinates and converting to/from logical coordinates will
397 // lose precision.
398 bool old_txinv = txinv;
399 QTransform old_invMatrix = invMatrix;
400 txinv = true;
401 invMatrix = QTransform();
402 QPainterPath clipPath = q->clipPath();
403 QRectF r = clipPath.boundingRect().intersected(r: absPathRect);
404 absPathRect = r.toAlignedRect();
405 txinv = old_txinv;
406 invMatrix = old_invMatrix;
407 }
408 }
409
410// qDebug("\nQPainterPrivate::draw_helper(), x=%d, y=%d, w=%d, h=%d",
411// devMinX, devMinY, device->width(), device->height());
412// qDebug() << " - matrix" << state->matrix;
413// qDebug() << " - originalPath.bounds" << originalPath.boundingRect();
414// qDebug() << " - path.bounds" << path.boundingRect();
415
416 if (absPathRect.width() <= 0 || absPathRect.height() <= 0)
417 return;
418
419 QImage image(absPathRect.width(), absPathRect.height(), QImage::Format_ARGB32_Premultiplied);
420 image.fill(pixel: 0);
421
422 QPainter p(&image);
423
424 p.d_ptr->helper_device = helper_device;
425
426 p.setOpacity(state->opacity);
427 p.translate(dx: -absPathRect.x(), dy: -absPathRect.y());
428 p.setTransform(transform: state->matrix, combine: true);
429 p.setPen(doStroke ? state->pen : QPen(Qt::NoPen));
430 p.setBrush((op & FillDraw) ? state->brush : QBrush(Qt::NoBrush));
431 p.setBackground(state->bgBrush);
432 p.setBackgroundMode(state->bgMode);
433 p.setBrushOrigin(state->brushOrigin);
434
435 p.setRenderHint(hint: QPainter::Antialiasing, on: state->renderHints & QPainter::Antialiasing);
436 p.setRenderHint(hint: QPainter::SmoothPixmapTransform,
437 on: state->renderHints & QPainter::SmoothPixmapTransform);
438
439 p.drawPath(path: originalPath);
440
441#ifndef QT_NO_DEBUG
442 static bool do_fallback_overlay = !qEnvironmentVariableIsEmpty(varName: "QT_PAINT_FALLBACK_OVERLAY");
443 if (do_fallback_overlay) {
444 QImage block(8, 8, QImage::Format_ARGB32_Premultiplied);
445 QPainter pt(&block);
446 pt.fillRect(x: 0, y: 0, w: 8, h: 8, b: QColor(196, 0, 196));
447 pt.drawLine(x1: 0, y1: 0, x2: 8, y2: 8);
448 pt.end();
449 p.resetTransform();
450 p.setCompositionMode(QPainter::CompositionMode_SourceAtop);
451 p.setOpacity(0.5);
452 p.fillRect(x: 0, y: 0, w: image.width(), h: image.height(), b: QBrush(block));
453 }
454#endif
455
456 p.end();
457
458 q->save();
459 state->matrix = QTransform();
460 if (extended) {
461 extended->transformChanged();
462 } else {
463 state->dirtyFlags |= QPaintEngine::DirtyTransform;
464 updateState(state);
465 }
466 engine->drawImage(r: absPathRect,
467 pm: image,
468 sr: QRectF(0, 0, absPathRect.width(), absPathRect.height()),
469 flags: Qt::OrderedDither | Qt::OrderedAlphaDither);
470 q->restore();
471}
472
473void QPainterPrivate::drawOpaqueBackground(const QPainterPath &path, DrawOperation op)
474{
475 Q_Q(QPainter);
476
477 q->setBackgroundMode(Qt::TransparentMode);
478
479 if (op & FillDraw && state->brush.style() != Qt::NoBrush) {
480 q->fillPath(path, brush: state->bgBrush.color());
481 q->fillPath(path, brush: state->brush);
482 }
483
484 if (op & StrokeDraw && state->pen.style() != Qt::NoPen) {
485 q->strokePath(path, pen: QPen(state->bgBrush.color(), state->pen.width()));
486 q->strokePath(path, pen: state->pen);
487 }
488
489 q->setBackgroundMode(Qt::OpaqueMode);
490}
491
492static inline QBrush stretchGradientToUserSpace(const QBrush &brush, const QRectF &boundingRect)
493{
494 Q_ASSERT(brush.style() >= Qt::LinearGradientPattern
495 && brush.style() <= Qt::ConicalGradientPattern);
496
497 QTransform gradientToUser(boundingRect.width(), 0, 0, boundingRect.height(),
498 boundingRect.x(), boundingRect.y());
499
500 QGradient g = *brush.gradient();
501 g.setCoordinateMode(QGradient::LogicalMode);
502
503 QBrush b(g);
504 if (brush.gradient()->coordinateMode() == QGradient::ObjectMode)
505 b.setTransform(b.transform() * gradientToUser);
506 else
507 b.setTransform(gradientToUser * b.transform());
508 return b;
509}
510
511void QPainterPrivate::drawStretchedGradient(const QPainterPath &path, DrawOperation op)
512{
513 Q_Q(QPainter);
514
515 const qreal sw = helper_device->width();
516 const qreal sh = helper_device->height();
517
518 bool changedPen = false;
519 bool changedBrush = false;
520 bool needsFill = false;
521
522 const QPen pen = state->pen;
523 const QBrush brush = state->brush;
524
525 const QGradient::CoordinateMode penMode = coordinateMode(brush: pen.brush());
526 const QGradient::CoordinateMode brushMode = coordinateMode(brush);
527
528 QRectF boundingRect;
529
530 // Draw the xformed fill if the brush is a stretch gradient.
531 if ((op & FillDraw) && brush.style() != Qt::NoBrush) {
532 if (brushMode == QGradient::StretchToDeviceMode) {
533 q->setPen(Qt::NoPen);
534 changedPen = pen.style() != Qt::NoPen;
535 q->scale(sx: sw, sy: sh);
536 updateState(state);
537
538 const qreal isw = 1.0 / sw;
539 const qreal ish = 1.0 / sh;
540 QTransform inv(isw, 0, 0, ish, 0, 0);
541 engine->drawPath(path: path * inv);
542 q->scale(sx: isw, sy: ish);
543 } else {
544 needsFill = true;
545
546 if (brushMode == QGradient::ObjectBoundingMode || brushMode == QGradient::ObjectMode) {
547 Q_ASSERT(engine->hasFeature(QPaintEngine::PatternTransform));
548 boundingRect = path.boundingRect();
549 q->setBrush(stretchGradientToUserSpace(brush, boundingRect));
550 changedBrush = true;
551 }
552 }
553 }
554
555 if ((op & StrokeDraw) && pen.style() != Qt::NoPen) {
556 // Draw the xformed outline if the pen is a stretch gradient.
557 if (penMode == QGradient::StretchToDeviceMode) {
558 q->setPen(Qt::NoPen);
559 changedPen = true;
560
561 if (needsFill) {
562 updateState(state);
563 engine->drawPath(path);
564 }
565
566 q->scale(sx: sw, sy: sh);
567 q->setBrush(pen.brush());
568 changedBrush = true;
569 updateState(state);
570
571 QPainterPathStroker stroker;
572 stroker.setDashPattern(pen.style());
573 stroker.setWidth(pen.widthF());
574 stroker.setJoinStyle(pen.joinStyle());
575 stroker.setCapStyle(pen.capStyle());
576 stroker.setMiterLimit(pen.miterLimit());
577 QPainterPath stroke = stroker.createStroke(path);
578
579 const qreal isw = 1.0 / sw;
580 const qreal ish = 1.0 / sh;
581 QTransform inv(isw, 0, 0, ish, 0, 0);
582 engine->drawPath(path: stroke * inv);
583 q->scale(sx: isw, sy: ish);
584 } else {
585 if (!needsFill && brush.style() != Qt::NoBrush) {
586 q->setBrush(Qt::NoBrush);
587 changedBrush = true;
588 }
589
590 if (penMode == QGradient::ObjectBoundingMode || penMode == QGradient::ObjectMode) {
591 Q_ASSERT(engine->hasFeature(QPaintEngine::PatternTransform));
592
593 // avoid computing the bounding rect twice
594 if (!needsFill || (brushMode != QGradient::ObjectBoundingMode && brushMode != QGradient::ObjectMode))
595 boundingRect = path.boundingRect();
596
597 QPen p = pen;
598 p.setBrush(stretchGradientToUserSpace(brush: pen.brush(), boundingRect));
599 q->setPen(p);
600 changedPen = true;
601 } else if (changedPen) {
602 q->setPen(pen);
603 changedPen = false;
604 }
605
606 updateState(state);
607 engine->drawPath(path);
608 }
609 } else if (needsFill) {
610 if (pen.style() != Qt::NoPen) {
611 q->setPen(Qt::NoPen);
612 changedPen = true;
613 }
614
615 updateState(state);
616 engine->drawPath(path);
617 }
618
619 if (changedPen)
620 q->setPen(pen);
621 if (changedBrush)
622 q->setBrush(brush);
623}
624
625
626void QPainterPrivate::updateMatrix()
627{
628 state->matrix = state->WxF ? state->worldMatrix : QTransform();
629 if (state->VxF)
630 state->matrix *= viewTransform();
631
632 txinv = false; // no inverted matrix
633 state->matrix *= state->redirectionMatrix;
634 if (extended)
635 extended->transformChanged();
636 else
637 state->dirtyFlags |= QPaintEngine::DirtyTransform;
638
639 state->matrix *= hidpiScaleTransform();
640
641// printf("VxF=%d, WxF=%d\n", state->VxF, state->WxF);
642// qDebug() << " --- using matrix" << state->matrix << redirection_offset;
643}
644
645/*! \internal */
646void QPainterPrivate::updateInvMatrix()
647{
648 Q_ASSERT(txinv == false);
649 txinv = true; // creating inverted matrix
650 invMatrix = state->matrix.inverted();
651}
652
653extern bool qt_isExtendedRadialGradient(const QBrush &brush);
654
655void QPainterPrivate::updateEmulationSpecifier(QPainterState *s)
656{
657 bool alpha = false;
658 bool linearGradient = false;
659 bool radialGradient = false;
660 bool extendedRadialGradient = false;
661 bool conicalGradient = false;
662 bool patternBrush = false;
663 bool xform = false;
664 bool complexXform = false;
665
666 bool skip = true;
667
668 // Pen and brush properties (we have to check both if one changes because the
669 // one that's unchanged can still be in a state which requires emulation)
670 if (s->state() & (QPaintEngine::DirtyPen | QPaintEngine::DirtyBrush | QPaintEngine::DirtyHints)) {
671 // Check Brush stroke emulation
672 if (!s->pen.isSolid() && !engine->hasFeature(feature: QPaintEngine::BrushStroke))
673 s->emulationSpecifier |= QPaintEngine::BrushStroke;
674 else
675 s->emulationSpecifier &= ~QPaintEngine::BrushStroke;
676
677 skip = false;
678
679 QBrush penBrush = (qpen_style(p: s->pen) == Qt::NoPen) ? QBrush(Qt::NoBrush) : qpen_brush(p: s->pen);
680 Qt::BrushStyle brushStyle = qbrush_style(b: s->brush);
681 Qt::BrushStyle penBrushStyle = qbrush_style(b: penBrush);
682 alpha = (penBrushStyle != Qt::NoBrush
683 && (penBrushStyle < Qt::LinearGradientPattern && penBrush.color().alpha() != 255)
684 && !penBrush.isOpaque())
685 || (brushStyle != Qt::NoBrush
686 && (brushStyle < Qt::LinearGradientPattern && s->brush.color().alpha() != 255)
687 && !s->brush.isOpaque());
688 linearGradient = ((penBrushStyle == Qt::LinearGradientPattern) ||
689 (brushStyle == Qt::LinearGradientPattern));
690 radialGradient = ((penBrushStyle == Qt::RadialGradientPattern) ||
691 (brushStyle == Qt::RadialGradientPattern));
692 extendedRadialGradient = radialGradient && (qt_isExtendedRadialGradient(brush: penBrush) || qt_isExtendedRadialGradient(brush: s->brush));
693 conicalGradient = ((penBrushStyle == Qt::ConicalGradientPattern) ||
694 (brushStyle == Qt::ConicalGradientPattern));
695 patternBrush = (((penBrushStyle > Qt::SolidPattern
696 && penBrushStyle < Qt::LinearGradientPattern)
697 || penBrushStyle == Qt::TexturePattern) ||
698 ((brushStyle > Qt::SolidPattern
699 && brushStyle < Qt::LinearGradientPattern)
700 || brushStyle == Qt::TexturePattern));
701
702 bool penTextureAlpha = false;
703 if (penBrush.style() == Qt::TexturePattern)
704 penTextureAlpha = qHasPixmapTexture(penBrush)
705 ? (penBrush.texture().depth() > 1) && penBrush.texture().hasAlpha()
706 : penBrush.textureImage().hasAlphaChannel();
707 bool brushTextureAlpha = false;
708 if (s->brush.style() == Qt::TexturePattern) {
709 brushTextureAlpha = qHasPixmapTexture(s->brush)
710 ? (s->brush.texture().depth() > 1) && s->brush.texture().hasAlpha()
711 : s->brush.textureImage().hasAlphaChannel();
712 }
713 if (((penBrush.style() == Qt::TexturePattern && penTextureAlpha)
714 || (s->brush.style() == Qt::TexturePattern && brushTextureAlpha))
715 && !engine->hasFeature(feature: QPaintEngine::MaskedBrush))
716 s->emulationSpecifier |= QPaintEngine::MaskedBrush;
717 else
718 s->emulationSpecifier &= ~QPaintEngine::MaskedBrush;
719 }
720
721 if (s->state() & (QPaintEngine::DirtyHints
722 | QPaintEngine::DirtyOpacity
723 | QPaintEngine::DirtyBackgroundMode)) {
724 skip = false;
725 }
726
727 if (skip)
728 return;
729
730#if 0
731 qDebug("QPainterPrivate::updateEmulationSpecifier, state=%p\n"
732 " - alpha: %d\n"
733 " - linearGradient: %d\n"
734 " - radialGradient: %d\n"
735 " - conicalGradient: %d\n"
736 " - patternBrush: %d\n"
737 " - hints: %x\n"
738 " - xform: %d\n",
739 s,
740 alpha,
741 linearGradient,
742 radialGradient,
743 conicalGradient,
744 patternBrush,
745 uint(s->renderHints),
746 xform);
747#endif
748
749 // XForm properties
750 if (s->state() & QPaintEngine::DirtyTransform) {
751 xform = !s->matrix.isIdentity();
752 complexXform = !s->matrix.isAffine();
753 } else if (s->matrix.type() >= QTransform::TxTranslate) {
754 xform = true;
755 complexXform = !s->matrix.isAffine();
756 }
757
758 const bool brushXform = (s->brush.transform().type() != QTransform::TxNone);
759 const bool penXform = (s->pen.brush().transform().type() != QTransform::TxNone);
760
761 const bool patternXform = patternBrush && (xform || brushXform || penXform);
762
763 // Check alphablending
764 if (alpha && !engine->hasFeature(feature: QPaintEngine::AlphaBlend))
765 s->emulationSpecifier |= QPaintEngine::AlphaBlend;
766 else
767 s->emulationSpecifier &= ~QPaintEngine::AlphaBlend;
768
769 // Linear gradient emulation
770 if (linearGradient && !engine->hasFeature(feature: QPaintEngine::LinearGradientFill))
771 s->emulationSpecifier |= QPaintEngine::LinearGradientFill;
772 else
773 s->emulationSpecifier &= ~QPaintEngine::LinearGradientFill;
774
775 // Radial gradient emulation
776 if (extendedRadialGradient || (radialGradient && !engine->hasFeature(feature: QPaintEngine::RadialGradientFill)))
777 s->emulationSpecifier |= QPaintEngine::RadialGradientFill;
778 else
779 s->emulationSpecifier &= ~QPaintEngine::RadialGradientFill;
780
781 // Conical gradient emulation
782 if (conicalGradient && !engine->hasFeature(feature: QPaintEngine::ConicalGradientFill))
783 s->emulationSpecifier |= QPaintEngine::ConicalGradientFill;
784 else
785 s->emulationSpecifier &= ~QPaintEngine::ConicalGradientFill;
786
787 // Pattern brushes
788 if (patternBrush && !engine->hasFeature(feature: QPaintEngine::PatternBrush))
789 s->emulationSpecifier |= QPaintEngine::PatternBrush;
790 else
791 s->emulationSpecifier &= ~QPaintEngine::PatternBrush;
792
793 // Pattern XForms
794 if (patternXform && !engine->hasFeature(feature: QPaintEngine::PatternTransform))
795 s->emulationSpecifier |= QPaintEngine::PatternTransform;
796 else
797 s->emulationSpecifier &= ~QPaintEngine::PatternTransform;
798
799 // Primitive XForms
800 if (xform && !engine->hasFeature(feature: QPaintEngine::PrimitiveTransform))
801 s->emulationSpecifier |= QPaintEngine::PrimitiveTransform;
802 else
803 s->emulationSpecifier &= ~QPaintEngine::PrimitiveTransform;
804
805 // Perspective XForms
806 if (complexXform && !engine->hasFeature(feature: QPaintEngine::PerspectiveTransform))
807 s->emulationSpecifier |= QPaintEngine::PerspectiveTransform;
808 else
809 s->emulationSpecifier &= ~QPaintEngine::PerspectiveTransform;
810
811 // Constant opacity
812 if (state->opacity != 1 && !engine->hasFeature(feature: QPaintEngine::ConstantOpacity))
813 s->emulationSpecifier |= QPaintEngine::ConstantOpacity;
814 else
815 s->emulationSpecifier &= ~QPaintEngine::ConstantOpacity;
816
817 bool gradientStretch = false;
818 bool objectBoundingMode = false;
819 if (linearGradient || conicalGradient || radialGradient) {
820 QGradient::CoordinateMode brushMode = coordinateMode(brush: s->brush);
821 QGradient::CoordinateMode penMode = coordinateMode(brush: s->pen.brush());
822
823 gradientStretch |= (brushMode == QGradient::StretchToDeviceMode);
824 gradientStretch |= (penMode == QGradient::StretchToDeviceMode);
825
826 objectBoundingMode |= (brushMode == QGradient::ObjectBoundingMode || brushMode == QGradient::ObjectMode);
827 objectBoundingMode |= (penMode == QGradient::ObjectBoundingMode || penMode == QGradient::ObjectMode);
828 }
829 if (gradientStretch)
830 s->emulationSpecifier |= QGradient_StretchToDevice;
831 else
832 s->emulationSpecifier &= ~QGradient_StretchToDevice;
833
834 if (objectBoundingMode && !engine->hasFeature(feature: QPaintEngine::ObjectBoundingModeGradients))
835 s->emulationSpecifier |= QPaintEngine::ObjectBoundingModeGradients;
836 else
837 s->emulationSpecifier &= ~QPaintEngine::ObjectBoundingModeGradients;
838
839 // Opaque backgrounds...
840 if (s->bgMode == Qt::OpaqueMode &&
841 (is_pen_transparent(pen: s->pen) || is_brush_transparent(brush: s->brush)))
842 s->emulationSpecifier |= QPaintEngine_OpaqueBackground;
843 else
844 s->emulationSpecifier &= ~QPaintEngine_OpaqueBackground;
845
846#if 0
847 //won't be correct either way because the device can already have
848 // something rendered to it in which case subsequent emulation
849 // on a fully transparent qimage and then blitting the results
850 // won't produce correct results
851 // Blend modes
852 if (state->composition_mode > QPainter::CompositionMode_Xor &&
853 !engine->hasFeature(QPaintEngine::BlendModes))
854 s->emulationSpecifier |= QPaintEngine::BlendModes;
855 else
856 s->emulationSpecifier &= ~QPaintEngine::BlendModes;
857#endif
858}
859
860void QPainterPrivate::updateStateImpl(QPainterState *newState)
861{
862 // ### we might have to call QPainter::begin() here...
863 if (!engine->state) {
864 engine->state = newState;
865 engine->setDirty(QPaintEngine::AllDirty);
866 }
867
868 if (engine->state->painter() != newState->painter)
869 // ### this could break with clip regions vs paths.
870 engine->setDirty(QPaintEngine::AllDirty);
871
872 // Upon restore, revert all changes since last save
873 else if (engine->state != newState)
874 newState->dirtyFlags |= QPaintEngine::DirtyFlags(static_cast<QPainterState *>(engine->state)->changeFlags);
875
876 // We need to store all changes made so that restore can deal with them
877 else
878 newState->changeFlags |= newState->dirtyFlags;
879
880 updateEmulationSpecifier(s: newState);
881
882 // Unset potential dirty background mode
883 newState->dirtyFlags &= ~(QPaintEngine::DirtyBackgroundMode
884 | QPaintEngine::DirtyBackground);
885
886 engine->state = newState;
887 engine->updateState(state: *newState);
888 engine->clearDirty(df: QPaintEngine::AllDirty);
889
890}
891
892void QPainterPrivate::updateState(QPainterState *newState)
893{
894
895 if (!newState) {
896 engine->state = newState;
897 } else if (newState->state() || engine->state!=newState) {
898 updateStateImpl(newState);
899 }
900}
901
902
903/*!
904 \class QPainter
905 \brief The QPainter class performs low-level painting on widgets and
906 other paint devices.
907
908 \inmodule QtGui
909 \ingroup painting
910
911 \reentrant
912
913 QPainter provides highly optimized functions to do most of the
914 drawing GUI programs require. It can draw everything from simple
915 lines to complex shapes like pies and chords. It can also draw
916 aligned text and pixmaps. Normally, it draws in a "natural"
917 coordinate system, but it can also do view and world
918 transformation. QPainter can operate on any object that inherits
919 the QPaintDevice class.
920
921 The common use of QPainter is inside a widget's paint event:
922 Construct and customize (e.g. set the pen or the brush) the
923 painter. Then draw. Remember to destroy the QPainter object after
924 drawing. For example:
925
926 \snippet code/src_gui_painting_qpainter.cpp 0
927
928 The core functionality of QPainter is drawing, but the class also
929 provide several functions that allows you to customize QPainter's
930 settings and its rendering quality, and others that enable
931 clipping. In addition you can control how different shapes are
932 merged together by specifying the painter's composition mode.
933
934 The isActive() function indicates whether the painter is active. A
935 painter is activated by the begin() function and the constructor
936 that takes a QPaintDevice argument. The end() function, and the
937 destructor, deactivates it.
938
939 Together with the QPaintDevice and QPaintEngine classes, QPainter
940 form the basis for Qt's paint system. QPainter is the class used
941 to perform drawing operations. QPaintDevice represents a device
942 that can be painted on using a QPainter. QPaintEngine provides the
943 interface that the painter uses to draw onto different types of
944 devices. If the painter is active, device() returns the paint
945 device on which the painter paints, and paintEngine() returns the
946 paint engine that the painter is currently operating on. For more
947 information, see the \l {Paint System}.
948
949 Sometimes it is desirable to make someone else paint on an unusual
950 QPaintDevice. QPainter supports a static function to do this,
951 setRedirected().
952
953 \warning When the paintdevice is a widget, QPainter can only be
954 used inside a paintEvent() function or in a function called by
955 paintEvent().
956
957 \section1 Settings
958
959 There are several settings that you can customize to make QPainter
960 draw according to your preferences:
961
962 \list
963
964 \li font() is the font used for drawing text. If the painter
965 isActive(), you can retrieve information about the currently set
966 font, and its metrics, using the fontInfo() and fontMetrics()
967 functions respectively.
968
969 \li brush() defines the color or pattern that is used for filling
970 shapes.
971
972 \li pen() defines the color or stipple that is used for drawing
973 lines or boundaries.
974
975 \li backgroundMode() defines whether there is a background() or
976 not, i.e it is either Qt::OpaqueMode or Qt::TransparentMode.
977
978 \li background() only applies when backgroundMode() is \l
979 Qt::OpaqueMode and pen() is a stipple. In that case, it
980 describes the color of the background pixels in the stipple.
981
982 \li brushOrigin() defines the origin of the tiled brushes, normally
983 the origin of widget's background.
984
985 \li viewport(), window(), worldTransform() make up the painter's coordinate
986 transformation system. For more information, see the \l
987 {Coordinate Transformations} section and the \l {Coordinate
988 System} documentation.
989
990 \li hasClipping() tells whether the painter clips at all. (The paint
991 device clips, too.) If the painter clips, it clips to clipRegion().
992
993 \li layoutDirection() defines the layout direction used by the
994 painter when drawing text.
995
996 \li worldMatrixEnabled() tells whether world transformation is enabled.
997
998 \li viewTransformEnabled() tells whether view transformation is
999 enabled.
1000
1001 \endlist
1002
1003 Note that some of these settings mirror settings in some paint
1004 devices, e.g. QWidget::font(). The QPainter::begin() function (or
1005 equivalently the QPainter constructor) copies these attributes
1006 from the paint device.
1007
1008 You can at any time save the QPainter's state by calling the
1009 save() function which saves all the available settings on an
1010 internal stack. The restore() function pops them back.
1011
1012 \section1 Drawing
1013
1014 QPainter provides functions to draw most primitives: drawPoint(),
1015 drawPoints(), drawLine(), drawRect(), drawRoundedRect(),
1016 drawEllipse(), drawArc(), drawPie(), drawChord(), drawPolyline(),
1017 drawPolygon(), drawConvexPolygon() and drawCubicBezier(). The two
1018 convenience functions, drawRects() and drawLines(), draw the given
1019 number of rectangles or lines in the given array of \l
1020 {QRect}{QRects} or \l {QLine}{QLines} using the current pen and
1021 brush.
1022
1023 The QPainter class also provides the fillRect() function which
1024 fills the given QRect, with the given QBrush, and the eraseRect()
1025 function that erases the area inside the given rectangle.
1026
1027 All of these functions have both integer and floating point
1028 versions.
1029
1030 \table 100%
1031 \row
1032 \li \inlineimage qpainter-basicdrawing.png
1033 \li
1034 \b {Basic Drawing Example}
1035
1036 The \l {painting/basicdrawing}{Basic Drawing} example shows how to
1037 display basic graphics primitives in a variety of styles using the
1038 QPainter class.
1039
1040 \endtable
1041
1042 If you need to draw a complex shape, especially if you need to do
1043 so repeatedly, consider creating a QPainterPath and drawing it
1044 using drawPath().
1045
1046 \table 100%
1047 \row
1048 \li
1049 \b {Painter Paths example}
1050
1051 The QPainterPath class provides a container for painting
1052 operations, enabling graphical shapes to be constructed and
1053 reused.
1054
1055 The \l {painting/painterpaths}{Painter Paths} example shows how
1056 painter paths can be used to build complex shapes for rendering.
1057
1058 \li \inlineimage qpainter-painterpaths.png
1059 \endtable
1060
1061 QPainter also provides the fillPath() function which fills the
1062 given QPainterPath with the given QBrush, and the strokePath()
1063 function that draws the outline of the given path (i.e. strokes
1064 the path).
1065
1066 See also the \l {painting/deform}{Vector Deformation} example which
1067 shows how to use advanced vector techniques to draw text using a
1068 QPainterPath, the \l {painting/gradients}{Gradients} example which shows
1069 the different types of gradients that are available in Qt, and the \l
1070 {painting/pathstroke}{Path Stroking} example which shows Qt's built-in
1071 dash patterns and shows how custom patterns can be used to extend
1072 the range of available patterns.
1073
1074 \table
1075 \header
1076 \li \l {painting/deform}{Vector Deformation}
1077 \li \l {painting/gradients}{Gradients}
1078 \li \l {painting/pathstroke}{Path Stroking}
1079 \row
1080 \li \inlineimage qpainter-vectordeformation.png
1081 \li \inlineimage qpainter-gradients.png
1082 \li \inlineimage qpainter-pathstroking.png
1083 \endtable
1084
1085 Text drawing is done using drawText(). When you need
1086 fine-grained positioning, boundingRect() tells you where a given
1087 drawText() command will draw.
1088
1089 \section1 Drawing Pixmaps and Images
1090
1091 There are functions to draw pixmaps/images, namely drawPixmap(),
1092 drawImage() and drawTiledPixmap(). Both drawPixmap() and drawImage()
1093 produce the same result, except that drawPixmap() is faster
1094 on-screen while drawImage() may be faster on a QPrinter or other
1095 devices.
1096
1097 There is a drawPicture() function that draws the contents of an
1098 entire QPicture. The drawPicture() function is the only function
1099 that disregards all the painter's settings as QPicture has its own
1100 settings.
1101
1102 \section2 Drawing High Resolution Versions of Pixmaps and Images
1103
1104 High resolution versions of pixmaps have a \e{device pixel ratio} value larger
1105 than 1 (see QImageReader, QPixmap::devicePixelRatio()). Should it match the value
1106 of the underlying QPaintDevice, it is drawn directly onto the device with no
1107 additional transformation applied.
1108
1109 This is for example the case when drawing a QPixmap of 64x64 pixels size with
1110 a device pixel ratio of 2 onto a high DPI screen which also has
1111 a device pixel ratio of 2. Note that the pixmap is then effectively 32x32
1112 pixels in \e{user space}. Code paths in Qt that calculate layout geometry
1113 based on the pixmap size will use this size. The net effect of this is that
1114 the pixmap is displayed as high DPI pixmap rather than a large pixmap.
1115
1116 \section1 Rendering Quality
1117
1118 To get the optimal rendering result using QPainter, you should use
1119 the platform independent QImage as paint device; i.e. using QImage
1120 will ensure that the result has an identical pixel representation
1121 on any platform.
1122
1123 The QPainter class also provides a means of controlling the
1124 rendering quality through its RenderHint enum and the support for
1125 floating point precision: All the functions for drawing primitives
1126 have floating point versions.
1127
1128 \snippet code/src_gui_painting_qpainter.cpp floatBased
1129
1130 These are often used in combination
1131 with the \l {RenderHint}{QPainter::Antialiasing} render hint.
1132
1133 \snippet code/src_gui_painting_qpainter.cpp renderHint
1134
1135 \table 100%
1136 \row
1137 \li Comparing concentric circles with int and float, and with or without
1138 anti-aliased rendering. Using the floating point precision versions
1139 produces evenly spaced rings. Anti-aliased rendering results in
1140 smooth circles.
1141 \li \inlineimage qpainter-concentriccircles.png
1142 \endtable
1143
1144 The RenderHint enum specifies flags to QPainter that may or may
1145 not be respected by any given engine. \l
1146 {RenderHint}{QPainter::Antialiasing} indicates that the engine
1147 should antialias edges of primitives if possible, \l
1148 {RenderHint}{QPainter::TextAntialiasing} indicates that the engine
1149 should antialias text if possible, and the \l
1150 {RenderHint}{QPainter::SmoothPixmapTransform} indicates that the
1151 engine should use a smooth pixmap transformation algorithm.
1152
1153 The renderHints() function returns a flag that specifies the
1154 rendering hints that are set for this painter. Use the
1155 setRenderHint() function to set or clear the currently set
1156 RenderHints.
1157
1158 \section1 Coordinate Transformations
1159
1160 Normally, the QPainter operates on the device's own coordinate
1161 system (usually pixels), but QPainter has good support for
1162 coordinate transformations.
1163
1164 \table
1165 \header
1166 \li nop \li rotate() \li scale() \li translate()
1167 \row
1168 \li \inlineimage qpainter-clock.png
1169 \li \inlineimage qpainter-rotation.png
1170 \li \inlineimage qpainter-scale.png
1171 \li \inlineimage qpainter-translation.png
1172 \endtable
1173
1174 The most commonly used transformations are scaling, rotation,
1175 translation and shearing. Use the scale() function to scale the
1176 coordinate system by a given offset, the rotate() function to
1177 rotate it clockwise and translate() to translate it (i.e. adding a
1178 given offset to the points). You can also twist the coordinate
1179 system around the origin using the shear() function. See the \l
1180 {painting/affine}{Affine Transformations} example for a visualization of
1181 a sheared coordinate system.
1182
1183 See also the \l {painting/transformations}{Transformations}
1184 example which shows how transformations influence the way that
1185 QPainter renders graphics primitives. In particular it shows how
1186 the order of transformations affects the result.
1187
1188 \table 100%
1189 \row
1190 \li
1191 \b {Affine Transformations Example}
1192
1193 The \l {painting/affine}{Affine Transformations} example shows Qt's
1194 ability to perform affine transformations on painting
1195 operations. The demo also allows the user to experiment with the
1196 transformation operations and see the results immediately.
1197
1198 \li \inlineimage qpainter-affinetransformations.png
1199 \endtable
1200
1201 All the transformation operations operate on the transformation
1202 worldTransform(). A matrix transforms a point in the plane to another
1203 point. For more information about the transformation matrix, see
1204 the \l {Coordinate System} and QTransform documentation.
1205
1206 The setWorldTransform() function can replace or add to the currently
1207 set worldTransform(). The resetTransform() function resets any
1208 transformations that were made using translate(), scale(),
1209 shear(), rotate(), setWorldTransform(), setViewport() and setWindow()
1210 functions. The deviceTransform() returns the matrix that transforms
1211 from logical coordinates to device coordinates of the platform
1212 dependent paint device. The latter function is only needed when
1213 using platform painting commands on the platform dependent handle,
1214 and the platform does not do transformations nativly.
1215
1216 When drawing with QPainter, we specify points using logical
1217 coordinates which then are converted into the physical coordinates
1218 of the paint device. The mapping of the logical coordinates to the
1219 physical coordinates are handled by QPainter's combinedTransform(), a
1220 combination of viewport() and window() and worldTransform(). The
1221 viewport() represents the physical coordinates specifying an
1222 arbitrary rectangle, the window() describes the same rectangle in
1223 logical coordinates, and the worldTransform() is identical with the
1224 transformation matrix.
1225
1226 See also \l {Coordinate System}
1227
1228 \section1 Clipping
1229
1230 QPainter can clip any drawing operation to a rectangle, a region,
1231 or a vector path. The current clip is available using the
1232 functions clipRegion() and clipPath(). Whether paths or regions are
1233 preferred (faster) depends on the underlying paintEngine(). For
1234 example, the QImage paint engine prefers paths while the X11 paint
1235 engine prefers regions. Setting a clip is done in the painters
1236 logical coordinates.
1237
1238 After QPainter's clipping, the paint device may also clip. For
1239 example, most widgets clip away the pixels used by child widgets,
1240 and most printers clip away an area near the edges of the paper.
1241 This additional clipping is not reflected by the return value of
1242 clipRegion() or hasClipping().
1243
1244 \section1 Composition Modes
1245 \target Composition Modes
1246
1247 QPainter provides the CompositionMode enum which defines the
1248 Porter-Duff rules for digital image compositing; it describes a
1249 model for combining the pixels in one image, the source, with the
1250 pixels in another image, the destination.
1251
1252 The two most common forms of composition are \l
1253 {QPainter::CompositionMode}{Source} and \l
1254 {QPainter::CompositionMode}{SourceOver}. \l
1255 {QPainter::CompositionMode}{Source} is used to draw opaque objects
1256 onto a paint device. In this mode, each pixel in the source
1257 replaces the corresponding pixel in the destination. In \l
1258 {QPainter::CompositionMode}{SourceOver} composition mode, the
1259 source object is transparent and is drawn on top of the
1260 destination.
1261
1262 Note that composition transformation operates pixelwise. For that
1263 reason, there is a difference between using the graphic primitive
1264 itself and its bounding rectangle: The bounding rect contains
1265 pixels with alpha == 0 (i.e the pixels surrounding the
1266 primitive). These pixels will overwrite the other image's pixels,
1267 effectively clearing those, while the primitive only overwrites
1268 its own area.
1269
1270 \table 100%
1271 \row
1272 \li \inlineimage qpainter-compositiondemo.png
1273
1274 \li
1275 \b {Composition Modes Example}
1276
1277 The \l {painting/composition}{Composition Modes} example, available in
1278 Qt's examples directory, allows you to experiment with the various
1279 composition modes and see the results immediately.
1280
1281 \endtable
1282
1283 \section1 Limitations
1284 \target Limitations
1285
1286 If you are using coordinates with Qt's raster-based paint engine, it is
1287 important to note that, while coordinates greater than +/- 2\sup 15 can
1288 be used, any painting performed with coordinates outside this range is not
1289 guaranteed to be shown; the drawing may be clipped. This is due to the
1290 use of \c{short int} in the implementation.
1291
1292 The outlines generated by Qt's stroker are only an approximation when dealing
1293 with curved shapes. It is in most cases impossible to represent the outline of
1294 a bezier curve segment using another bezier curve segment, and so Qt approximates
1295 the curve outlines by using several smaller curves. For performance reasons there
1296 is a limit to how many curves Qt uses for these outlines, and thus when using
1297 large pen widths or scales the outline error increases. To generate outlines with
1298 smaller errors it is possible to use the QPainterPathStroker class, which has the
1299 setCurveThreshold member function which let's the user specify the error tolerance.
1300 Another workaround is to convert the paths to polygons first and then draw the
1301 polygons instead.
1302
1303 \section1 Performance
1304
1305 QPainter is a rich framework that allows developers to do a great
1306 variety of graphical operations, such as gradients, composition
1307 modes and vector graphics. And QPainter can do this across a
1308 variety of different hardware and software stacks. Naturally the
1309 underlying combination of hardware and software has some
1310 implications for performance, and ensuring that every single
1311 operation is fast in combination with all the various combinations
1312 of composition modes, brushes, clipping, transformation, etc, is
1313 close to an impossible task because of the number of
1314 permutations. As a compromise we have selected a subset of the
1315 QPainter API and backends, where performance is guaranteed to be as
1316 good as we can sensibly get it for the given combination of
1317 hardware and software.
1318
1319 The backends we focus on as high-performance engines are:
1320
1321 \list
1322
1323 \li Raster - This backend implements all rendering in pure software
1324 and is always used to render into QImages. For optimal performance
1325 only use the format types QImage::Format_ARGB32_Premultiplied,
1326 QImage::Format_RGB32 or QImage::Format_RGB16. Any other format,
1327 including QImage::Format_ARGB32, has significantly worse
1328 performance. This engine is used by default for QWidget and QPixmap.
1329
1330 \li OpenGL 2.0 (ES) - This backend is the primary backend for
1331 hardware accelerated graphics. It can be run on desktop machines
1332 and embedded devices supporting the OpenGL 2.0 or OpenGL/ES 2.0
1333 specification. This includes most graphics chips produced in the
1334 last couple of years. The engine can be enabled by using QPainter
1335 onto a QOpenGLWidget.
1336
1337 \endlist
1338
1339 These operations are:
1340
1341 \list
1342
1343 \li Simple transformations, meaning translation and scaling, pluss
1344 0, 90, 180, 270 degree rotations.
1345
1346 \li \c drawPixmap() in combination with simple transformations and
1347 opacity with non-smooth transformation mode
1348 (\c QPainter::SmoothPixmapTransform not enabled as a render hint).
1349
1350 \li Rectangle fills with solid color, two-color linear gradients
1351 and simple transforms.
1352
1353 \li Rectangular clipping with simple transformations and intersect
1354 clip.
1355
1356 \li Composition Modes \c QPainter::CompositionMode_Source and
1357 QPainter::CompositionMode_SourceOver.
1358
1359 \li Rounded rectangle filling using solid color and two-color
1360 linear gradients fills.
1361
1362 \li 3x3 patched pixmaps, via qDrawBorderPixmap.
1363
1364 \endlist
1365
1366 This list gives an indication of which features to safely use in
1367 an application where performance is critical. For certain setups,
1368 other operations may be fast too, but before making extensive use
1369 of them, it is recommended to benchmark and verify them on the
1370 system where the software will run in the end. There are also
1371 cases where expensive operations are ok to use, for instance when
1372 the result is cached in a QPixmap.
1373
1374 \sa QPaintDevice, QPaintEngine, {Qt SVG}, {Basic Drawing Example}, {<qdrawutil.h>}{Drawing Utility Functions}
1375*/
1376
1377/*!
1378 \enum QPainter::RenderHint
1379
1380 Renderhints are used to specify flags to QPainter that may or
1381 may not be respected by any given engine.
1382
1383 \value Antialiasing Indicates that the engine should antialias
1384 edges of primitives if possible.
1385
1386 \value TextAntialiasing Indicates that the engine should antialias
1387 text if possible. To forcibly disable antialiasing for text, do not
1388 use this hint. Instead, set QFont::NoAntialias on your font's style
1389 strategy.
1390
1391 \value SmoothPixmapTransform Indicates that the engine should use
1392 a smooth pixmap transformation algorithm (such as bilinear) rather
1393 than nearest neighbor.
1394
1395 \value VerticalSubpixelPositioning Allow text to be positioned at fractions
1396 of pixels vertically as well as horizontally, if this is supported by the
1397 font engine. This is currently supported by Freetype on all platforms when
1398 the hinting preference is QFont::PreferNoHinting, and also on macOS. For
1399 most use cases this will not improve visual quality, but may increase memory
1400 consumption and some reduction in text rendering performance. Therefore, enabling
1401 this is not recommended unless the use case requires it. One such use case could
1402 be aligning glyphs with other visual primitives.
1403 This value was added in Qt 6.1.
1404
1405 \value LosslessImageRendering Use a lossless image rendering, whenever possible.
1406 Currently, this hint is only used when QPainter is employed to output a PDF
1407 file through QPrinter or QPdfWriter, where drawImage()/drawPixmap() calls
1408 will encode images using a lossless compression algorithm instead of lossy
1409 JPEG compression.
1410 This value was added in Qt 5.13.
1411
1412 \value NonCosmeticBrushPatterns When painting with a brush with one of the predefined pattern
1413 styles, transform the pattern too, along with the object being painted. The default is to treat
1414 the pattern as cosmetic, so that the pattern pixels will map directly to device pixels,
1415 independently of any active transformations.
1416 This value was added in Qt 6.4.
1417
1418 \sa renderHints(), setRenderHint(), {QPainter#Rendering
1419 Quality}{Rendering Quality}
1420
1421*/
1422
1423/*!
1424 Constructs a painter.
1425
1426 \sa begin(), end()
1427*/
1428
1429QPainter::QPainter()
1430 : d_ptr(new QPainterPrivate(this))
1431{
1432}
1433
1434/*!
1435 \fn QPainter::QPainter(QPaintDevice *device)
1436
1437 Constructs a painter that begins painting the paint \a device
1438 immediately.
1439
1440 This constructor is convenient for short-lived painters, e.g. in a
1441 QWidget::paintEvent() and should be used only once. The
1442 constructor calls begin() for you and the QPainter destructor
1443 automatically calls end().
1444
1445 Here's an example using begin() and end():
1446 \snippet code/src_gui_painting_qpainter.cpp 1
1447
1448 The same example using this constructor:
1449 \snippet code/src_gui_painting_qpainter.cpp 2
1450
1451 Since the constructor cannot provide feedback when the initialization
1452 of the painter failed you should rather use begin() and end() to paint
1453 on external devices, e.g. printers.
1454
1455 \sa begin(), end()
1456*/
1457
1458QPainter::QPainter(QPaintDevice *pd)
1459 : d_ptr(nullptr)
1460{
1461 Q_ASSERT(pd != nullptr);
1462 if (!QPainterPrivate::attachPainterPrivate(q: this, pdev: pd)) {
1463 d_ptr.reset(p: new QPainterPrivate(this));
1464 begin(pd);
1465 }
1466 Q_ASSERT(d_ptr);
1467}
1468
1469/*!
1470 Destroys the painter.
1471*/
1472QPainter::~QPainter()
1473{
1474 d_ptr->inDestructor = true;
1475 QT_TRY {
1476 if (isActive())
1477 end();
1478 else if (d_ptr->refcount > 1)
1479 d_ptr->detachPainterPrivate(q: this);
1480 } QT_CATCH(...) {
1481 // don't throw anything in the destructor.
1482 }
1483 if (d_ptr) {
1484 // Make sure we haven't messed things up.
1485 Q_ASSERT(d_ptr->inDestructor);
1486 d_ptr->inDestructor = false;
1487 Q_ASSERT(d_ptr->refcount == 1);
1488 }
1489}
1490
1491/*!
1492 Returns the paint device on which this painter is currently
1493 painting, or \nullptr if the painter is not active.
1494
1495 \sa isActive()
1496*/
1497
1498QPaintDevice *QPainter::device() const
1499{
1500 Q_D(const QPainter);
1501 if (isActive() && d->engine->d_func()->currentClipDevice)
1502 return d->engine->d_func()->currentClipDevice;
1503 return d->original_device;
1504}
1505
1506/*!
1507 Returns \c true if begin() has been called and end() has not yet been
1508 called; otherwise returns \c false.
1509
1510 \sa begin(), QPaintDevice::paintingActive()
1511*/
1512
1513bool QPainter::isActive() const
1514{
1515 Q_D(const QPainter);
1516 return d->engine != nullptr;
1517}
1518
1519void QPainterPrivate::initFrom(const QPaintDevice *device)
1520{
1521 if (!engine) {
1522 qWarning(msg: "QPainter::initFrom: Painter not active, aborted");
1523 return;
1524 }
1525
1526 Q_Q(QPainter);
1527 device->initPainter(painter: q);
1528
1529 if (extended) {
1530 extended->penChanged();
1531 } else if (engine) {
1532 engine->setDirty(QPaintEngine::DirtyPen);
1533 engine->setDirty(QPaintEngine::DirtyBrush);
1534 engine->setDirty(QPaintEngine::DirtyFont);
1535 }
1536}
1537
1538/*!
1539 Saves the current painter state (pushes the state onto a stack). A
1540 save() must be followed by a corresponding restore(); the end()
1541 function unwinds the stack.
1542
1543 \sa restore()
1544*/
1545
1546void QPainter::save()
1547{
1548#ifdef QT_DEBUG_DRAW
1549 if (qt_show_painter_debug_output)
1550 printf("QPainter::save()\n");
1551#endif
1552 Q_D(QPainter);
1553 if (!d->engine) {
1554 qWarning(msg: "QPainter::save: Painter not active");
1555 return;
1556 }
1557
1558 std::unique_ptr<QPainterState> prev;
1559 if (d->extended) {
1560 // separate the creation of a new state from the update of d->state, since some
1561 // engines access d->state directly (not via createState()'s argument)
1562 std::unique_ptr<QPainterState> next(d->extended->createState(orig: d->state.get()));
1563 prev = std::exchange(obj&: d->state, new_val: std::move(next));
1564 d->extended->setState(d->state.get());
1565 } else {
1566 d->updateState(state&: d->state);
1567 prev = std::exchange(obj&: d->state, new_val: std::make_unique<QPainterState>(args: d->state.get()));
1568 d->engine->state = d->state.get();
1569 }
1570 d->savedStates.push(x: std::move(prev));
1571}
1572
1573/*!
1574 Restores the current painter state (pops a saved state off the
1575 stack).
1576
1577 \sa save()
1578*/
1579
1580void QPainter::restore()
1581{
1582#ifdef QT_DEBUG_DRAW
1583 if (qt_show_painter_debug_output)
1584 printf("QPainter::restore()\n");
1585#endif
1586 Q_D(QPainter);
1587 if (d->savedStates.empty()) {
1588 qWarning(msg: "QPainter::restore: Unbalanced save/restore");
1589 return;
1590 } else if (!d->engine) {
1591 qWarning(msg: "QPainter::restore: Painter not active");
1592 return;
1593 }
1594
1595 const auto tmp = std::exchange(obj&: d->state, new_val: std::move(d->savedStates.top()));
1596 d->savedStates.pop();
1597 d->txinv = false;
1598
1599 if (d->extended) {
1600 d->checkEmulation();
1601 d->extended->setState(d->state.get());
1602 return;
1603 }
1604
1605 // trigger clip update if the clip path/region has changed since
1606 // last save
1607 if (!d->state->clipInfo.isEmpty()
1608 && (tmp->changeFlags & (QPaintEngine::DirtyClipRegion | QPaintEngine::DirtyClipPath))) {
1609 // reuse the tmp state to avoid any extra allocs...
1610 tmp->dirtyFlags = QPaintEngine::DirtyClipPath;
1611 tmp->clipOperation = Qt::NoClip;
1612 tmp->clipPath = QPainterPath();
1613 d->engine->updateState(state: *tmp);
1614 // replay the list of clip states,
1615 for (const QPainterClipInfo &info : std::as_const(t&: d->state->clipInfo)) {
1616 tmp->matrix = info.matrix;
1617 tmp->clipOperation = info.operation;
1618 if (info.clipType == QPainterClipInfo::RectClip) {
1619 tmp->dirtyFlags = QPaintEngine::DirtyClipRegion | QPaintEngine::DirtyTransform;
1620 tmp->clipRegion = info.rect;
1621 } else if (info.clipType == QPainterClipInfo::RegionClip) {
1622 tmp->dirtyFlags = QPaintEngine::DirtyClipRegion | QPaintEngine::DirtyTransform;
1623 tmp->clipRegion = info.region;
1624 } else { // clipType == QPainterClipInfo::PathClip
1625 tmp->dirtyFlags = QPaintEngine::DirtyClipPath | QPaintEngine::DirtyTransform;
1626 tmp->clipPath = info.path;
1627 }
1628 d->engine->updateState(state: *tmp);
1629 }
1630
1631
1632 //Since we've updated the clip region anyway, pretend that the clip path hasn't changed:
1633 d->state->dirtyFlags &= ~(QPaintEngine::DirtyClipPath | QPaintEngine::DirtyClipRegion);
1634 tmp->changeFlags &= ~uint(QPaintEngine::DirtyClipPath | QPaintEngine::DirtyClipRegion);
1635 tmp->changeFlags |= QPaintEngine::DirtyTransform;
1636 }
1637
1638 d->updateState(newState: d->state.get());
1639}
1640
1641
1642/*!
1643
1644 \fn bool QPainter::begin(QPaintDevice *device)
1645
1646 Begins painting the paint \a device and returns \c true if
1647 successful; otherwise returns \c false.
1648
1649 Notice that all painter settings (setPen(), setBrush() etc.) are reset
1650 to default values when begin() is called.
1651
1652 The errors that can occur are serious problems, such as these:
1653
1654 \snippet code/src_gui_painting_qpainter.cpp 3
1655
1656 Note that most of the time, you can use one of the constructors
1657 instead of begin(), and that end() is automatically done at
1658 destruction.
1659
1660 \warning A paint device can only be painted by one painter at a
1661 time.
1662
1663 \warning Painting on a QImage with the format
1664 QImage::Format_Indexed8 is not supported.
1665
1666 \sa end(), QPainter()
1667*/
1668
1669static inline void qt_cleanup_painter_state(QPainterPrivate *d)
1670{
1671 d->savedStates.clear();
1672 d->state = nullptr;
1673 d->engine = nullptr;
1674 d->device = nullptr;
1675}
1676
1677bool QPainter::begin(QPaintDevice *pd)
1678{
1679 Q_ASSERT(pd);
1680
1681 if (pd->painters > 0) {
1682 qWarning(msg: "QPainter::begin: A paint device can only be painted by one painter at a time.");
1683 return false;
1684 }
1685
1686 if (d_ptr->engine) {
1687 qWarning(msg: "QPainter::begin: Painter already active");
1688 return false;
1689 }
1690
1691 if (QPainterPrivate::attachPainterPrivate(q: this, pdev: pd))
1692 return true;
1693
1694 Q_D(QPainter);
1695
1696 d->helper_device = pd;
1697 d->original_device = pd;
1698
1699 QPoint redirectionOffset;
1700 QPaintDevice *rpd = pd->redirected(offset: &redirectionOffset);
1701 if (rpd)
1702 pd = rpd;
1703
1704#ifdef QT_DEBUG_DRAW
1705 if (qt_show_painter_debug_output)
1706 printf("QPainter::begin(), device=%p, type=%d\n", pd, pd->devType());
1707#endif
1708
1709 if (pd->devType() == QInternal::Pixmap)
1710 static_cast<QPixmap *>(pd)->detach();
1711 else if (pd->devType() == QInternal::Image)
1712 static_cast<QImage *>(pd)->detach();
1713
1714 d->engine.reset(p: pd->paintEngine());
1715
1716 if (!d->engine) {
1717 qWarning(msg: "QPainter::begin: Paint device returned engine == 0, type: %d", pd->devType());
1718 return false;
1719 }
1720
1721 d->device = pd;
1722
1723 d->extended = d->engine->isExtended() ? static_cast<QPaintEngineEx *>(d->engine.get()) : nullptr;
1724 if (d->emulationEngine)
1725 d->emulationEngine->real_engine = d->extended;
1726
1727 // Setup new state...
1728 Q_ASSERT(!d->state);
1729 d->state.reset(p: d->extended ? d->extended->createState(orig: nullptr) : new QPainterState);
1730 d->state->painter = this;
1731
1732 d->state->redirectionMatrix.translate(dx: -redirectionOffset.x(), dy: -redirectionOffset.y());
1733 d->state->brushOrigin = QPointF();
1734
1735 // Slip a painter state into the engine before we do any other operations
1736 if (d->extended)
1737 d->extended->setState(d->state.get());
1738 else
1739 d->engine->state = d->state.get();
1740
1741 switch (pd->devType()) {
1742 case QInternal::Pixmap:
1743 {
1744 QPixmap *pm = static_cast<QPixmap *>(pd);
1745 Q_ASSERT(pm);
1746 if (pm->isNull()) {
1747 qWarning(msg: "QPainter::begin: Cannot paint on a null pixmap");
1748 qt_cleanup_painter_state(d);
1749 return false;
1750 }
1751
1752 if (pm->depth() == 1) {
1753 d->state->pen = QPen(Qt::color1);
1754 d->state->brush = QBrush(Qt::color0);
1755 }
1756 break;
1757 }
1758 case QInternal::Image:
1759 {
1760 QImage *img = static_cast<QImage *>(pd);
1761 Q_ASSERT(img);
1762 if (img->isNull()) {
1763 qWarning(msg: "QPainter::begin: Cannot paint on a null image");
1764 qt_cleanup_painter_state(d);
1765 return false;
1766 } else if (img->format() == QImage::Format_Indexed8 ||
1767 img->format() == QImage::Format_CMYK8888) {
1768 // Painting on these formats is not supported.
1769 qWarning() << "QPainter::begin: Cannot paint on an image with the"
1770 << img->format()
1771 << "format";
1772 qt_cleanup_painter_state(d);
1773 return false;
1774 }
1775 if (img->depth() == 1) {
1776 d->state->pen = QPen(Qt::color1);
1777 d->state->brush = QBrush(Qt::color0);
1778 }
1779 break;
1780 }
1781 default:
1782 break;
1783 }
1784 if (d->state->ww == 0) // For compat with 3.x painter defaults
1785 d->state->ww = d->state->wh = d->state->vw = d->state->vh = 1024;
1786
1787 d->engine->setPaintDevice(pd);
1788
1789 bool begun = d->engine->begin(pdev: pd);
1790 if (!begun) {
1791 qWarning(msg: "QPainter::begin(): Returned false");
1792 if (d->engine->isActive()) {
1793 end();
1794 } else {
1795 qt_cleanup_painter_state(d);
1796 }
1797 return false;
1798 } else {
1799 d->engine->setActive(begun);
1800 }
1801
1802 // Copy painter properties from original paint device,
1803 // required for QPixmap::grabWidget()
1804 if (d->original_device->devType() == QInternal::Widget) {
1805 d->initFrom(device: d->original_device);
1806 } else {
1807 d->state->layoutDirection = Qt::LayoutDirectionAuto;
1808 // make sure we have a font compatible with the paintdevice
1809 d->state->deviceFont = d->state->font = QFont(d->state->deviceFont, device());
1810 }
1811
1812 QRect systemRect = d->engine->systemRect();
1813 if (!systemRect.isEmpty()) {
1814 d->state->ww = d->state->vw = systemRect.width();
1815 d->state->wh = d->state->vh = systemRect.height();
1816 } else {
1817 d->state->ww = d->state->vw = pd->metric(metric: QPaintDevice::PdmWidth);
1818 d->state->wh = d->state->vh = pd->metric(metric: QPaintDevice::PdmHeight);
1819 }
1820
1821 const QPoint coordinateOffset = d->engine->coordinateOffset();
1822 d->state->redirectionMatrix.translate(dx: -coordinateOffset.x(), dy: -coordinateOffset.y());
1823
1824 Q_ASSERT(d->engine->isActive());
1825
1826 if (!d->state->redirectionMatrix.isIdentity() || !qFuzzyCompare(p1: d->effectiveDevicePixelRatio(), p2: qreal(1.0)))
1827 d->updateMatrix();
1828
1829 Q_ASSERT(d->engine->isActive());
1830 d->state->renderHints = QPainter::TextAntialiasing;
1831 ++d->device->painters;
1832
1833 d->state->emulationSpecifier = 0;
1834
1835 return true;
1836}
1837
1838/*!
1839 Ends painting. Any resources used while painting are released. You
1840 don't normally need to call this since it is called by the
1841 destructor.
1842
1843 Returns \c true if the painter is no longer active; otherwise returns \c false.
1844
1845 \sa begin(), isActive()
1846*/
1847
1848bool QPainter::end()
1849{
1850#ifdef QT_DEBUG_DRAW
1851 if (qt_show_painter_debug_output)
1852 printf("QPainter::end()\n");
1853#endif
1854 Q_D(QPainter);
1855
1856 if (!d->engine) {
1857 qWarning(msg: "QPainter::end: Painter not active, aborted");
1858 qt_cleanup_painter_state(d);
1859 return false;
1860 }
1861
1862 if (d->refcount > 1) {
1863 d->detachPainterPrivate(q: this);
1864 return true;
1865 }
1866
1867 bool ended = true;
1868
1869 if (d->engine->isActive()) {
1870 ended = d->engine->end();
1871 d->updateState(newState: nullptr);
1872
1873 --d->device->painters;
1874 if (d->device->painters == 0) {
1875 d->engine->setPaintDevice(nullptr);
1876 d->engine->setActive(false);
1877 }
1878 }
1879
1880 if (d->savedStates.size() > 0) {
1881 qWarning(msg: "QPainter::end: Painter ended with %d saved states", int(d->savedStates.size()));
1882 }
1883
1884 d->engine.reset();
1885 d->emulationEngine = nullptr;
1886 d->extended = nullptr;
1887
1888 qt_cleanup_painter_state(d);
1889
1890 return ended;
1891}
1892
1893
1894/*!
1895 Returns the paint engine that the painter is currently operating
1896 on if the painter is active; otherwise 0.
1897
1898 \sa isActive()
1899*/
1900QPaintEngine *QPainter::paintEngine() const
1901{
1902 Q_D(const QPainter);
1903 return d->engine.get();
1904}
1905
1906/*!
1907 \since 4.6
1908
1909 Flushes the painting pipeline and prepares for the user issuing commands
1910 directly to the underlying graphics context. Must be followed by a call to
1911 endNativePainting().
1912
1913 Note that only the states the underlying paint engine changes will be reset
1914 to their respective default states. The states we reset may change from
1915 release to release. The following states are currently reset in the OpenGL
1916 2 engine:
1917
1918 \list
1919 \li blending is disabled
1920 \li the depth, stencil and scissor tests are disabled
1921 \li the active texture unit is reset to 0
1922 \li the depth mask, depth function and the clear depth are reset to their
1923 default values
1924 \li the stencil mask, stencil operation and stencil function are reset to
1925 their default values
1926 \li the current color is reset to solid white
1927 \endlist
1928
1929 If, for example, the OpenGL polygon mode is changed by the user inside a
1930 beginNativePaint()/endNativePainting() block, it will not be reset to the
1931 default state by endNativePainting(). Here is an example that shows
1932 intermixing of painter commands and raw OpenGL commands:
1933
1934 \snippet code/src_gui_painting_qpainter.cpp 21
1935
1936 \sa endNativePainting()
1937*/
1938void QPainter::beginNativePainting()
1939{
1940 Q_D(QPainter);
1941 if (!d->engine) {
1942 qWarning(msg: "QPainter::beginNativePainting: Painter not active");
1943 return;
1944 }
1945
1946 if (d->extended)
1947 d->extended->beginNativePainting();
1948}
1949
1950/*!
1951 \since 4.6
1952
1953 Restores the painter after manually issuing native painting commands. Lets
1954 the painter restore any native state that it relies on before calling any
1955 other painter commands.
1956
1957 \sa beginNativePainting()
1958*/
1959void QPainter::endNativePainting()
1960{
1961 Q_D(const QPainter);
1962 if (!d->engine) {
1963 qWarning(msg: "QPainter::beginNativePainting: Painter not active");
1964 return;
1965 }
1966
1967 if (d->extended)
1968 d->extended->endNativePainting();
1969 else
1970 d->engine->syncState();
1971}
1972
1973/*!
1974 Returns the font metrics for the painter if the painter is
1975 active. Otherwise, the return value is undefined.
1976
1977 \sa font(), isActive(), {QPainter#Settings}{Settings}
1978*/
1979
1980QFontMetrics QPainter::fontMetrics() const
1981{
1982 Q_D(const QPainter);
1983 if (!d->engine) {
1984 qWarning(msg: "QPainter::fontMetrics: Painter not active");
1985 return QFontMetrics(QFont());
1986 }
1987 return QFontMetrics(d->state->font);
1988}
1989
1990
1991/*!
1992 Returns the font info for the painter if the painter is
1993 active. Otherwise, the return value is undefined.
1994
1995 \sa font(), isActive(), {QPainter#Settings}{Settings}
1996*/
1997
1998QFontInfo QPainter::fontInfo() const
1999{
2000 Q_D(const QPainter);
2001 if (!d->engine) {
2002 qWarning(msg: "QPainter::fontInfo: Painter not active");
2003 return QFontInfo(QFont());
2004 }
2005 return QFontInfo(d->state->font);
2006}
2007
2008/*!
2009 \since 4.2
2010
2011 Returns the opacity of the painter. The default value is
2012 1.
2013*/
2014
2015qreal QPainter::opacity() const
2016{
2017 Q_D(const QPainter);
2018 if (!d->engine) {
2019 qWarning(msg: "QPainter::opacity: Painter not active");
2020 return 1.0;
2021 }
2022 return d->state->opacity;
2023}
2024
2025/*!
2026 \since 4.2
2027
2028 Sets the opacity of the painter to \a opacity. The value should
2029 be in the range 0.0 to 1.0, where 0.0 is fully transparent and
2030 1.0 is fully opaque.
2031
2032 Opacity set on the painter will apply to all drawing operations
2033 individually.
2034*/
2035
2036void QPainter::setOpacity(qreal opacity)
2037{
2038 Q_D(QPainter);
2039
2040 if (!d->engine) {
2041 qWarning(msg: "QPainter::setOpacity: Painter not active");
2042 return;
2043 }
2044
2045 opacity = qMin(a: qreal(1), b: qMax(a: qreal(0), b: opacity));
2046
2047 if (opacity == d->state->opacity)
2048 return;
2049
2050 d->state->opacity = opacity;
2051
2052 if (d->extended)
2053 d->extended->opacityChanged();
2054 else
2055 d->state->dirtyFlags |= QPaintEngine::DirtyOpacity;
2056}
2057
2058
2059/*!
2060 Returns the currently set brush origin.
2061
2062 \sa setBrushOrigin(), {QPainter#Settings}{Settings}
2063*/
2064
2065QPoint QPainter::brushOrigin() const
2066{
2067 Q_D(const QPainter);
2068 if (!d->engine) {
2069 qWarning(msg: "QPainter::brushOrigin: Painter not active");
2070 return QPoint();
2071 }
2072 return QPointF(d->state->brushOrigin).toPoint();
2073}
2074
2075/*!
2076 \fn void QPainter::setBrushOrigin(const QPointF &position)
2077
2078 Sets the brush origin to \a position.
2079
2080 The brush origin specifies the (0, 0) coordinate of the painter's
2081 brush.
2082
2083 Note that while the brushOrigin() was necessary to adopt the
2084 parent's background for a widget in Qt 3, this is no longer the
2085 case since the Qt 4 painter doesn't paint the background unless
2086 you explicitly tell it to do so by setting the widget's \l
2087 {QWidget::autoFillBackground}{autoFillBackground} property to
2088 true.
2089
2090 \sa brushOrigin(), {QPainter#Settings}{Settings}
2091*/
2092
2093void QPainter::setBrushOrigin(const QPointF &p)
2094{
2095 Q_D(QPainter);
2096#ifdef QT_DEBUG_DRAW
2097 if (qt_show_painter_debug_output)
2098 printf("QPainter::setBrushOrigin(), (%.2f,%.2f)\n", p.x(), p.y());
2099#endif
2100
2101 if (!d->engine) {
2102 qWarning(msg: "QPainter::setBrushOrigin: Painter not active");
2103 return;
2104 }
2105
2106 d->state->brushOrigin = p;
2107
2108 if (d->extended) {
2109 d->extended->brushOriginChanged();
2110 return;
2111 }
2112
2113 d->state->dirtyFlags |= QPaintEngine::DirtyBrushOrigin;
2114}
2115
2116/*!
2117 \fn void QPainter::setBrushOrigin(const QPoint &position)
2118 \overload
2119
2120 Sets the brush's origin to the given \a position.
2121*/
2122
2123/*!
2124 \fn void QPainter::setBrushOrigin(int x, int y)
2125
2126 \overload
2127
2128 Sets the brush's origin to point (\a x, \a y).
2129*/
2130
2131/*!
2132 \enum QPainter::CompositionMode
2133
2134 Defines the modes supported for digital image compositing.
2135 Composition modes are used to specify how the pixels in one image,
2136 the source, are merged with the pixel in another image, the
2137 destination.
2138
2139 Please note that the bitwise raster operation modes, denoted with
2140 a RasterOp prefix, are only natively supported in the X11 and
2141 raster paint engines. This means that the only way to utilize
2142 these modes on the Mac is via a QImage. The RasterOp denoted blend
2143 modes are \e not supported for pens and brushes with alpha
2144 components. Also, turning on the QPainter::Antialiasing render
2145 hint will effectively disable the RasterOp modes.
2146
2147
2148 \image qpainter-compositionmode1.png
2149 \image qpainter-compositionmode2.png
2150
2151 The most common type is SourceOver (often referred to as just
2152 alpha blending) where the source pixel is blended on top of the
2153 destination pixel in such a way that the alpha component of the
2154 source defines the translucency of the pixel.
2155
2156 Several composition modes require an alpha channel in the source or
2157 target images to have an effect. For optimal performance the
2158 image format \l {QImage::Format}{Format_ARGB32_Premultiplied} is
2159 preferred.
2160
2161 When a composition mode is set it applies to all painting
2162 operator, pens, brushes, gradients and pixmap/image drawing.
2163
2164 \value CompositionMode_SourceOver This is the default mode. The
2165 alpha of the source is used to blend the pixel on top of the
2166 destination.
2167
2168 \value CompositionMode_DestinationOver The alpha of the
2169 destination is used to blend it on top of the source pixels. This
2170 mode is the inverse of CompositionMode_SourceOver.
2171
2172 \value CompositionMode_Clear The pixels in the destination are
2173 cleared (set to fully transparent) independent of the source.
2174
2175 \value CompositionMode_Source The output is the source
2176 pixel. (This means a basic copy operation and is identical to
2177 SourceOver when the source pixel is opaque).
2178
2179 \value CompositionMode_Destination The output is the destination
2180 pixel. This means that the blending has no effect. This mode is
2181 the inverse of CompositionMode_Source.
2182
2183 \value CompositionMode_SourceIn The output is the source, where
2184 the alpha is reduced by that of the destination.
2185
2186 \value CompositionMode_DestinationIn The output is the
2187 destination, where the alpha is reduced by that of the
2188 source. This mode is the inverse of CompositionMode_SourceIn.
2189
2190 \value CompositionMode_SourceOut The output is the source, where
2191 the alpha is reduced by the inverse of destination.
2192
2193 \value CompositionMode_DestinationOut The output is the
2194 destination, where the alpha is reduced by the inverse of the
2195 source. This mode is the inverse of CompositionMode_SourceOut.
2196
2197 \value CompositionMode_SourceAtop The source pixel is blended on
2198 top of the destination, with the alpha of the source pixel reduced
2199 by the alpha of the destination pixel.
2200
2201 \value CompositionMode_DestinationAtop The destination pixel is
2202 blended on top of the source, with the alpha of the destination
2203 pixel is reduced by the alpha of the destination pixel. This mode
2204 is the inverse of CompositionMode_SourceAtop.
2205
2206 \value CompositionMode_Xor The source, whose alpha is reduced with
2207 the inverse of the destination alpha, is merged with the
2208 destination, whose alpha is reduced by the inverse of the source
2209 alpha. CompositionMode_Xor is not the same as the bitwise Xor.
2210
2211 \value CompositionMode_Plus Both the alpha and color of the source
2212 and destination pixels are added together.
2213
2214 \value CompositionMode_Multiply The output is the source color
2215 multiplied by the destination. Multiplying a color with white
2216 leaves the color unchanged, while multiplying a color
2217 with black produces black.
2218
2219 \value CompositionMode_Screen The source and destination colors
2220 are inverted and then multiplied. Screening a color with white
2221 produces white, whereas screening a color with black leaves the
2222 color unchanged.
2223
2224 \value CompositionMode_Overlay Multiplies or screens the colors
2225 depending on the destination color. The destination color is mixed
2226 with the source color to reflect the lightness or darkness of the
2227 destination.
2228
2229 \value CompositionMode_Darken The darker of the source and
2230 destination colors is selected.
2231
2232 \value CompositionMode_Lighten The lighter of the source and
2233 destination colors is selected.
2234
2235 \value CompositionMode_ColorDodge The destination color is
2236 brightened to reflect the source color. A black source color
2237 leaves the destination color unchanged.
2238
2239 \value CompositionMode_ColorBurn The destination color is darkened
2240 to reflect the source color. A white source color leaves the
2241 destination color unchanged.
2242
2243 \value CompositionMode_HardLight Multiplies or screens the colors
2244 depending on the source color. A light source color will lighten
2245 the destination color, whereas a dark source color will darken the
2246 destination color.
2247
2248 \value CompositionMode_SoftLight Darkens or lightens the colors
2249 depending on the source color. Similar to
2250 CompositionMode_HardLight.
2251
2252 \value CompositionMode_Difference Subtracts the darker of the
2253 colors from the lighter. Painting with white inverts the
2254 destination color, whereas painting with black leaves the
2255 destination color unchanged.
2256
2257 \value CompositionMode_Exclusion Similar to
2258 CompositionMode_Difference, but with a lower contrast. Painting
2259 with white inverts the destination color, whereas painting with
2260 black leaves the destination color unchanged.
2261
2262 \value RasterOp_SourceOrDestination Does a bitwise OR operation on
2263 the source and destination pixels (src OR dst).
2264
2265 \value RasterOp_SourceAndDestination Does a bitwise AND operation
2266 on the source and destination pixels (src AND dst).
2267
2268 \value RasterOp_SourceXorDestination Does a bitwise XOR operation
2269 on the source and destination pixels (src XOR dst).
2270
2271 \value RasterOp_NotSourceAndNotDestination Does a bitwise NOR
2272 operation on the source and destination pixels ((NOT src) AND (NOT
2273 dst)).
2274
2275 \value RasterOp_NotSourceOrNotDestination Does a bitwise NAND
2276 operation on the source and destination pixels ((NOT src) OR (NOT
2277 dst)).
2278
2279 \value RasterOp_NotSourceXorDestination Does a bitwise operation
2280 where the source pixels are inverted and then XOR'ed with the
2281 destination ((NOT src) XOR dst).
2282
2283 \value RasterOp_NotSource Does a bitwise operation where the
2284 source pixels are inverted (NOT src).
2285
2286 \value RasterOp_NotSourceAndDestination Does a bitwise operation
2287 where the source is inverted and then AND'ed with the destination
2288 ((NOT src) AND dst).
2289
2290 \value RasterOp_SourceAndNotDestination Does a bitwise operation
2291 where the source is AND'ed with the inverted destination pixels
2292 (src AND (NOT dst)).
2293
2294 \value RasterOp_NotSourceOrDestination Does a bitwise operation
2295 where the source is inverted and then OR'ed with the destination
2296 ((NOT src) OR dst).
2297
2298 \value RasterOp_ClearDestination The pixels in the destination are
2299 cleared (set to 0) independent of the source.
2300
2301 \value RasterOp_SetDestination The pixels in the destination are
2302 set (set to 1) independent of the source.
2303
2304 \value RasterOp_NotDestination Does a bitwise operation
2305 where the destination pixels are inverted (NOT dst).
2306
2307 \value RasterOp_SourceOrNotDestination Does a bitwise operation
2308 where the source is OR'ed with the inverted destination pixels
2309 (src OR (NOT dst)).
2310
2311 \sa compositionMode(), setCompositionMode(), {QPainter#Composition
2312 Modes}{Composition Modes}, {Image Composition Example}
2313*/
2314
2315/*!
2316 Sets the composition mode to the given \a mode.
2317
2318 \warning Only a QPainter operating on a QImage fully supports all
2319 composition modes. The RasterOp modes are supported for X11 as
2320 described in compositionMode().
2321
2322 \sa compositionMode()
2323*/
2324void QPainter::setCompositionMode(CompositionMode mode)
2325{
2326 Q_D(QPainter);
2327 if (!d->engine) {
2328 qWarning(msg: "QPainter::setCompositionMode: Painter not active");
2329 return;
2330 }
2331 if (d->state->composition_mode == mode)
2332 return;
2333 if (d->extended) {
2334 d->state->composition_mode = mode;
2335 d->extended->compositionModeChanged();
2336 return;
2337 }
2338
2339 if (mode >= QPainter::RasterOp_SourceOrDestination) {
2340 if (!d->engine->hasFeature(feature: QPaintEngine::RasterOpModes)) {
2341 qWarning(msg: "QPainter::setCompositionMode: "
2342 "Raster operation modes not supported on device");
2343 return;
2344 }
2345 } else if (mode >= QPainter::CompositionMode_Plus) {
2346 if (!d->engine->hasFeature(feature: QPaintEngine::BlendModes)) {
2347 qWarning(msg: "QPainter::setCompositionMode: "
2348 "Blend modes not supported on device");
2349 return;
2350 }
2351 } else if (!d->engine->hasFeature(feature: QPaintEngine::PorterDuff)) {
2352 if (mode != CompositionMode_Source && mode != CompositionMode_SourceOver) {
2353 qWarning(msg: "QPainter::setCompositionMode: "
2354 "PorterDuff modes not supported on device");
2355 return;
2356 }
2357 }
2358
2359 d->state->composition_mode = mode;
2360 d->state->dirtyFlags |= QPaintEngine::DirtyCompositionMode;
2361}
2362
2363/*!
2364 Returns the current composition mode.
2365
2366 \sa CompositionMode, setCompositionMode()
2367*/
2368QPainter::CompositionMode QPainter::compositionMode() const
2369{
2370 Q_D(const QPainter);
2371 if (!d->engine) {
2372 qWarning(msg: "QPainter::compositionMode: Painter not active");
2373 return QPainter::CompositionMode_SourceOver;
2374 }
2375 return d->state->composition_mode;
2376}
2377
2378/*!
2379 Returns the current background brush.
2380
2381 \sa setBackground(), {QPainter#Settings}{Settings}
2382*/
2383
2384const QBrush &QPainter::background() const
2385{
2386 Q_D(const QPainter);
2387 if (!d->engine) {
2388 qWarning(msg: "QPainter::background: Painter not active");
2389 return d->fakeState()->brush;
2390 }
2391 return d->state->bgBrush;
2392}
2393
2394
2395/*!
2396 Returns \c true if clipping has been set; otherwise returns \c false.
2397
2398 \sa setClipping(), {QPainter#Clipping}{Clipping}
2399*/
2400
2401bool QPainter::hasClipping() const
2402{
2403 Q_D(const QPainter);
2404 if (!d->engine) {
2405 qWarning(msg: "QPainter::hasClipping: Painter not active");
2406 return false;
2407 }
2408 return d->state->clipEnabled && d->state->clipOperation != Qt::NoClip;
2409}
2410
2411
2412/*!
2413 Enables clipping if \a enable is true, or disables clipping if \a
2414 enable is false.
2415
2416 \sa hasClipping(), {QPainter#Clipping}{Clipping}
2417*/
2418
2419void QPainter::setClipping(bool enable)
2420{
2421 Q_D(QPainter);
2422#ifdef QT_DEBUG_DRAW
2423 if (qt_show_painter_debug_output)
2424 printf("QPainter::setClipping(), enable=%s, was=%s\n",
2425 enable ? "on" : "off",
2426 hasClipping() ? "on" : "off");
2427#endif
2428 if (!d->engine) {
2429 qWarning(msg: "QPainter::setClipping: Painter not active, state will be reset by begin");
2430 return;
2431 }
2432
2433 if (hasClipping() == enable)
2434 return;
2435
2436 // we can't enable clipping if we don't have a clip
2437 if (enable
2438 && (d->state->clipInfo.isEmpty() || d->state->clipInfo.constLast().operation == Qt::NoClip))
2439 return;
2440 d->state->clipEnabled = enable;
2441
2442 if (d->extended) {
2443 d->extended->clipEnabledChanged();
2444 return;
2445 }
2446
2447 d->state->dirtyFlags |= QPaintEngine::DirtyClipEnabled;
2448 d->updateState(state&: d->state);
2449}
2450
2451
2452/*!
2453 Returns the currently set clip region. Note that the clip region
2454 is given in logical coordinates.
2455
2456 \warning QPainter does not store the combined clip explicitly as
2457 this is handled by the underlying QPaintEngine, so the path is
2458 recreated on demand and transformed to the current logical
2459 coordinate system. This is potentially an expensive operation.
2460
2461 \sa setClipRegion(), clipPath(), setClipping()
2462*/
2463
2464QRegion QPainter::clipRegion() const
2465{
2466 Q_D(const QPainter);
2467 if (!d->engine) {
2468 qWarning(msg: "QPainter::clipRegion: Painter not active");
2469 return QRegion();
2470 }
2471
2472 QRegion region;
2473 bool lastWasNothing = true;
2474
2475 if (!d->txinv)
2476 const_cast<QPainter *>(this)->d_ptr->updateInvMatrix();
2477
2478 // ### Falcon: Use QPainterPath
2479 for (const QPainterClipInfo &info : std::as_const(t&: d->state->clipInfo)) {
2480 switch (info.clipType) {
2481
2482 case QPainterClipInfo::RegionClip: {
2483 QTransform matrix = (info.matrix * d->invMatrix);
2484 if (lastWasNothing) {
2485 region = info.region * matrix;
2486 lastWasNothing = false;
2487 continue;
2488 }
2489 if (info.operation == Qt::IntersectClip)
2490 region &= info.region * matrix;
2491 else if (info.operation == Qt::NoClip) {
2492 lastWasNothing = true;
2493 region = QRegion();
2494 } else
2495 region = info.region * matrix;
2496 break;
2497 }
2498
2499 case QPainterClipInfo::PathClip: {
2500 QTransform matrix = (info.matrix * d->invMatrix);
2501 if (lastWasNothing) {
2502 region = QRegion((info.path * matrix).toFillPolygon().toPolygon(),
2503 info.path.fillRule());
2504 lastWasNothing = false;
2505 continue;
2506 }
2507 if (info.operation == Qt::IntersectClip) {
2508 region &= QRegion((info.path * matrix).toFillPolygon().toPolygon(),
2509 info.path.fillRule());
2510 } else if (info.operation == Qt::NoClip) {
2511 lastWasNothing = true;
2512 region = QRegion();
2513 } else {
2514 region = QRegion((info.path * matrix).toFillPolygon().toPolygon(),
2515 info.path.fillRule());
2516 }
2517 break;
2518 }
2519
2520 case QPainterClipInfo::RectClip: {
2521 QTransform matrix = (info.matrix * d->invMatrix);
2522 if (lastWasNothing) {
2523 region = QRegion(info.rect) * matrix;
2524 lastWasNothing = false;
2525 continue;
2526 }
2527 if (info.operation == Qt::IntersectClip) {
2528 // Use rect intersection if possible.
2529 if (matrix.type() <= QTransform::TxScale)
2530 region &= matrix.mapRect(info.rect);
2531 else
2532 region &= matrix.map(r: QRegion(info.rect));
2533 } else if (info.operation == Qt::NoClip) {
2534 lastWasNothing = true;
2535 region = QRegion();
2536 } else {
2537 region = QRegion(info.rect) * matrix;
2538 }
2539 break;
2540 }
2541
2542 case QPainterClipInfo::RectFClip: {
2543 QTransform matrix = (info.matrix * d->invMatrix);
2544 if (lastWasNothing) {
2545 region = QRegion(info.rectf.toRect()) * matrix;
2546 lastWasNothing = false;
2547 continue;
2548 }
2549 if (info.operation == Qt::IntersectClip) {
2550 // Use rect intersection if possible.
2551 if (matrix.type() <= QTransform::TxScale)
2552 region &= matrix.mapRect(info.rectf.toRect());
2553 else
2554 region &= matrix.map(r: QRegion(info.rectf.toRect()));
2555 } else if (info.operation == Qt::NoClip) {
2556 lastWasNothing = true;
2557 region = QRegion();
2558 } else {
2559 region = QRegion(info.rectf.toRect()) * matrix;
2560 }
2561 break;
2562 }
2563 }
2564 }
2565
2566 return region;
2567}
2568
2569extern QPainterPath qt_regionToPath(const QRegion &region);
2570
2571/*!
2572 Returns the current clip path in logical coordinates.
2573
2574 \warning QPainter does not store the combined clip explicitly as
2575 this is handled by the underlying QPaintEngine, so the path is
2576 recreated on demand and transformed to the current logical
2577 coordinate system. This is potentially an expensive operation.
2578
2579 \sa setClipPath(), clipRegion(), setClipping()
2580*/
2581QPainterPath QPainter::clipPath() const
2582{
2583 Q_D(const QPainter);
2584
2585 // ### Since we do not support path intersections and path unions yet,
2586 // we just use clipRegion() here...
2587 if (!d->engine) {
2588 qWarning(msg: "QPainter::clipPath: Painter not active");
2589 return QPainterPath();
2590 }
2591
2592 // No clip, return empty
2593 if (d->state->clipInfo.isEmpty()) {
2594 return QPainterPath();
2595 } else {
2596
2597 // Update inverse matrix, used below.
2598 if (!d->txinv)
2599 const_cast<QPainter *>(this)->d_ptr->updateInvMatrix();
2600
2601 // For the simple case avoid conversion.
2602 if (d->state->clipInfo.size() == 1
2603 && d->state->clipInfo.at(i: 0).clipType == QPainterClipInfo::PathClip) {
2604 QTransform matrix = (d->state->clipInfo.at(i: 0).matrix * d->invMatrix);
2605 return d->state->clipInfo.at(i: 0).path * matrix;
2606
2607 } else if (d->state->clipInfo.size() == 1
2608 && d->state->clipInfo.at(i: 0).clipType == QPainterClipInfo::RectClip) {
2609 QTransform matrix = (d->state->clipInfo.at(i: 0).matrix * d->invMatrix);
2610 QPainterPath path;
2611 path.addRect(rect: d->state->clipInfo.at(i: 0).rect);
2612 return path * matrix;
2613 } else {
2614 // Fallback to clipRegion() for now, since we don't have isect/unite for paths
2615 return qt_regionToPath(region: clipRegion());
2616 }
2617 }
2618}
2619
2620/*!
2621 Returns the bounding rectangle of the current clip if there is a clip;
2622 otherwise returns an empty rectangle. Note that the clip region is
2623 given in logical coordinates.
2624
2625 The bounding rectangle is not guaranteed to be tight.
2626
2627 \sa setClipRect(), setClipPath(), setClipRegion()
2628
2629 \since 4.8
2630 */
2631
2632QRectF QPainter::clipBoundingRect() const
2633{
2634 Q_D(const QPainter);
2635
2636 if (!d->engine) {
2637 qWarning(msg: "QPainter::clipBoundingRect: Painter not active");
2638 return QRectF();
2639 }
2640
2641 // Accumulate the bounding box in device space. This is not 100%
2642 // precise, but it fits within the guarantee and it is reasonably
2643 // fast.
2644 QRectF bounds;
2645 bool first = true;
2646 for (const QPainterClipInfo &info : std::as_const(t&: d->state->clipInfo)) {
2647 QRectF r;
2648
2649 if (info.clipType == QPainterClipInfo::RectClip)
2650 r = info.rect;
2651 else if (info.clipType == QPainterClipInfo::RectFClip)
2652 r = info.rectf;
2653 else if (info.clipType == QPainterClipInfo::RegionClip)
2654 r = info.region.boundingRect();
2655 else
2656 r = info.path.boundingRect();
2657
2658 r = info.matrix.mapRect(r);
2659
2660 if (first)
2661 bounds = r;
2662 else if (info.operation == Qt::IntersectClip)
2663 bounds &= r;
2664 first = false;
2665 }
2666
2667
2668 // Map the rectangle back into logical space using the inverse
2669 // matrix.
2670 if (!d->txinv)
2671 const_cast<QPainter *>(this)->d_ptr->updateInvMatrix();
2672
2673 return d->invMatrix.mapRect(bounds);
2674}
2675
2676/*!
2677 \fn void QPainter::setClipRect(const QRectF &rectangle, Qt::ClipOperation operation)
2678
2679 Enables clipping, and sets the clip region to the given \a
2680 rectangle using the given clip \a operation. The default operation
2681 is to replace the current clip rectangle.
2682
2683 Note that the clip rectangle is specified in logical (painter)
2684 coordinates.
2685
2686 \sa clipRegion(), setClipping(), {QPainter#Clipping}{Clipping}
2687*/
2688void QPainter::setClipRect(const QRectF &rect, Qt::ClipOperation op)
2689{
2690 Q_D(QPainter);
2691
2692 if (d->extended) {
2693 if (!d->engine) {
2694 qWarning(msg: "QPainter::setClipRect: Painter not active");
2695 return;
2696 }
2697 bool simplifyClipOp = (paintEngine()->type() != QPaintEngine::Picture);
2698 if (simplifyClipOp && (!d->state->clipEnabled && op != Qt::NoClip))
2699 op = Qt::ReplaceClip;
2700
2701 qreal right = rect.x() + rect.width();
2702 qreal bottom = rect.y() + rect.height();
2703 qreal pts[] = { rect.x(), rect.y(),
2704 right, rect.y(),
2705 right, bottom,
2706 rect.x(), bottom };
2707 QVectorPath vp(pts, 4, nullptr, QVectorPath::RectangleHint);
2708 d->state->clipEnabled = true;
2709 d->extended->clip(path: vp, op);
2710 if (op == Qt::ReplaceClip || op == Qt::NoClip)
2711 d->state->clipInfo.clear();
2712 d->state->clipInfo.append(t: QPainterClipInfo(rect, op, d->state->matrix));
2713 d->state->clipOperation = op;
2714 return;
2715 }
2716
2717 if (qreal(int(rect.top())) == rect.top()
2718 && qreal(int(rect.bottom())) == rect.bottom()
2719 && qreal(int(rect.left())) == rect.left()
2720 && qreal(int(rect.right())) == rect.right())
2721 {
2722 setClipRect(rect.toRect(), op);
2723 return;
2724 }
2725
2726 if (rect.isEmpty()) {
2727 setClipRegion(QRegion(), op);
2728 return;
2729 }
2730
2731 QPainterPath path;
2732 path.addRect(rect);
2733 setClipPath(path, op);
2734}
2735
2736/*!
2737 \fn void QPainter::setClipRect(const QRect &rectangle, Qt::ClipOperation operation)
2738 \overload
2739
2740 Enables clipping, and sets the clip region to the given \a rectangle using the given
2741 clip \a operation.
2742*/
2743void QPainter::setClipRect(const QRect &rect, Qt::ClipOperation op)
2744{
2745 Q_D(QPainter);
2746
2747 if (!d->engine) {
2748 qWarning(msg: "QPainter::setClipRect: Painter not active");
2749 return;
2750 }
2751 bool simplifyClipOp = (paintEngine()->type() != QPaintEngine::Picture);
2752
2753 if (simplifyClipOp && (!d->state->clipEnabled && op != Qt::NoClip))
2754 op = Qt::ReplaceClip;
2755
2756 if (d->extended) {
2757 d->state->clipEnabled = true;
2758 d->extended->clip(rect, op);
2759 if (op == Qt::ReplaceClip || op == Qt::NoClip)
2760 d->state->clipInfo.clear();
2761 d->state->clipInfo.append(t: QPainterClipInfo(rect, op, d->state->matrix));
2762 d->state->clipOperation = op;
2763 return;
2764 }
2765
2766 if (simplifyClipOp && d->state->clipOperation == Qt::NoClip && op == Qt::IntersectClip)
2767 op = Qt::ReplaceClip;
2768
2769 d->state->clipRegion = rect;
2770 d->state->clipOperation = op;
2771 if (op == Qt::NoClip || op == Qt::ReplaceClip)
2772 d->state->clipInfo.clear();
2773 d->state->clipInfo.append(t: QPainterClipInfo(rect, op, d->state->matrix));
2774 d->state->clipEnabled = true;
2775 d->state->dirtyFlags |= QPaintEngine::DirtyClipRegion | QPaintEngine::DirtyClipEnabled;
2776 d->updateState(state&: d->state);
2777}
2778
2779/*!
2780 \fn void QPainter::setClipRect(int x, int y, int width, int height, Qt::ClipOperation operation)
2781
2782 Enables clipping, and sets the clip region to the rectangle beginning at (\a x, \a y)
2783 with the given \a width and \a height.
2784*/
2785
2786/*!
2787 \fn void QPainter::setClipRegion(const QRegion &region, Qt::ClipOperation operation)
2788
2789 Sets the clip region to the given \a region using the specified clip
2790 \a operation. The default clip operation is to replace the current
2791 clip region.
2792
2793 Note that the clip region is given in logical coordinates.
2794
2795 \sa clipRegion(), setClipRect(), {QPainter#Clipping}{Clipping}
2796*/
2797void QPainter::setClipRegion(const QRegion &r, Qt::ClipOperation op)
2798{
2799 Q_D(QPainter);
2800#ifdef QT_DEBUG_DRAW
2801 QRect rect = r.boundingRect();
2802 if (qt_show_painter_debug_output)
2803 printf("QPainter::setClipRegion(), size=%d, [%d,%d,%d,%d]\n",
2804 r.rectCount(), rect.x(), rect.y(), rect.width(), rect.height());
2805#endif
2806 if (!d->engine) {
2807 qWarning(msg: "QPainter::setClipRegion: Painter not active");
2808 return;
2809 }
2810 bool simplifyClipOp = (paintEngine()->type() != QPaintEngine::Picture);
2811
2812 if (simplifyClipOp && (!d->state->clipEnabled && op != Qt::NoClip))
2813 op = Qt::ReplaceClip;
2814
2815 if (d->extended) {
2816 d->state->clipEnabled = true;
2817 d->extended->clip(region: r, op);
2818 if (op == Qt::NoClip || op == Qt::ReplaceClip)
2819 d->state->clipInfo.clear();
2820 d->state->clipInfo.append(t: QPainterClipInfo(r, op, d->state->matrix));
2821 d->state->clipOperation = op;
2822 return;
2823 }
2824
2825 if (simplifyClipOp && d->state->clipOperation == Qt::NoClip && op == Qt::IntersectClip)
2826 op = Qt::ReplaceClip;
2827
2828 d->state->clipRegion = r;
2829 d->state->clipOperation = op;
2830 if (op == Qt::NoClip || op == Qt::ReplaceClip)
2831 d->state->clipInfo.clear();
2832 d->state->clipInfo.append(t: QPainterClipInfo(r, op, d->state->matrix));
2833 d->state->clipEnabled = true;
2834 d->state->dirtyFlags |= QPaintEngine::DirtyClipRegion | QPaintEngine::DirtyClipEnabled;
2835 d->updateState(state&: d->state);
2836}
2837
2838/*!
2839 \since 4.2
2840
2841 Enables transformations if \a enable is true, or disables
2842 transformations if \a enable is false. The world transformation
2843 matrix is not changed.
2844
2845 \sa worldMatrixEnabled(), worldTransform(), {QPainter#Coordinate
2846 Transformations}{Coordinate Transformations}
2847*/
2848
2849void QPainter::setWorldMatrixEnabled(bool enable)
2850{
2851 Q_D(QPainter);
2852#ifdef QT_DEBUG_DRAW
2853 if (qt_show_painter_debug_output)
2854 printf("QPainter::setMatrixEnabled(), enable=%d\n", enable);
2855#endif
2856
2857 if (!d->engine) {
2858 qWarning(msg: "QPainter::setMatrixEnabled: Painter not active");
2859 return;
2860 }
2861 if (enable == d->state->WxF)
2862 return;
2863
2864 d->state->WxF = enable;
2865 d->updateMatrix();
2866}
2867
2868/*!
2869 \since 4.2
2870
2871 Returns \c true if world transformation is enabled; otherwise returns
2872 false.
2873
2874 \sa setWorldMatrixEnabled(), worldTransform(), {Coordinate System}
2875*/
2876
2877bool QPainter::worldMatrixEnabled() const
2878{
2879 Q_D(const QPainter);
2880 if (!d->engine) {
2881 qWarning(msg: "QPainter::worldMatrixEnabled: Painter not active");
2882 return false;
2883 }
2884 return d->state->WxF;
2885}
2886
2887/*!
2888 Scales the coordinate system by (\a{sx}, \a{sy}).
2889
2890 \sa setWorldTransform(), {QPainter#Coordinate Transformations}{Coordinate Transformations}
2891*/
2892
2893void QPainter::scale(qreal sx, qreal sy)
2894{
2895#ifdef QT_DEBUG_DRAW
2896 if (qt_show_painter_debug_output)
2897 printf("QPainter::scale(), sx=%f, sy=%f\n", sx, sy);
2898#endif
2899 Q_D(QPainter);
2900 if (!d->engine) {
2901 qWarning(msg: "QPainter::scale: Painter not active");
2902 return;
2903 }
2904
2905 d->state->worldMatrix.scale(sx,sy);
2906 d->state->WxF = true;
2907 d->updateMatrix();
2908}
2909
2910/*!
2911 Shears the coordinate system by (\a{sh}, \a{sv}).
2912
2913 \sa setWorldTransform(), {QPainter#Coordinate Transformations}{Coordinate Transformations}
2914*/
2915
2916void QPainter::shear(qreal sh, qreal sv)
2917{
2918#ifdef QT_DEBUG_DRAW
2919 if (qt_show_painter_debug_output)
2920 printf("QPainter::shear(), sh=%f, sv=%f\n", sh, sv);
2921#endif
2922 Q_D(QPainter);
2923 if (!d->engine) {
2924 qWarning(msg: "QPainter::shear: Painter not active");
2925 return;
2926 }
2927
2928 d->state->worldMatrix.shear(sh, sv);
2929 d->state->WxF = true;
2930 d->updateMatrix();
2931}
2932
2933/*!
2934 \fn void QPainter::rotate(qreal angle)
2935
2936 Rotates the coordinate system clockwise. The given \a angle parameter is in degrees.
2937
2938 \sa setWorldTransform(), {QPainter#Coordinate Transformations}{Coordinate Transformations}
2939*/
2940
2941void QPainter::rotate(qreal a)
2942{
2943#ifdef QT_DEBUG_DRAW
2944 if (qt_show_painter_debug_output)
2945 printf("QPainter::rotate(), angle=%f\n", a);
2946#endif
2947 Q_D(QPainter);
2948 if (!d->engine) {
2949 qWarning(msg: "QPainter::rotate: Painter not active");
2950 return;
2951 }
2952
2953 d->state->worldMatrix.rotate(a);
2954 d->state->WxF = true;
2955 d->updateMatrix();
2956}
2957
2958/*!
2959 Translates the coordinate system by the given \a offset; i.e. the
2960 given \a offset is added to points.
2961
2962 \sa setWorldTransform(), {QPainter#Coordinate Transformations}{Coordinate Transformations}
2963*/
2964void QPainter::translate(const QPointF &offset)
2965{
2966 qreal dx = offset.x();
2967 qreal dy = offset.y();
2968#ifdef QT_DEBUG_DRAW
2969 if (qt_show_painter_debug_output)
2970 printf("QPainter::translate(), dx=%f, dy=%f\n", dx, dy);
2971#endif
2972 Q_D(QPainter);
2973 if (!d->engine) {
2974 qWarning(msg: "QPainter::translate: Painter not active");
2975 return;
2976 }
2977
2978 d->state->worldMatrix.translate(dx, dy);
2979 d->state->WxF = true;
2980 d->updateMatrix();
2981}
2982
2983/*!
2984 \fn void QPainter::translate(const QPoint &offset)
2985 \overload
2986
2987 Translates the coordinate system by the given \a offset.
2988*/
2989
2990/*!
2991 \fn void QPainter::translate(qreal dx, qreal dy)
2992 \overload
2993
2994 Translates the coordinate system by the vector (\a dx, \a dy).
2995*/
2996
2997/*!
2998 \fn void QPainter::setClipPath(const QPainterPath &path, Qt::ClipOperation operation)
2999
3000 Enables clipping, and sets the clip path for the painter to the
3001 given \a path, with the clip \a operation.
3002
3003 Note that the clip path is specified in logical (painter)
3004 coordinates.
3005
3006 \sa clipPath(), clipRegion(), {QPainter#Clipping}{Clipping}
3007
3008*/
3009void QPainter::setClipPath(const QPainterPath &path, Qt::ClipOperation op)
3010{
3011#ifdef QT_DEBUG_DRAW
3012 if (qt_show_painter_debug_output) {
3013 QRectF b = path.boundingRect();
3014 printf("QPainter::setClipPath(), size=%d, op=%d, bounds=[%.2f,%.2f,%.2f,%.2f]\n",
3015 path.elementCount(), op, b.x(), b.y(), b.width(), b.height());
3016 }
3017#endif
3018 Q_D(QPainter);
3019
3020 if (!d->engine) {
3021 qWarning(msg: "QPainter::setClipPath: Painter not active");
3022 return;
3023 }
3024
3025 bool simplifyClipOp = (paintEngine()->type() != QPaintEngine::Picture);
3026 if (simplifyClipOp && (!d->state->clipEnabled && op != Qt::NoClip))
3027 op = Qt::ReplaceClip;
3028
3029 if (d->extended) {
3030 d->state->clipEnabled = true;
3031 d->extended->clip(path, op);
3032 if (op == Qt::NoClip || op == Qt::ReplaceClip)
3033 d->state->clipInfo.clear();
3034 d->state->clipInfo.append(t: QPainterClipInfo(path, op, d->state->matrix));
3035 d->state->clipOperation = op;
3036 return;
3037 }
3038
3039 if (simplifyClipOp && d->state->clipOperation == Qt::NoClip && op == Qt::IntersectClip)
3040 op = Qt::ReplaceClip;
3041
3042 d->state->clipPath = path;
3043 d->state->clipOperation = op;
3044 if (op == Qt::NoClip || op == Qt::ReplaceClip)
3045 d->state->clipInfo.clear();
3046 d->state->clipInfo.append(t: QPainterClipInfo(path, op, d->state->matrix));
3047 d->state->clipEnabled = true;
3048 d->state->dirtyFlags |= QPaintEngine::DirtyClipPath | QPaintEngine::DirtyClipEnabled;
3049 d->updateState(state&: d->state);
3050}
3051
3052/*!
3053 Draws the outline (strokes) the path \a path with the pen specified
3054 by \a pen
3055
3056 \sa fillPath(), {QPainter#Drawing}{Drawing}
3057*/
3058void QPainter::strokePath(const QPainterPath &path, const QPen &pen)
3059{
3060 Q_D(QPainter);
3061
3062 if (!d->engine) {
3063 qWarning(msg: "QPainter::strokePath: Painter not active");
3064 return;
3065 }
3066
3067 if (path.isEmpty())
3068 return;
3069
3070 if (d->extended && !needsEmulation(brush: pen.brush())) {
3071 d->extended->stroke(path: qtVectorPathForPath(path), pen);
3072 return;
3073 }
3074
3075 QBrush oldBrush = d->state->brush;
3076 QPen oldPen = d->state->pen;
3077
3078 setPen(pen);
3079 setBrush(Qt::NoBrush);
3080
3081 drawPath(path);
3082
3083 // Reset old state
3084 setPen(oldPen);
3085 setBrush(oldBrush);
3086}
3087
3088/*!
3089 Fills the given \a path using the given \a brush. The outline is
3090 not drawn.
3091
3092 Alternatively, you can specify a QColor instead of a QBrush; the
3093 QBrush constructor (taking a QColor argument) will automatically
3094 create a solid pattern brush.
3095
3096 \sa drawPath()
3097*/
3098void QPainter::fillPath(const QPainterPath &path, const QBrush &brush)
3099{
3100 Q_D(QPainter);
3101
3102 if (!d->engine) {
3103 qWarning(msg: "QPainter::fillPath: Painter not active");
3104 return;
3105 }
3106
3107 if (path.isEmpty())
3108 return;
3109
3110 if (d->extended && !needsEmulation(brush)) {
3111 d->extended->fill(path: qtVectorPathForPath(path), brush);
3112 return;
3113 }
3114
3115 QBrush oldBrush = d->state->brush;
3116 QPen oldPen = d->state->pen;
3117
3118 setPen(Qt::NoPen);
3119 setBrush(brush);
3120
3121 drawPath(path);
3122
3123 // Reset old state
3124 setPen(oldPen);
3125 setBrush(oldBrush);
3126}
3127
3128/*!
3129 Draws the given painter \a path using the current pen for outline
3130 and the current brush for filling.
3131
3132 \table 100%
3133 \row
3134 \li \inlineimage qpainter-path.png
3135 \li
3136 \snippet code/src_gui_painting_qpainter.cpp 5
3137 \endtable
3138
3139 \sa {painting/painterpaths}{the Painter Paths
3140 example},{painting/deform}{the Vector Deformation example}
3141*/
3142void QPainter::drawPath(const QPainterPath &path)
3143{
3144#ifdef QT_DEBUG_DRAW
3145 QRectF pathBounds = path.boundingRect();
3146 if (qt_show_painter_debug_output)
3147 printf("QPainter::drawPath(), size=%d, [%.2f,%.2f,%.2f,%.2f]\n",
3148 path.elementCount(),
3149 pathBounds.x(), pathBounds.y(), pathBounds.width(), pathBounds.height());
3150#endif
3151
3152 Q_D(QPainter);
3153
3154 if (!d->engine) {
3155 qWarning(msg: "QPainter::drawPath: Painter not active");
3156 return;
3157 }
3158
3159 if (d->extended) {
3160 d->extended->drawPath(path);
3161 return;
3162 }
3163 d->updateState(state&: d->state);
3164
3165 if (d->engine->hasFeature(feature: QPaintEngine::PainterPaths) && d->state->emulationSpecifier == 0) {
3166 d->engine->drawPath(path);
3167 } else {
3168 d->draw_helper(originalPath: path);
3169 }
3170}
3171
3172/*!
3173 \fn void QPainter::drawLine(const QLineF &line)
3174
3175 Draws a line defined by \a line.
3176
3177 \table 100%
3178 \row
3179 \li \inlineimage qpainter-line.png
3180 \li
3181 \snippet code/src_gui_painting_qpainter.cpp 6
3182 \endtable
3183
3184 \sa drawLines(), drawPolyline(), {Coordinate System}
3185*/
3186
3187/*!
3188 \fn void QPainter::drawLine(const QLine &line)
3189 \overload
3190
3191 Draws a line defined by \a line.
3192*/
3193
3194/*!
3195 \fn void QPainter::drawLine(const QPoint &p1, const QPoint &p2)
3196 \overload
3197
3198 Draws a line from \a p1 to \a p2.
3199*/
3200
3201/*!
3202 \fn void QPainter::drawLine(const QPointF &p1, const QPointF &p2)
3203 \overload
3204
3205 Draws a line from \a p1 to \a p2.
3206*/
3207
3208/*!
3209 \fn void QPainter::drawLine(int x1, int y1, int x2, int y2)
3210 \overload
3211
3212 Draws a line from (\a x1, \a y1) to (\a x2, \a y2).
3213*/
3214
3215/*!
3216 \fn void QPainter::drawRect(const QRectF &rectangle)
3217
3218 Draws the current \a rectangle with the current pen and brush.
3219
3220 A filled rectangle has a size of \a{rectangle}.size(). A stroked
3221 rectangle has a size of \a{rectangle}.size() plus the pen width.
3222
3223 \table 100%
3224 \row
3225 \li \inlineimage qpainter-rectangle.png
3226 \li
3227 \snippet code/src_gui_painting_qpainter.cpp 7
3228 \endtable
3229
3230 \sa drawRects(), drawPolygon(), {Coordinate System}
3231*/
3232
3233/*!
3234 \fn void QPainter::drawRect(const QRect &rectangle)
3235
3236 \overload
3237
3238 Draws the current \a rectangle with the current pen and brush.
3239*/
3240
3241/*!
3242 \fn void QPainter::drawRect(int x, int y, int width, int height)
3243
3244 \overload
3245
3246 Draws a rectangle with upper left corner at (\a{x}, \a{y}) and
3247 with the given \a width and \a height.
3248*/
3249
3250/*!
3251 \fn void QPainter::drawRects(const QRectF *rectangles, int rectCount)
3252
3253 Draws the first \a rectCount of the given \a rectangles using the
3254 current pen and brush.
3255
3256 \sa drawRect()
3257*/
3258void QPainter::drawRects(const QRectF *rects, int rectCount)
3259{
3260#ifdef QT_DEBUG_DRAW
3261 if (qt_show_painter_debug_output)
3262 printf("QPainter::drawRects(), count=%d\n", rectCount);
3263#endif
3264 Q_D(QPainter);
3265
3266 if (!d->engine) {
3267 qWarning(msg: "QPainter::drawRects: Painter not active");
3268 return;
3269 }
3270
3271 if (rectCount <= 0)
3272 return;
3273
3274 if (d->extended) {
3275 d->extended->drawRects(rects, rectCount);
3276 return;
3277 }
3278
3279 d->updateState(state&: d->state);
3280
3281 if (!d->state->emulationSpecifier) {
3282 d->engine->drawRects(rects, rectCount);
3283 return;
3284 }
3285
3286 if (d->state->emulationSpecifier == QPaintEngine::PrimitiveTransform
3287 && d->state->matrix.type() == QTransform::TxTranslate) {
3288 for (int i=0; i<rectCount; ++i) {
3289 QRectF r(rects[i].x() + d->state->matrix.dx(),
3290 rects[i].y() + d->state->matrix.dy(),
3291 rects[i].width(),
3292 rects[i].height());
3293 d->engine->drawRects(rects: &r, rectCount: 1);
3294 }
3295 } else {
3296 if (d->state->brushNeedsResolving() || d->state->penNeedsResolving()) {
3297 for (int i=0; i<rectCount; ++i) {
3298 QPainterPath rectPath;
3299 rectPath.addRect(rect: rects[i]);
3300 d->draw_helper(originalPath: rectPath, op: QPainterPrivate::StrokeAndFillDraw);
3301 }
3302 } else {
3303 QPainterPath rectPath;
3304 for (int i=0; i<rectCount; ++i)
3305 rectPath.addRect(rect: rects[i]);
3306 d->draw_helper(originalPath: rectPath, op: QPainterPrivate::StrokeAndFillDraw);
3307 }
3308 }
3309}
3310
3311/*!
3312 \fn void QPainter::drawRects(const QRect *rectangles, int rectCount)
3313 \overload
3314
3315 Draws the first \a rectCount of the given \a rectangles using the
3316 current pen and brush.
3317*/
3318void QPainter::drawRects(const QRect *rects, int rectCount)
3319{
3320#ifdef QT_DEBUG_DRAW
3321 if (qt_show_painter_debug_output)
3322 printf("QPainter::drawRects(), count=%d\n", rectCount);
3323#endif
3324 Q_D(QPainter);
3325
3326 if (!d->engine) {
3327 qWarning(msg: "QPainter::drawRects: Painter not active");
3328 return;
3329 }
3330
3331 if (rectCount <= 0)
3332 return;
3333
3334 if (d->extended) {
3335 d->extended->drawRects(rects, rectCount);
3336 return;
3337 }
3338
3339 d->updateState(state&: d->state);
3340
3341 if (!d->state->emulationSpecifier) {
3342 d->engine->drawRects(rects, rectCount);
3343 return;
3344 }
3345
3346 if (d->state->emulationSpecifier == QPaintEngine::PrimitiveTransform
3347 && d->state->matrix.type() == QTransform::TxTranslate) {
3348 for (int i=0; i<rectCount; ++i) {
3349 QRectF r(rects[i].x() + d->state->matrix.dx(),
3350 rects[i].y() + d->state->matrix.dy(),
3351 rects[i].width(),
3352 rects[i].height());
3353
3354 d->engine->drawRects(rects: &r, rectCount: 1);
3355 }
3356 } else {
3357 if (d->state->brushNeedsResolving() || d->state->penNeedsResolving()) {
3358 for (int i=0; i<rectCount; ++i) {
3359 QPainterPath rectPath;
3360 rectPath.addRect(rect: rects[i]);
3361 d->draw_helper(originalPath: rectPath, op: QPainterPrivate::StrokeAndFillDraw);
3362 }
3363 } else {
3364 QPainterPath rectPath;
3365 for (int i=0; i<rectCount; ++i)
3366 rectPath.addRect(rect: rects[i]);
3367
3368 d->draw_helper(originalPath: rectPath, op: QPainterPrivate::StrokeAndFillDraw);
3369 }
3370 }
3371}
3372
3373/*!
3374 \fn void QPainter::drawRects(const QList<QRectF> &rectangles)
3375 \overload
3376
3377 Draws the given \a rectangles using the current pen and brush.
3378*/
3379
3380/*!
3381 \fn void QPainter::drawRects(const QList<QRect> &rectangles)
3382
3383 \overload
3384
3385 Draws the given \a rectangles using the current pen and brush.
3386*/
3387
3388/*!
3389 \fn void QPainter::drawPoint(const QPointF &position)
3390
3391 Draws a single point at the given \a position using the current
3392 pen's color.
3393
3394 \sa {Coordinate System}
3395*/
3396
3397/*!
3398 \fn void QPainter::drawPoint(const QPoint &position)
3399 \overload
3400
3401 Draws a single point at the given \a position using the current
3402 pen's color.
3403*/
3404
3405/*! \fn void QPainter::drawPoint(int x, int y)
3406
3407 \overload
3408
3409 Draws a single point at position (\a x, \a y).
3410*/
3411
3412/*!
3413 Draws the first \a pointCount points in the array \a points using
3414 the current pen's color.
3415
3416 \sa {Coordinate System}
3417*/
3418void QPainter::drawPoints(const QPointF *points, int pointCount)
3419{
3420#ifdef QT_DEBUG_DRAW
3421 if (qt_show_painter_debug_output)
3422 printf("QPainter::drawPoints(), count=%d\n", pointCount);
3423#endif
3424 Q_D(QPainter);
3425
3426 if (!d->engine) {
3427 qWarning(msg: "QPainter::drawPoints: Painter not active");
3428 return;
3429 }
3430
3431 if (pointCount <= 0)
3432 return;
3433
3434 if (d->extended) {
3435 d->extended->drawPoints(points, pointCount);
3436 return;
3437 }
3438
3439 d->updateState(state&: d->state);
3440
3441 if (!d->state->emulationSpecifier) {
3442 d->engine->drawPoints(points, pointCount);
3443 return;
3444 }
3445
3446 if (d->state->emulationSpecifier == QPaintEngine::PrimitiveTransform
3447 && d->state->matrix.type() == QTransform::TxTranslate) {
3448 // ### use drawPoints function
3449 for (int i=0; i<pointCount; ++i) {
3450 QPointF pt(points[i].x() + d->state->matrix.dx(),
3451 points[i].y() + d->state->matrix.dy());
3452 d->engine->drawPoints(points: &pt, pointCount: 1);
3453 }
3454 } else {
3455 QPen pen = d->state->pen;
3456 bool flat_pen = pen.capStyle() == Qt::FlatCap;
3457 if (flat_pen) {
3458 save();
3459 pen.setCapStyle(Qt::SquareCap);
3460 setPen(pen);
3461 }
3462 QPainterPath path;
3463 for (int i=0; i<pointCount; ++i) {
3464 path.moveTo(x: points[i].x(), y: points[i].y());
3465 path.lineTo(x: points[i].x() + 0.0001, y: points[i].y());
3466 }
3467 d->draw_helper(originalPath: path, op: QPainterPrivate::StrokeDraw);
3468 if (flat_pen)
3469 restore();
3470 }
3471}
3472
3473/*!
3474 \overload
3475
3476 Draws the first \a pointCount points in the array \a points using
3477 the current pen's color.
3478*/
3479
3480void QPainter::drawPoints(const QPoint *points, int pointCount)
3481{
3482#ifdef QT_DEBUG_DRAW
3483 if (qt_show_painter_debug_output)
3484 printf("QPainter::drawPoints(), count=%d\n", pointCount);
3485#endif
3486 Q_D(QPainter);
3487
3488 if (!d->engine) {
3489 qWarning(msg: "QPainter::drawPoints: Painter not active");
3490 return;
3491 }
3492
3493 if (pointCount <= 0)
3494 return;
3495
3496 if (d->extended) {
3497 d->extended->drawPoints(points, pointCount);
3498 return;
3499 }
3500
3501 d->updateState(state&: d->state);
3502
3503 if (!d->state->emulationSpecifier) {
3504 d->engine->drawPoints(points, pointCount);
3505 return;
3506 }
3507
3508 if (d->state->emulationSpecifier == QPaintEngine::PrimitiveTransform
3509 && d->state->matrix.type() == QTransform::TxTranslate) {
3510 // ### use drawPoints function
3511 for (int i=0; i<pointCount; ++i) {
3512 QPointF pt(points[i].x() + d->state->matrix.dx(),
3513 points[i].y() + d->state->matrix.dy());
3514 d->engine->drawPoints(points: &pt, pointCount: 1);
3515 }
3516 } else {
3517 QPen pen = d->state->pen;
3518 bool flat_pen = (pen.capStyle() == Qt::FlatCap);
3519 if (flat_pen) {
3520 save();
3521 pen.setCapStyle(Qt::SquareCap);
3522 setPen(pen);
3523 }
3524 QPainterPath path;
3525 for (int i=0; i<pointCount; ++i) {
3526 path.moveTo(x: points[i].x(), y: points[i].y());
3527 path.lineTo(x: points[i].x() + 0.0001, y: points[i].y());
3528 }
3529 d->draw_helper(originalPath: path, op: QPainterPrivate::StrokeDraw);
3530 if (flat_pen)
3531 restore();
3532 }
3533}
3534
3535/*!
3536 \fn void QPainter::drawPoints(const QPolygonF &points)
3537
3538 \overload
3539
3540 Draws the points in the vector \a points.
3541*/
3542
3543/*!
3544 \fn void QPainter::drawPoints(const QPolygon &points)
3545
3546 \overload
3547
3548 Draws the points in the vector \a points.
3549*/
3550
3551/*!
3552 Sets the background mode of the painter to the given \a mode
3553
3554 Qt::TransparentMode (the default) draws stippled lines and text
3555 without setting the background pixels. Qt::OpaqueMode fills these
3556 space with the current background color.
3557
3558 Note that in order to draw a bitmap or pixmap transparently, you
3559 must use QPixmap::setMask().
3560
3561 \sa backgroundMode(), setBackground(),
3562 {QPainter#Settings}{Settings}
3563*/
3564
3565void QPainter::setBackgroundMode(Qt::BGMode mode)
3566{
3567#ifdef QT_DEBUG_DRAW
3568 if (qt_show_painter_debug_output)
3569 printf("QPainter::setBackgroundMode(), mode=%d\n", mode);
3570#endif
3571
3572 Q_D(QPainter);
3573 if (!d->engine) {
3574 qWarning(msg: "QPainter::setBackgroundMode: Painter not active");
3575 return;
3576 }
3577 if (d->state->bgMode == mode)
3578 return;
3579
3580 d->state->bgMode = mode;
3581 if (d->extended) {
3582 d->checkEmulation();
3583 } else {
3584 d->state->dirtyFlags |= QPaintEngine::DirtyBackgroundMode;
3585 }
3586}
3587
3588/*!
3589 Returns the current background mode.
3590
3591 \sa setBackgroundMode(), {QPainter#Settings}{Settings}
3592*/
3593Qt::BGMode QPainter::backgroundMode() const
3594{
3595 Q_D(const QPainter);
3596 if (!d->engine) {
3597 qWarning(msg: "QPainter::backgroundMode: Painter not active");
3598 return Qt::TransparentMode;
3599 }
3600 return d->state->bgMode;
3601}
3602
3603
3604/*!
3605 \overload
3606
3607 Sets the painter's pen to have style Qt::SolidLine, width 1 and the
3608 specified \a color.
3609*/
3610
3611void QPainter::setPen(const QColor &color)
3612{
3613#ifdef QT_DEBUG_DRAW
3614 if (qt_show_painter_debug_output)
3615 printf("QPainter::setPen(), color=%04x\n", color.rgb());
3616#endif
3617 Q_D(QPainter);
3618 if (!d->engine) {
3619 qWarning(msg: "QPainter::setPen: Painter not active");
3620 return;
3621 }
3622
3623 QPen pen(color.isValid() ? color : QColor(Qt::black));
3624
3625 if (d->state->pen == pen)
3626 return;
3627
3628 d->state->pen = pen;
3629 if (d->extended)
3630 d->extended->penChanged();
3631 else
3632 d->state->dirtyFlags |= QPaintEngine::DirtyPen;
3633}
3634
3635/*!
3636 Sets the painter's pen to be the given \a pen.
3637
3638 The \a pen defines how to draw lines and outlines, and it also
3639 defines the text color.
3640
3641 \sa pen(), {QPainter#Settings}{Settings}
3642*/
3643
3644void QPainter::setPen(const QPen &pen)
3645{
3646
3647#ifdef QT_DEBUG_DRAW
3648 if (qt_show_painter_debug_output)
3649 printf("QPainter::setPen(), color=%04x, (brushStyle=%d) style=%d, cap=%d, join=%d\n",
3650 pen.color().rgb(), pen.brush().style(), pen.style(), pen.capStyle(), pen.joinStyle());
3651#endif
3652 Q_D(QPainter);
3653 if (!d->engine) {
3654 qWarning(msg: "QPainter::setPen: Painter not active");
3655 return;
3656 }
3657
3658 if (d->state->pen == pen)
3659 return;
3660
3661 d->state->pen = pen;
3662
3663 if (d->extended) {
3664 d->checkEmulation();
3665 d->extended->penChanged();
3666 return;
3667 }
3668
3669 d->state->dirtyFlags |= QPaintEngine::DirtyPen;
3670}
3671
3672/*!
3673 \overload
3674
3675 Sets the painter's pen to have the given \a style, width 1 and
3676 black color.
3677*/
3678
3679void QPainter::setPen(Qt::PenStyle style)
3680{
3681 Q_D(QPainter);
3682 if (!d->engine) {
3683 qWarning(msg: "QPainter::setPen: Painter not active");
3684 return;
3685 }
3686
3687 QPen pen = QPen(style);
3688
3689 if (d->state->pen == pen)
3690 return;
3691
3692 d->state->pen = pen;
3693
3694 if (d->extended)
3695 d->extended->penChanged();
3696 else
3697 d->state->dirtyFlags |= QPaintEngine::DirtyPen;
3698
3699}
3700
3701/*!
3702 Returns the painter's current pen.
3703
3704 \sa setPen(), {QPainter#Settings}{Settings}
3705*/
3706
3707const QPen &QPainter::pen() const
3708{
3709 Q_D(const QPainter);
3710 if (!d->engine) {
3711 qWarning(msg: "QPainter::pen: Painter not active");
3712 return d->fakeState()->pen;
3713 }
3714 return d->state->pen;
3715}
3716
3717
3718/*!
3719 Sets the painter's brush to the given \a brush.
3720
3721 The painter's brush defines how shapes are filled.
3722
3723 \sa brush(), {QPainter#Settings}{Settings}
3724*/
3725
3726void QPainter::setBrush(const QBrush &brush)
3727{
3728#ifdef QT_DEBUG_DRAW
3729 if (qt_show_painter_debug_output)
3730 printf("QPainter::setBrush(), color=%04x, style=%d\n", brush.color().rgb(), brush.style());
3731#endif
3732 Q_D(QPainter);
3733 if (!d->engine) {
3734 qWarning(msg: "QPainter::setBrush: Painter not active");
3735 return;
3736 }
3737
3738 if (d->state->brush.d == brush.d)
3739 return;
3740
3741 if (d->extended) {
3742 d->state->brush = brush;
3743 d->checkEmulation();
3744 d->extended->brushChanged();
3745 return;
3746 }
3747
3748 d->state->brush = brush;
3749 d->state->dirtyFlags |= QPaintEngine::DirtyBrush;
3750}
3751
3752
3753/*!
3754 \overload
3755
3756 Sets the painter's brush to black color and the specified \a
3757 style.
3758*/
3759
3760void QPainter::setBrush(Qt::BrushStyle style)
3761{
3762 Q_D(QPainter);
3763 if (!d->engine) {
3764 qWarning(msg: "QPainter::setBrush: Painter not active");
3765 return;
3766 }
3767 if (d->state->brush.style() == style &&
3768 (style == Qt::NoBrush
3769 || (style == Qt::SolidPattern && d->state->brush.color() == QColor(0, 0, 0))))
3770 return;
3771 d->state->brush = QBrush(Qt::black, style);
3772 if (d->extended)
3773 d->extended->brushChanged();
3774 else
3775 d->state->dirtyFlags |= QPaintEngine::DirtyBrush;
3776}
3777
3778/*!
3779 Returns the painter's current brush.
3780
3781 \sa QPainter::setBrush(), {QPainter#Settings}{Settings}
3782*/
3783
3784const QBrush &QPainter::brush() const
3785{
3786 Q_D(const QPainter);
3787 if (!d->engine) {
3788 qWarning(msg: "QPainter::brush: Painter not active");
3789 return d->fakeState()->brush;
3790 }
3791 return d->state->brush;
3792}
3793
3794/*!
3795 \fn void QPainter::setBackground(const QBrush &brush)
3796
3797 Sets the background brush of the painter to the given \a brush.
3798
3799 The background brush is the brush that is filled in when drawing
3800 opaque text, stippled lines and bitmaps. The background brush has
3801 no effect in transparent background mode (which is the default).
3802
3803 \sa background(), setBackgroundMode(),
3804 {QPainter#Settings}{Settings}
3805*/
3806
3807void QPainter::setBackground(const QBrush &bg)
3808{
3809#ifdef QT_DEBUG_DRAW
3810 if (qt_show_painter_debug_output)
3811 printf("QPainter::setBackground(), color=%04x, style=%d\n", bg.color().rgb(), bg.style());
3812#endif
3813
3814 Q_D(QPainter);
3815 if (!d->engine) {
3816 qWarning(msg: "QPainter::setBackground: Painter not active");
3817 return;
3818 }
3819 d->state->bgBrush = bg;
3820 if (!d->extended)
3821 d->state->dirtyFlags |= QPaintEngine::DirtyBackground;
3822}
3823
3824/*!
3825 Sets the painter's font to the given \a font.
3826
3827 This font is used by subsequent drawText() functions. The text
3828 color is the same as the pen color.
3829
3830 If you set a font that isn't available, Qt finds a close match.
3831 font() will return what you set using setFont() and fontInfo() returns the
3832 font actually being used (which may be the same).
3833
3834 \sa font(), drawText(), {QPainter#Settings}{Settings}
3835*/
3836
3837void QPainter::setFont(const QFont &font)
3838{
3839 Q_D(QPainter);
3840
3841#ifdef QT_DEBUG_DRAW
3842 if (qt_show_painter_debug_output)
3843 printf("QPainter::setFont(), family=%s, pointSize=%d\n", font.families().first().toLatin1().constData(), font.pointSize());
3844#endif
3845
3846 if (!d->engine) {
3847 qWarning(msg: "QPainter::setFont: Painter not active");
3848 return;
3849 }
3850
3851 d->state->font = QFont(font.resolve(d->state->deviceFont), device());
3852 if (!d->extended)
3853 d->state->dirtyFlags |= QPaintEngine::DirtyFont;
3854}
3855
3856/*!
3857 Returns the currently set font used for drawing text.
3858
3859 \sa setFont(), drawText(), {QPainter#Settings}{Settings}
3860*/
3861const QFont &QPainter::font() const
3862{
3863 Q_D(const QPainter);
3864 if (!d->engine) {
3865 qWarning(msg: "QPainter::font: Painter not active");
3866 return d->fakeState()->font;
3867 }
3868 return d->state->font;
3869}
3870
3871/*!
3872 \since 4.4
3873
3874 Draws the given rectangle \a rect with rounded corners.
3875
3876 The \a xRadius and \a yRadius arguments specify the radii
3877 of the ellipses defining the corners of the rounded rectangle.
3878 When \a mode is Qt::RelativeSize, \a xRadius and
3879 \a yRadius are specified in percentage of half the rectangle's
3880 width and height respectively, and should be in the range
3881 0.0 to 100.0.
3882
3883 A filled rectangle has a size of rect.size(). A stroked rectangle
3884 has a size of rect.size() plus the pen width.
3885
3886 \table 100%
3887 \row
3888 \li \inlineimage qpainter-roundrect.png
3889 \li
3890 \snippet code/src_gui_painting_qpainter.cpp 8
3891 \endtable
3892
3893 \sa drawRect(), QPen
3894*/
3895void QPainter::drawRoundedRect(const QRectF &rect, qreal xRadius, qreal yRadius, Qt::SizeMode mode)
3896{
3897#ifdef QT_DEBUG_DRAW
3898 if (qt_show_painter_debug_output)
3899 printf("QPainter::drawRoundedRect(), [%.2f,%.2f,%.2f,%.2f]\n", rect.x(), rect.y(), rect.width(), rect.height());
3900#endif
3901 Q_D(QPainter);
3902
3903 if (!d->engine) {
3904 qWarning(msg: "QPainter::drawRoundedRect: Painter not active");
3905 return;
3906 }
3907
3908 if (xRadius <= 0 || yRadius <= 0) { // draw normal rectangle
3909 drawRect(rect);
3910 return;
3911 }
3912
3913 if (d->extended) {
3914 d->extended->drawRoundedRect(rect, xrad: xRadius, yrad: yRadius, mode);
3915 return;
3916 }
3917
3918 QPainterPath path;
3919 path.addRoundedRect(rect, xRadius, yRadius, mode);
3920 drawPath(path);
3921}
3922
3923/*!
3924 \fn void QPainter::drawRoundedRect(const QRect &rect, qreal xRadius, qreal yRadius,
3925 Qt::SizeMode mode = Qt::AbsoluteSize);
3926 \since 4.4
3927 \overload
3928
3929 Draws the given rectangle \a rect with rounded corners.
3930*/
3931
3932/*!
3933 \fn void QPainter::drawRoundedRect(int x, int y, int w, int h, qreal xRadius, qreal yRadius,
3934 Qt::SizeMode mode = Qt::AbsoluteSize);
3935 \since 4.4
3936 \overload
3937
3938 Draws the given rectangle \a x, \a y, \a w, \a h with rounded corners.
3939*/
3940
3941/*!
3942 \fn void QPainter::drawEllipse(const QRectF &rectangle)
3943
3944 Draws the ellipse defined by the given \a rectangle.
3945
3946 A filled ellipse has a size of \a{rectangle}.\l
3947 {QRect::size()}{size()}. A stroked ellipse has a size of
3948 \a{rectangle}.\l {QRect::size()}{size()} plus the pen width.
3949
3950 \table 100%
3951 \row
3952 \li \inlineimage qpainter-ellipse.png
3953 \li
3954 \snippet code/src_gui_painting_qpainter.cpp 9
3955 \endtable
3956
3957 \sa drawPie(), {Coordinate System}
3958*/
3959void QPainter::drawEllipse(const QRectF &r)
3960{
3961#ifdef QT_DEBUG_DRAW
3962 if (qt_show_painter_debug_output)
3963 printf("QPainter::drawEllipse(), [%.2f,%.2f,%.2f,%.2f]\n", r.x(), r.y(), r.width(), r.height());
3964#endif
3965 Q_D(QPainter);
3966
3967 if (!d->engine) {
3968 qWarning(msg: "QPainter::drawEllipse: Painter not active");
3969 return;
3970 }
3971
3972 QRectF rect(r.normalized());
3973
3974 if (d->extended) {
3975 d->extended->drawEllipse(r: rect);
3976 return;
3977 }
3978
3979 d->updateState(state&: d->state);
3980 if (d->state->emulationSpecifier) {
3981 if (d->state->emulationSpecifier == QPaintEngine::PrimitiveTransform
3982 && d->state->matrix.type() == QTransform::TxTranslate) {
3983 rect.translate(p: QPointF(d->state->matrix.dx(), d->state->matrix.dy()));
3984 } else {
3985 QPainterPath path;
3986 path.addEllipse(rect);
3987 d->draw_helper(originalPath: path, op: QPainterPrivate::StrokeAndFillDraw);
3988 return;
3989 }
3990 }
3991
3992 d->engine->drawEllipse(r: rect);
3993}
3994
3995/*!
3996 \fn void QPainter::drawEllipse(const QRect &rectangle)
3997
3998 \overload
3999
4000 Draws the ellipse defined by the given \a rectangle.
4001*/
4002void QPainter::drawEllipse(const QRect &r)
4003{
4004#ifdef QT_DEBUG_DRAW
4005 if (qt_show_painter_debug_output)
4006 printf("QPainter::drawEllipse(), [%d,%d,%d,%d]\n", r.x(), r.y(), r.width(), r.height());
4007#endif
4008 Q_D(QPainter);
4009
4010 if (!d->engine) {
4011 qWarning(msg: "QPainter::drawEllipse: Painter not active");
4012 return;
4013 }
4014
4015 QRect rect(r.normalized());
4016
4017 if (d->extended) {
4018 d->extended->drawEllipse(r: rect);
4019 return;
4020 }
4021
4022 d->updateState(state&: d->state);
4023
4024 if (d->state->emulationSpecifier) {
4025 if (d->state->emulationSpecifier == QPaintEngine::PrimitiveTransform
4026 && d->state->matrix.type() == QTransform::TxTranslate) {
4027 rect.translate(p: QPoint(qRound(d: d->state->matrix.dx()), qRound(d: d->state->matrix.dy())));
4028 } else {
4029 QPainterPath path;
4030 path.addEllipse(rect);
4031 d->draw_helper(originalPath: path, op: QPainterPrivate::StrokeAndFillDraw);
4032 return;
4033 }
4034 }
4035
4036 d->engine->drawEllipse(r: rect);
4037}
4038
4039/*!
4040 \fn void QPainter::drawEllipse(int x, int y, int width, int height)
4041
4042 \overload
4043
4044 Draws the ellipse defined by the rectangle beginning at (\a{x},
4045 \a{y}) with the given \a width and \a height.
4046*/
4047
4048/*!
4049 \since 4.4
4050
4051 \fn void QPainter::drawEllipse(const QPointF &center, qreal rx, qreal ry)
4052
4053 \overload
4054
4055 Draws the ellipse positioned at \a{center} with radii \a{rx} and \a{ry}.
4056*/
4057
4058/*!
4059 \since 4.4
4060
4061 \fn void QPainter::drawEllipse(const QPoint &center, int rx, int ry)
4062
4063 \overload
4064
4065 Draws the ellipse positioned at \a{center} with radii \a{rx} and \a{ry}.
4066*/
4067
4068/*!
4069 \fn void QPainter::drawArc(const QRectF &rectangle, int startAngle, int spanAngle)
4070
4071 Draws the arc defined by the given \a rectangle, \a startAngle and
4072 \a spanAngle.
4073
4074 The \a startAngle and \a spanAngle must be specified in 1/16th of
4075 a degree, i.e. a full circle equals 5760 (16 * 360). Positive
4076 values for the angles mean counter-clockwise while negative values
4077 mean the clockwise direction. Zero degrees is at the 3 o'clock
4078 position.
4079
4080 \table 100%
4081 \row
4082 \li \inlineimage qpainter-arc.png
4083 \li
4084 \snippet code/src_gui_painting_qpainter.cpp 10
4085 \endtable
4086
4087 \sa drawPie(), drawChord(), {Coordinate System}
4088*/
4089
4090void QPainter::drawArc(const QRectF &r, int a, int alen)
4091{
4092#ifdef QT_DEBUG_DRAW
4093 if (qt_show_painter_debug_output)
4094 printf("QPainter::drawArc(), [%.2f,%.2f,%.2f,%.2f], angle=%d, sweep=%d\n",
4095 r.x(), r.y(), r.width(), r.height(), a/16, alen/16);
4096#endif
4097 Q_D(QPainter);
4098
4099 if (!d->engine) {
4100 qWarning(msg: "QPainter::drawArc: Painter not active");
4101 return;
4102 }
4103
4104 QRectF rect = r.normalized();
4105
4106 QPainterPath path;
4107 path.arcMoveTo(rect, angle: a/16.0);
4108 path.arcTo(rect, startAngle: a/16.0, arcLength: alen/16.0);
4109 strokePath(path, pen: d->state->pen);
4110}
4111
4112/*! \fn void QPainter::drawArc(const QRect &rectangle, int startAngle,
4113 int spanAngle)
4114
4115 \overload
4116
4117 Draws the arc defined by the given \a rectangle, \a startAngle and
4118 \a spanAngle.
4119*/
4120
4121/*!
4122 \fn void QPainter::drawArc(int x, int y, int width, int height,
4123 int startAngle, int spanAngle)
4124
4125 \overload
4126
4127 Draws the arc defined by the rectangle beginning at (\a x, \a y)
4128 with the specified \a width and \a height, and the given \a
4129 startAngle and \a spanAngle.
4130*/
4131
4132/*!
4133 \fn void QPainter::drawPie(const QRectF &rectangle, int startAngle, int spanAngle)
4134
4135 Draws a pie defined by the given \a rectangle, \a startAngle and \a spanAngle.
4136
4137 The pie is filled with the current brush().
4138
4139 The startAngle and spanAngle must be specified in 1/16th of a
4140 degree, i.e. a full circle equals 5760 (16 * 360). Positive values
4141 for the angles mean counter-clockwise while negative values mean
4142 the clockwise direction. Zero degrees is at the 3 o'clock
4143 position.
4144
4145 \table 100%
4146 \row
4147 \li \inlineimage qpainter-pie.png
4148 \li
4149 \snippet code/src_gui_painting_qpainter.cpp 11
4150 \endtable
4151
4152 \sa drawEllipse(), drawChord(), {Coordinate System}
4153*/
4154void QPainter::drawPie(const QRectF &r, int a, int alen)
4155{
4156#ifdef QT_DEBUG_DRAW
4157 if (qt_show_painter_debug_output)
4158 printf("QPainter::drawPie(), [%.2f,%.2f,%.2f,%.2f], angle=%d, sweep=%d\n",
4159 r.x(), r.y(), r.width(), r.height(), a/16, alen/16);
4160#endif
4161 Q_D(QPainter);
4162
4163 if (!d->engine) {
4164 qWarning(msg: "QPainter::drawPie: Painter not active");
4165 return;
4166 }
4167
4168 if (a > (360*16)) {
4169 a = a % (360*16);
4170 } else if (a < 0) {
4171 a = a % (360*16);
4172 if (a < 0) a += (360*16);
4173 }
4174
4175 QRectF rect = r.normalized();
4176
4177 QPainterPath path;
4178 path.moveTo(p: rect.center());
4179 path.arcTo(x: rect.x(), y: rect.y(), w: rect.width(), h: rect.height(), startAngle: a/16.0, arcLength: alen/16.0);
4180 path.closeSubpath();
4181 drawPath(path);
4182
4183}
4184
4185/*!
4186 \fn void QPainter::drawPie(const QRect &rectangle, int startAngle, int spanAngle)
4187 \overload
4188
4189 Draws a pie defined by the given \a rectangle, \a startAngle and
4190 and \a spanAngle.
4191*/
4192
4193/*!
4194 \fn void QPainter::drawPie(int x, int y, int width, int height, int
4195 startAngle, int spanAngle)
4196
4197 \overload
4198
4199 Draws the pie defined by the rectangle beginning at (\a x, \a y) with
4200 the specified \a width and \a height, and the given \a startAngle and
4201 \a spanAngle.
4202*/
4203
4204/*!
4205 \fn void QPainter::drawChord(const QRectF &rectangle, int startAngle, int spanAngle)
4206
4207 Draws the chord defined by the given \a rectangle, \a startAngle and
4208 \a spanAngle. The chord is filled with the current brush().
4209
4210 The startAngle and spanAngle must be specified in 1/16th of a
4211 degree, i.e. a full circle equals 5760 (16 * 360). Positive values
4212 for the angles mean counter-clockwise while negative values mean
4213 the clockwise direction. Zero degrees is at the 3 o'clock
4214 position.
4215
4216 \table 100%
4217 \row
4218 \li \inlineimage qpainter-chord.png
4219 \li
4220 \snippet code/src_gui_painting_qpainter.cpp 12
4221 \endtable
4222
4223 \sa drawArc(), drawPie(), {Coordinate System}
4224*/
4225void QPainter::drawChord(const QRectF &r, int a, int alen)
4226{
4227#ifdef QT_DEBUG_DRAW
4228 if (qt_show_painter_debug_output)
4229 printf("QPainter::drawChord(), [%.2f,%.2f,%.2f,%.2f], angle=%d, sweep=%d\n",
4230 r.x(), r.y(), r.width(), r.height(), a/16, alen/16);
4231#endif
4232 Q_D(QPainter);
4233
4234 if (!d->engine) {
4235 qWarning(msg: "QPainter::drawChord: Painter not active");
4236 return;
4237 }
4238
4239 QRectF rect = r.normalized();
4240
4241 QPainterPath path;
4242 path.arcMoveTo(rect, angle: a/16.0);
4243 path.arcTo(rect, startAngle: a/16.0, arcLength: alen/16.0);
4244 path.closeSubpath();
4245 drawPath(path);
4246}
4247/*!
4248 \fn void QPainter::drawChord(const QRect &rectangle, int startAngle, int spanAngle)
4249
4250 \overload
4251
4252 Draws the chord defined by the given \a rectangle, \a startAngle and
4253 \a spanAngle.
4254*/
4255
4256/*!
4257 \fn void QPainter::drawChord(int x, int y, int width, int height, int
4258 startAngle, int spanAngle)
4259
4260 \overload
4261
4262 Draws the chord defined by the rectangle beginning at (\a x, \a y)
4263 with the specified \a width and \a height, and the given \a
4264 startAngle and \a spanAngle.
4265*/
4266
4267
4268/*!
4269 Draws the first \a lineCount lines in the array \a lines
4270 using the current pen.
4271
4272 \sa drawLine(), drawPolyline()
4273*/
4274void QPainter::drawLines(const QLineF *lines, int lineCount)
4275{
4276#ifdef QT_DEBUG_DRAW
4277 if (qt_show_painter_debug_output)
4278 printf("QPainter::drawLines(), line count=%d\n", lineCount);
4279#endif
4280
4281 Q_D(QPainter);
4282
4283 if (!d->engine || lineCount < 1)
4284 return;
4285
4286 if (d->extended) {
4287 d->extended->drawLines(lines, lineCount);
4288 return;
4289 }
4290
4291 d->updateState(state&: d->state);
4292
4293 uint lineEmulation = line_emulation(emulation: d->state->emulationSpecifier);
4294
4295 if (lineEmulation) {
4296 if (lineEmulation == QPaintEngine::PrimitiveTransform
4297 && d->state->matrix.type() == QTransform::TxTranslate) {
4298 for (int i = 0; i < lineCount; ++i) {
4299 QLineF line = lines[i];
4300 line.translate(adx: d->state->matrix.dx(), ady: d->state->matrix.dy());
4301 d->engine->drawLines(lines: &line, lineCount: 1);
4302 }
4303 } else {
4304 QPainterPath linePath;
4305 for (int i = 0; i < lineCount; ++i) {
4306 linePath.moveTo(p: lines[i].p1());
4307 linePath.lineTo(p: lines[i].p2());
4308 }
4309 d->draw_helper(originalPath: linePath, op: QPainterPrivate::StrokeDraw);
4310 }
4311 return;
4312 }
4313 d->engine->drawLines(lines, lineCount);
4314}
4315
4316/*!
4317 \fn void QPainter::drawLines(const QLine *lines, int lineCount)
4318 \overload
4319
4320 Draws the first \a lineCount lines in the array \a lines
4321 using the current pen.
4322*/
4323void QPainter::drawLines(const QLine *lines, int lineCount)
4324{
4325#ifdef QT_DEBUG_DRAW
4326 if (qt_show_painter_debug_output)
4327 printf("QPainter::drawLine(), line count=%d\n", lineCount);
4328#endif
4329
4330 Q_D(QPainter);
4331
4332 if (!d->engine || lineCount < 1)
4333 return;
4334
4335 if (d->extended) {
4336 d->extended->drawLines(lines, lineCount);
4337 return;
4338 }
4339
4340 d->updateState(state&: d->state);
4341
4342 uint lineEmulation = line_emulation(emulation: d->state->emulationSpecifier);
4343
4344 if (lineEmulation) {
4345 if (lineEmulation == QPaintEngine::PrimitiveTransform
4346 && d->state->matrix.type() == QTransform::TxTranslate) {
4347 for (int i = 0; i < lineCount; ++i) {
4348 QLineF line = lines[i];
4349 line.translate(adx: d->state->matrix.dx(), ady: d->state->matrix.dy());
4350 d->engine->drawLines(lines: &line, lineCount: 1);
4351 }
4352 } else {
4353 QPainterPath linePath;
4354 for (int i = 0; i < lineCount; ++i) {
4355 linePath.moveTo(p: lines[i].p1());
4356 linePath.lineTo(p: lines[i].p2());
4357 }
4358 d->draw_helper(originalPath: linePath, op: QPainterPrivate::StrokeDraw);
4359 }
4360 return;
4361 }
4362 d->engine->drawLines(lines, lineCount);
4363}
4364
4365/*!
4366 \overload
4367
4368 Draws the first \a lineCount lines in the array \a pointPairs
4369 using the current pen. The lines are specified as pairs of points
4370 so the number of entries in \a pointPairs must be at least \a
4371 lineCount * 2.
4372*/
4373void QPainter::drawLines(const QPointF *pointPairs, int lineCount)
4374{
4375 Q_ASSERT(sizeof(QLineF) == 2*sizeof(QPointF));
4376
4377 drawLines(lines: (const QLineF*)pointPairs, lineCount);
4378}
4379
4380/*!
4381 \overload
4382
4383 Draws the first \a lineCount lines in the array \a pointPairs
4384 using the current pen.
4385*/
4386void QPainter::drawLines(const QPoint *pointPairs, int lineCount)
4387{
4388 Q_ASSERT(sizeof(QLine) == 2*sizeof(QPoint));
4389
4390 drawLines(lines: (const QLine*)pointPairs, lineCount);
4391}
4392
4393
4394/*!
4395 \fn void QPainter::drawLines(const QList<QPointF> &pointPairs)
4396 \overload
4397
4398 Draws a line for each pair of points in the vector \a pointPairs
4399 using the current pen. If there is an odd number of points in the
4400 array, the last point will be ignored.
4401*/
4402
4403/*!
4404 \fn void QPainter::drawLines(const QList<QPoint> &pointPairs)
4405 \overload
4406
4407 Draws a line for each pair of points in the vector \a pointPairs
4408 using the current pen.
4409*/
4410
4411/*!
4412 \fn void QPainter::drawLines(const QList<QLineF> &lines)
4413 \overload
4414
4415 Draws the set of lines defined by the list \a lines using the
4416 current pen and brush.
4417*/
4418
4419/*!
4420 \fn void QPainter::drawLines(const QList<QLine> &lines)
4421 \overload
4422
4423 Draws the set of lines defined by the list \a lines using the
4424 current pen and brush.
4425*/
4426
4427/*!
4428 Draws the polyline defined by the first \a pointCount points in \a
4429 points using the current pen.
4430
4431 Note that unlike the drawPolygon() function the last point is \e
4432 not connected to the first, neither is the polyline filled.
4433
4434 \table 100%
4435 \row
4436 \li
4437 \snippet code/src_gui_painting_qpainter.cpp 13
4438 \endtable
4439
4440 \sa drawLines(), drawPolygon(), {Coordinate System}
4441*/
4442void QPainter::drawPolyline(const QPointF *points, int pointCount)
4443{
4444#ifdef QT_DEBUG_DRAW
4445 if (qt_show_painter_debug_output)
4446 printf("QPainter::drawPolyline(), count=%d\n", pointCount);
4447#endif
4448 Q_D(QPainter);
4449
4450 if (!d->engine || pointCount < 2)
4451 return;
4452
4453 if (d->extended) {
4454 d->extended->drawPolygon(points, pointCount, mode: QPaintEngine::PolylineMode);
4455 return;
4456 }
4457
4458 d->updateState(state&: d->state);
4459
4460 uint lineEmulation = line_emulation(emulation: d->state->emulationSpecifier);
4461
4462 if (lineEmulation) {
4463 // ###
4464// if (lineEmulation == QPaintEngine::PrimitiveTransform
4465// && d->state->matrix.type() == QTransform::TxTranslate) {
4466// } else {
4467 QPainterPath polylinePath(points[0]);
4468 for (int i=1; i<pointCount; ++i)
4469 polylinePath.lineTo(p: points[i]);
4470 d->draw_helper(originalPath: polylinePath, op: QPainterPrivate::StrokeDraw);
4471// }
4472 } else {
4473 d->engine->drawPolygon(points, pointCount, mode: QPaintEngine::PolylineMode);
4474 }
4475}
4476
4477/*!
4478 \overload
4479
4480 Draws the polyline defined by the first \a pointCount points in \a
4481 points using the current pen.
4482 */
4483void QPainter::drawPolyline(const QPoint *points, int pointCount)
4484{
4485#ifdef QT_DEBUG_DRAW
4486 if (qt_show_painter_debug_output)
4487 printf("QPainter::drawPolyline(), count=%d\n", pointCount);
4488#endif
4489 Q_D(QPainter);
4490
4491 if (!d->engine || pointCount < 2)
4492 return;
4493
4494 if (d->extended) {
4495 d->extended->drawPolygon(points, pointCount, mode: QPaintEngine::PolylineMode);
4496 return;
4497 }
4498
4499 d->updateState(state&: d->state);
4500
4501 uint lineEmulation = line_emulation(emulation: d->state->emulationSpecifier);
4502
4503 if (lineEmulation) {
4504 // ###
4505// if (lineEmulation == QPaintEngine::PrimitiveTransform
4506// && d->state->matrix.type() == QTransform::TxTranslate) {
4507// } else {
4508 QPainterPath polylinePath(points[0]);
4509 for (int i=1; i<pointCount; ++i)
4510 polylinePath.lineTo(p: points[i]);
4511 d->draw_helper(originalPath: polylinePath, op: QPainterPrivate::StrokeDraw);
4512// }
4513 } else {
4514 d->engine->drawPolygon(points, pointCount, mode: QPaintEngine::PolylineMode);
4515 }
4516}
4517
4518/*!
4519 \fn void QPainter::drawPolyline(const QPolygonF &points)
4520
4521 \overload
4522
4523 Draws the polyline defined by the given \a points using the
4524 current pen.
4525*/
4526
4527/*!
4528 \fn void QPainter::drawPolyline(const QPolygon &points)
4529
4530 \overload
4531
4532 Draws the polyline defined by the given \a points using the
4533 current pen.
4534*/
4535
4536/*!
4537 Draws the polygon defined by the first \a pointCount points in the
4538 array \a points using the current pen and brush.
4539
4540 \table 100%
4541 \row
4542 \li \inlineimage qpainter-polygon.png
4543 \li
4544 \snippet code/src_gui_painting_qpainter.cpp 14
4545 \endtable
4546
4547 The first point is implicitly connected to the last point, and the
4548 polygon is filled with the current brush().
4549
4550 If \a fillRule is Qt::WindingFill, the polygon is filled using the
4551 winding fill algorithm. If \a fillRule is Qt::OddEvenFill, the
4552 polygon is filled using the odd-even fill algorithm. See
4553 \l{Qt::FillRule} for a more detailed description of these fill
4554 rules.
4555
4556 \sa drawConvexPolygon(), drawPolyline(), {Coordinate System}
4557*/
4558void QPainter::drawPolygon(const QPointF *points, int pointCount, Qt::FillRule fillRule)
4559{
4560#ifdef QT_DEBUG_DRAW
4561 if (qt_show_painter_debug_output)
4562 printf("QPainter::drawPolygon(), count=%d\n", pointCount);
4563#endif
4564
4565 Q_D(QPainter);
4566
4567 if (!d->engine || pointCount < 2)
4568 return;
4569
4570 if (d->extended) {
4571 d->extended->drawPolygon(points, pointCount, mode: QPaintEngine::PolygonDrawMode(fillRule));
4572 return;
4573 }
4574
4575 d->updateState(state&: d->state);
4576
4577 uint emulationSpecifier = d->state->emulationSpecifier;
4578
4579 if (emulationSpecifier) {
4580 QPainterPath polygonPath(points[0]);
4581 for (int i=1; i<pointCount; ++i)
4582 polygonPath.lineTo(p: points[i]);
4583 polygonPath.closeSubpath();
4584 polygonPath.setFillRule(fillRule);
4585 d->draw_helper(originalPath: polygonPath);
4586 return;
4587 }
4588
4589 d->engine->drawPolygon(points, pointCount, mode: QPaintEngine::PolygonDrawMode(fillRule));
4590}
4591
4592/*! \overload
4593
4594 Draws the polygon defined by the first \a pointCount points in the
4595 array \a points.
4596*/
4597void QPainter::drawPolygon(const QPoint *points, int pointCount, Qt::FillRule fillRule)
4598{
4599#ifdef QT_DEBUG_DRAW
4600 if (qt_show_painter_debug_output)
4601 printf("QPainter::drawPolygon(), count=%d\n", pointCount);
4602#endif
4603
4604 Q_D(QPainter);
4605
4606 if (!d->engine || pointCount < 2)
4607 return;
4608
4609 if (d->extended) {
4610 d->extended->drawPolygon(points, pointCount, mode: QPaintEngine::PolygonDrawMode(fillRule));
4611 return;
4612 }
4613
4614 d->updateState(state&: d->state);
4615
4616 uint emulationSpecifier = d->state->emulationSpecifier;
4617
4618 if (emulationSpecifier) {
4619 QPainterPath polygonPath(points[0]);
4620 for (int i=1; i<pointCount; ++i)
4621 polygonPath.lineTo(p: points[i]);
4622 polygonPath.closeSubpath();
4623 polygonPath.setFillRule(fillRule);
4624 d->draw_helper(originalPath: polygonPath);
4625 return;
4626 }
4627
4628 d->engine->drawPolygon(points, pointCount, mode: QPaintEngine::PolygonDrawMode(fillRule));
4629}
4630
4631/*! \fn void QPainter::drawPolygon(const QPolygonF &points, Qt::FillRule fillRule)
4632
4633 \overload
4634
4635 Draws the polygon defined by the given \a points using the fill
4636 rule \a fillRule.
4637*/
4638
4639/*! \fn void QPainter::drawPolygon(const QPolygon &points, Qt::FillRule fillRule)
4640
4641 \overload
4642
4643 Draws the polygon defined by the given \a points using the fill
4644 rule \a fillRule.
4645*/
4646
4647/*!
4648 \fn void QPainter::drawConvexPolygon(const QPointF *points, int pointCount)
4649
4650 Draws the convex polygon defined by the first \a pointCount points
4651 in the array \a points using the current pen.
4652
4653 \table 100%
4654 \row
4655 \li \inlineimage qpainter-polygon.png
4656 \li
4657 \snippet code/src_gui_painting_qpainter.cpp 15
4658 \endtable
4659
4660 The first point is implicitly connected to the last point, and the
4661 polygon is filled with the current brush(). If the supplied
4662 polygon is not convex, i.e. it contains at least one angle larger
4663 than 180 degrees, the results are undefined.
4664
4665 On some platforms (e.g. X11), the drawConvexPolygon() function can
4666 be faster than the drawPolygon() function.
4667
4668 \sa drawPolygon(), drawPolyline(), {Coordinate System}
4669*/
4670
4671/*!
4672 \fn void QPainter::drawConvexPolygon(const QPoint *points, int pointCount)
4673 \overload
4674
4675 Draws the convex polygon defined by the first \a pointCount points
4676 in the array \a points using the current pen.
4677*/
4678
4679/*!
4680 \fn void QPainter::drawConvexPolygon(const QPolygonF &polygon)
4681
4682 \overload
4683
4684 Draws the convex polygon defined by \a polygon using the current
4685 pen and brush.
4686*/
4687
4688/*!
4689 \fn void QPainter::drawConvexPolygon(const QPolygon &polygon)
4690 \overload
4691
4692 Draws the convex polygon defined by \a polygon using the current
4693 pen and brush.
4694*/
4695
4696void QPainter::drawConvexPolygon(const QPoint *points, int pointCount)
4697{
4698#ifdef QT_DEBUG_DRAW
4699 if (qt_show_painter_debug_output)
4700 printf("QPainter::drawConvexPolygon(), count=%d\n", pointCount);
4701#endif
4702
4703 Q_D(QPainter);
4704
4705 if (!d->engine || pointCount < 2)
4706 return;
4707
4708 if (d->extended) {
4709 d->extended->drawPolygon(points, pointCount, mode: QPaintEngine::ConvexMode);
4710 return;
4711 }
4712
4713 d->updateState(state&: d->state);
4714
4715 uint emulationSpecifier = d->state->emulationSpecifier;
4716
4717 if (emulationSpecifier) {
4718 QPainterPath polygonPath(points[0]);
4719 for (int i=1; i<pointCount; ++i)
4720 polygonPath.lineTo(p: points[i]);
4721 polygonPath.closeSubpath();
4722 polygonPath.setFillRule(Qt::WindingFill);
4723 d->draw_helper(originalPath: polygonPath);
4724 return;
4725 }
4726
4727 d->engine->drawPolygon(points, pointCount, mode: QPaintEngine::ConvexMode);
4728}
4729
4730void QPainter::drawConvexPolygon(const QPointF *points, int pointCount)
4731{
4732#ifdef QT_DEBUG_DRAW
4733 if (qt_show_painter_debug_output)
4734 printf("QPainter::drawConvexPolygon(), count=%d\n", pointCount);
4735#endif
4736
4737 Q_D(QPainter);
4738
4739 if (!d->engine || pointCount < 2)
4740 return;
4741
4742 if (d->extended) {
4743 d->extended->drawPolygon(points, pointCount, mode: QPaintEngine::ConvexMode);
4744 return;
4745 }
4746
4747 d->updateState(state&: d->state);
4748
4749 uint emulationSpecifier = d->state->emulationSpecifier;
4750
4751 if (emulationSpecifier) {
4752 QPainterPath polygonPath(points[0]);
4753 for (int i=1; i<pointCount; ++i)
4754 polygonPath.lineTo(p: points[i]);
4755 polygonPath.closeSubpath();
4756 polygonPath.setFillRule(Qt::WindingFill);
4757 d->draw_helper(originalPath: polygonPath);
4758 return;
4759 }
4760
4761 d->engine->drawPolygon(points, pointCount, mode: QPaintEngine::ConvexMode);
4762}
4763
4764static inline QPointF roundInDeviceCoordinates(const QPointF &p, const QTransform &m)
4765{
4766 return m.inverted().map(p: QPointF(m.map(p).toPoint()));
4767}
4768
4769/*!
4770 \fn void QPainter::drawPixmap(const QRectF &target, const QPixmap &pixmap, const QRectF &source)
4771
4772 Draws the rectangular portion \a source of the given \a pixmap
4773 into the given \a target in the paint device.
4774
4775 \note The pixmap is scaled to fit the rectangle, if both the pixmap and rectangle size disagree.
4776 \note See \l{Drawing High Resolution Versions of Pixmaps and Images} on how this is affected
4777 by QPixmap::devicePixelRatio().
4778
4779 \table 100%
4780 \row
4781 \li
4782 \snippet code/src_gui_painting_qpainter.cpp 16
4783 \endtable
4784
4785 If \a pixmap is a QBitmap it is drawn with the bits that are "set"
4786 using the pens color. If backgroundMode is Qt::OpaqueMode, the
4787 "unset" bits are drawn using the color of the background brush; if
4788 backgroundMode is Qt::TransparentMode, the "unset" bits are
4789 transparent. Drawing bitmaps with gradient or texture colors is
4790 not supported.
4791
4792 \sa drawImage(), QPixmap::devicePixelRatio()
4793*/
4794void QPainter::drawPixmap(const QPointF &p, const QPixmap &pm)
4795{
4796#if defined QT_DEBUG_DRAW
4797 if (qt_show_painter_debug_output)
4798 printf("QPainter::drawPixmap(), p=[%.2f,%.2f], pix=[%d,%d]\n",
4799 p.x(), p.y(),
4800 pm.width(), pm.height());
4801#endif
4802
4803 Q_D(QPainter);
4804
4805 if (!d->engine || pm.isNull())
4806 return;
4807
4808#ifndef QT_NO_DEBUG
4809 qt_painter_thread_test(devType: d->device->devType(), engineType: d->engine->type(), what: "drawPixmap()");
4810#endif
4811
4812 if (d->extended) {
4813 d->extended->drawPixmap(pos: p, pm);
4814 return;
4815 }
4816
4817 qreal x = p.x();
4818 qreal y = p.y();
4819
4820 int w = pm.width();
4821 int h = pm.height();
4822
4823 if (w <= 0)
4824 return;
4825
4826 // Emulate opaque background for bitmaps
4827 if (d->state->bgMode == Qt::OpaqueMode && pm.isQBitmap()) {
4828 fillRect(QRectF(x, y, w, h), color: d->state->bgBrush.color());
4829 }
4830
4831 d->updateState(state&: d->state);
4832
4833 if ((d->state->matrix.type() > QTransform::TxTranslate
4834 && !d->engine->hasFeature(feature: QPaintEngine::PixmapTransform))
4835 || (!d->state->matrix.isAffine() && !d->engine->hasFeature(feature: QPaintEngine::PerspectiveTransform))
4836 || (d->state->opacity != 1.0 && !d->engine->hasFeature(feature: QPaintEngine::ConstantOpacity)))
4837 {
4838 save();
4839 // If there is no rotation involved we have to make sure we use the
4840 // antialiased and not the aliased coordinate system by rounding the coordinates.
4841 if (d->state->matrix.type() <= QTransform::TxScale) {
4842 const QPointF p = roundInDeviceCoordinates(p: QPointF(x, y), m: d->state->matrix);
4843 x = p.x();
4844 y = p.y();
4845 }
4846 translate(dx: x, dy: y);
4847 setBackgroundMode(Qt::TransparentMode);
4848 setRenderHint(hint: Antialiasing, on: renderHints() & SmoothPixmapTransform);
4849 QBrush brush(d->state->pen.color(), pm);
4850 setBrush(brush);
4851 setPen(Qt::NoPen);
4852 setBrushOrigin(QPointF(0, 0));
4853
4854 drawRect(r: pm.rect());
4855 restore();
4856 } else {
4857 if (!d->engine->hasFeature(feature: QPaintEngine::PixmapTransform)) {
4858 x += d->state->matrix.dx();
4859 y += d->state->matrix.dy();
4860 }
4861 qreal scale = pm.devicePixelRatio();
4862 d->engine->drawPixmap(r: QRectF(x, y, w / scale, h / scale), pm, sr: QRectF(0, 0, w, h));
4863 }
4864}
4865
4866void QPainter::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr)
4867{
4868#if defined QT_DEBUG_DRAW
4869 if (qt_show_painter_debug_output)
4870 printf("QPainter::drawPixmap(), target=[%.2f,%.2f,%.2f,%.2f], pix=[%d,%d], source=[%.2f,%.2f,%.2f,%.2f]\n",
4871 r.x(), r.y(), r.width(), r.height(),
4872 pm.width(), pm.height(),
4873 sr.x(), sr.y(), sr.width(), sr.height());
4874#endif
4875
4876 Q_D(QPainter);
4877 if (!d->engine || pm.isNull())
4878 return;
4879#ifndef QT_NO_DEBUG
4880 qt_painter_thread_test(devType: d->device->devType(), engineType: d->engine->type(), what: "drawPixmap()");
4881#endif
4882
4883 qreal x = r.x();
4884 qreal y = r.y();
4885 qreal w = r.width();
4886 qreal h = r.height();
4887 qreal sx = sr.x();
4888 qreal sy = sr.y();
4889 qreal sw = sr.width();
4890 qreal sh = sr.height();
4891
4892 // Get pixmap scale. Use it when calculating the target
4893 // rect size from pixmap size. For example, a 2X 64x64 pixel
4894 // pixmap should result in a 32x32 point target rect.
4895 const qreal pmscale = pm.devicePixelRatio();
4896
4897 // Sanity-check clipping
4898 if (sw <= 0)
4899 sw = pm.width() - sx;
4900
4901 if (sh <= 0)
4902 sh = pm.height() - sy;
4903
4904 if (w < 0)
4905 w = sw / pmscale;
4906 if (h < 0)
4907 h = sh / pmscale;
4908
4909 if (sx < 0) {
4910 qreal w_ratio = sx * w/sw;
4911 x -= w_ratio;
4912 w += w_ratio;
4913 sw += sx;
4914 sx = 0;
4915 }
4916
4917 if (sy < 0) {
4918 qreal h_ratio = sy * h/sh;
4919 y -= h_ratio;
4920 h += h_ratio;
4921 sh += sy;
4922 sy = 0;
4923 }
4924
4925 if (sw + sx > pm.width()) {
4926 qreal delta = sw - (pm.width() - sx);
4927 qreal w_ratio = delta * w/sw;
4928 sw -= delta;
4929 w -= w_ratio;
4930 }
4931
4932 if (sh + sy > pm.height()) {
4933 qreal delta = sh - (pm.height() - sy);
4934 qreal h_ratio = delta * h/sh;
4935 sh -= delta;
4936 h -= h_ratio;
4937 }
4938
4939 if (w == 0 || h == 0 || sw <= 0 || sh <= 0)
4940 return;
4941
4942 if (d->extended) {
4943 d->extended->drawPixmap(r: QRectF(x, y, w, h), pm, sr: QRectF(sx, sy, sw, sh));
4944 return;
4945 }
4946
4947 // Emulate opaque background for bitmaps
4948 if (d->state->bgMode == Qt::OpaqueMode && pm.isQBitmap())
4949 fillRect(QRectF(x, y, w, h), color: d->state->bgBrush.color());
4950
4951 d->updateState(state&: d->state);
4952
4953 if ((d->state->matrix.type() > QTransform::TxTranslate
4954 && !d->engine->hasFeature(feature: QPaintEngine::PixmapTransform))
4955 || (!d->state->matrix.isAffine() && !d->engine->hasFeature(feature: QPaintEngine::PerspectiveTransform))
4956 || (d->state->opacity != 1.0 && !d->engine->hasFeature(feature: QPaintEngine::ConstantOpacity))
4957 || ((sw != w || sh != h) && !d->engine->hasFeature(feature: QPaintEngine::PixmapTransform)))
4958 {
4959 save();
4960 // If there is no rotation involved we have to make sure we use the
4961 // antialiased and not the aliased coordinate system by rounding the coordinates.
4962 if (d->state->matrix.type() <= QTransform::TxScale) {
4963 const QPointF p = roundInDeviceCoordinates(p: QPointF(x, y), m: d->state->matrix);
4964 x = p.x();
4965 y = p.y();
4966 }
4967
4968 if (d->state->matrix.type() <= QTransform::TxTranslate && sw == w && sh == h) {
4969 sx = qRound(d: sx);
4970 sy = qRound(d: sy);
4971 sw = qRound(d: sw);
4972 sh = qRound(d: sh);
4973 }
4974
4975 translate(dx: x, dy: y);
4976 scale(sx: w / sw, sy: h / sh);
4977 setBackgroundMode(Qt::TransparentMode);
4978 setRenderHint(hint: Antialiasing, on: renderHints() & SmoothPixmapTransform);
4979 QBrush brush;
4980
4981 if (sw == pm.width() && sh == pm.height())
4982 brush = QBrush(d->state->pen.color(), pm);
4983 else
4984 brush = QBrush(d->state->pen.color(), pm.copy(ax: sx, ay: sy, awidth: sw, aheight: sh));
4985
4986 setBrush(brush);
4987 setPen(Qt::NoPen);
4988
4989 drawRect(rect: QRectF(0, 0, sw, sh));
4990 restore();
4991 } else {
4992 if (!d->engine->hasFeature(feature: QPaintEngine::PixmapTransform)) {
4993 x += d->state->matrix.dx();
4994 y += d->state->matrix.dy();
4995 }
4996 d->engine->drawPixmap(r: QRectF(x, y, w, h), pm, sr: QRectF(sx, sy, sw, sh));
4997 }
4998}
4999
5000
5001/*!
5002 \fn void QPainter::drawPixmap(const QRect &target, const QPixmap &pixmap,
5003 const QRect &source)
5004 \overload
5005
5006 Draws the rectangular portion \a source of the given \a pixmap
5007 into the given \a target in the paint device.
5008
5009 \note The pixmap is scaled to fit the rectangle, if both the pixmap and rectangle size disagree.
5010*/
5011
5012/*!
5013 \fn void QPainter::drawPixmap(const QPointF &point, const QPixmap &pixmap,
5014 const QRectF &source)
5015 \overload
5016
5017 Draws the rectangular portion \a source of the given \a pixmap
5018 with its origin at the given \a point.
5019*/
5020
5021/*!
5022 \fn void QPainter::drawPixmap(const QPoint &point, const QPixmap &pixmap,
5023 const QRect &source)
5024
5025 \overload
5026
5027 Draws the rectangular portion \a source of the given \a pixmap
5028 with its origin at the given \a point.
5029*/
5030
5031/*!
5032 \fn void QPainter::drawPixmap(const QPointF &point, const QPixmap &pixmap)
5033 \overload
5034
5035 Draws the given \a pixmap with its origin at the given \a point.
5036*/
5037
5038/*!
5039 \fn void QPainter::drawPixmap(const QPoint &point, const QPixmap &pixmap)
5040 \overload
5041
5042 Draws the given \a pixmap with its origin at the given \a point.
5043*/
5044
5045/*!
5046 \fn void QPainter::drawPixmap(int x, int y, const QPixmap &pixmap)
5047
5048 \overload
5049
5050 Draws the given \a pixmap at position (\a{x}, \a{y}).
5051*/
5052
5053/*!
5054 \fn void QPainter::drawPixmap(const QRect &rectangle, const QPixmap &pixmap)
5055 \overload
5056
5057 Draws the given \a pixmap into the given \a rectangle.
5058
5059 \note The pixmap is scaled to fit the rectangle, if both the pixmap and rectangle size disagree.
5060*/
5061
5062/*!
5063 \fn void QPainter::drawPixmap(int x, int y, int width, int height,
5064 const QPixmap &pixmap)
5065
5066 \overload
5067
5068 Draws the \a pixmap into the rectangle at position (\a{x}, \a{y})
5069 with the given \a width and \a height.
5070*/
5071
5072/*!
5073 \fn void QPainter::drawPixmap(int x, int y, int w, int h, const QPixmap &pixmap,
5074 int sx, int sy, int sw, int sh)
5075
5076 \overload
5077
5078 Draws the rectangular portion with the origin (\a{sx}, \a{sy}),
5079 width \a sw and height \a sh, of the given \a pixmap , at the
5080 point (\a{x}, \a{y}), with a width of \a w and a height of \a h.
5081 If sw or sh are equal to zero the width/height of the pixmap
5082 is used and adjusted by the offset sx/sy;
5083*/
5084
5085/*!
5086 \fn void QPainter::drawPixmap(int x, int y, const QPixmap &pixmap,
5087 int sx, int sy, int sw, int sh)
5088
5089 \overload
5090
5091 Draws a pixmap at (\a{x}, \a{y}) by copying a part of the given \a
5092 pixmap into the paint device.
5093
5094 (\a{x}, \a{y}) specifies the top-left point in the paint device that is
5095 to be drawn onto. (\a{sx}, \a{sy}) specifies the top-left point in \a
5096 pixmap that is to be drawn. The default is (0, 0).
5097
5098 (\a{sw}, \a{sh}) specifies the size of the pixmap that is to be drawn.
5099 The default, (0, 0) (and negative) means all the way to the
5100 bottom-right of the pixmap.
5101*/
5102
5103void QPainter::drawImage(const QPointF &p, const QImage &image)
5104{
5105 Q_D(QPainter);
5106
5107 if (!d->engine || image.isNull())
5108 return;
5109
5110 if (d->extended) {
5111 d->extended->drawImage(pos: p, image);
5112 return;
5113 }
5114
5115 qreal x = p.x();
5116 qreal y = p.y();
5117
5118 int w = image.width();
5119 int h = image.height();
5120 qreal scale = image.devicePixelRatio();
5121
5122 d->updateState(state&: d->state);
5123
5124 if (((d->state->matrix.type() > QTransform::TxTranslate)
5125 && !d->engine->hasFeature(feature: QPaintEngine::PixmapTransform))
5126 || (!d->state->matrix.isAffine() && !d->engine->hasFeature(feature: QPaintEngine::PerspectiveTransform))
5127 || (d->state->opacity != 1.0 && !d->engine->hasFeature(feature: QPaintEngine::ConstantOpacity)))
5128 {
5129 save();
5130 // If there is no rotation involved we have to make sure we use the
5131 // antialiased and not the aliased coordinate system by rounding the coordinates.
5132 if (d->state->matrix.type() <= QTransform::TxScale) {
5133 const QPointF p = roundInDeviceCoordinates(p: QPointF(x, y), m: d->state->matrix);
5134 x = p.x();
5135 y = p.y();
5136 }
5137 translate(dx: x, dy: y);
5138 setBackgroundMode(Qt::TransparentMode);
5139 setRenderHint(hint: Antialiasing, on: renderHints() & SmoothPixmapTransform);
5140 QBrush brush(image);
5141 setBrush(brush);
5142 setPen(Qt::NoPen);
5143 setBrushOrigin(QPointF(0, 0));
5144 drawRect(r: QRect(QPoint(0, 0), image.size() / scale));
5145 restore();
5146 return;
5147 }
5148
5149 if (d->state->matrix.type() == QTransform::TxTranslate
5150 && !d->engine->hasFeature(feature: QPaintEngine::PixmapTransform)) {
5151 x += d->state->matrix.dx();
5152 y += d->state->matrix.dy();
5153 }
5154
5155 d->engine->drawImage(r: QRectF(x, y, w / scale, h / scale), pm: image, sr: QRectF(0, 0, w, h), flags: Qt::AutoColor);
5156}
5157
5158void QPainter::drawImage(const QRectF &targetRect, const QImage &image, const QRectF &sourceRect,
5159 Qt::ImageConversionFlags flags)
5160{
5161 Q_D(QPainter);
5162
5163 if (!d->engine || image.isNull())
5164 return;
5165
5166 qreal x = targetRect.x();
5167 qreal y = targetRect.y();
5168 qreal w = targetRect.width();
5169 qreal h = targetRect.height();
5170 qreal sx = sourceRect.x();
5171 qreal sy = sourceRect.y();
5172 qreal sw = sourceRect.width();
5173 qreal sh = sourceRect.height();
5174 qreal imageScale = image.devicePixelRatio();
5175
5176 // Sanity-check clipping
5177 if (sw <= 0)
5178 sw = image.width() - sx;
5179
5180 if (sh <= 0)
5181 sh = image.height() - sy;
5182
5183 if (w < 0)
5184 w = sw / imageScale;
5185 if (h < 0)
5186 h = sh / imageScale;
5187
5188 if (sx < 0) {
5189 qreal w_ratio = sx * w/sw;
5190 x -= w_ratio;
5191 w += w_ratio;
5192 sw += sx;
5193 sx = 0;
5194 }
5195
5196 if (sy < 0) {
5197 qreal h_ratio = sy * h/sh;
5198 y -= h_ratio;
5199 h += h_ratio;
5200 sh += sy;
5201 sy = 0;
5202 }
5203
5204 if (sw + sx > image.width()) {
5205 qreal delta = sw - (image.width() - sx);
5206 qreal w_ratio = delta * w/sw;
5207 sw -= delta;
5208 w -= w_ratio;
5209 }
5210
5211 if (sh + sy > image.height()) {
5212 qreal delta = sh - (image.height() - sy);
5213 qreal h_ratio = delta * h/sh;
5214 sh -= delta;
5215 h -= h_ratio;
5216 }
5217
5218 if (w == 0 || h == 0 || sw <= 0 || sh <= 0)
5219 return;
5220
5221 if (d->extended) {
5222 d->extended->drawImage(r: QRectF(x, y, w, h), pm: image, sr: QRectF(sx, sy, sw, sh), flags);
5223 return;
5224 }
5225
5226 d->updateState(state&: d->state);
5227
5228 if (((d->state->matrix.type() > QTransform::TxTranslate || (sw != w || sh != h))
5229 && !d->engine->hasFeature(feature: QPaintEngine::PixmapTransform))
5230 || (!d->state->matrix.isAffine() && !d->engine->hasFeature(feature: QPaintEngine::PerspectiveTransform))
5231 || (d->state->opacity != 1.0 && !d->engine->hasFeature(feature: QPaintEngine::ConstantOpacity)))
5232 {
5233 save();
5234 // If there is no rotation involved we have to make sure we use the
5235 // antialiased and not the aliased coordinate system by rounding the coordinates.
5236 if (d->state->matrix.type() <= QTransform::TxScale) {
5237 const QPointF p = roundInDeviceCoordinates(p: QPointF(x, y), m: d->state->matrix);
5238 x = p.x();
5239 y = p.y();
5240 }
5241
5242 if (d->state->matrix.type() <= QTransform::TxTranslate && sw == w && sh == h) {
5243 sx = qRound(d: sx);
5244 sy = qRound(d: sy);
5245 sw = qRound(d: sw);
5246 sh = qRound(d: sh);
5247 }
5248 translate(dx: x, dy: y);
5249 scale(sx: w / sw, sy: h / sh);
5250 setBackgroundMode(Qt::TransparentMode);
5251 setRenderHint(hint: Antialiasing, on: renderHints() & SmoothPixmapTransform);
5252 QBrush brush(image);
5253 setBrush(brush);
5254 setPen(Qt::NoPen);
5255 setBrushOrigin(QPointF(-sx, -sy));
5256
5257 drawRect(rect: QRectF(0, 0, sw, sh));
5258 restore();
5259 return;
5260 }
5261
5262 if (d->state->matrix.type() == QTransform::TxTranslate
5263 && !d->engine->hasFeature(feature: QPaintEngine::PixmapTransform)) {
5264 x += d->state->matrix.dx();
5265 y += d->state->matrix.dy();
5266 }
5267
5268 d->engine->drawImage(r: QRectF(x, y, w, h), pm: image, sr: QRectF(sx, sy, sw, sh), flags);
5269}
5270
5271/*!
5272 \fn void QPainter::drawGlyphRun(const QPointF &position, const QGlyphRun &glyphs)
5273
5274 Draws the glyphs represented by \a glyphs at \a position. The \a position gives the
5275 edge of the baseline for the string of glyphs. The glyphs will be retrieved from the font
5276 selected on \a glyphs and at offsets given by the positions in \a glyphs.
5277
5278 \since 4.8
5279
5280 \sa QGlyphRun::setRawFont(), QGlyphRun::setPositions(), QGlyphRun::setGlyphIndexes()
5281*/
5282#if !defined(QT_NO_RAWFONT)
5283void QPainter::drawGlyphRun(const QPointF &position, const QGlyphRun &glyphRun)
5284{
5285 Q_D(QPainter);
5286
5287 if (!d->engine) {
5288 qWarning(msg: "QPainter::drawGlyphRun: Painter not active");
5289 return;
5290 }
5291
5292 QRawFont font = glyphRun.rawFont();
5293 if (!font.isValid())
5294 return;
5295
5296 QGlyphRunPrivate *glyphRun_d = QGlyphRunPrivate::get(glyphRun);
5297
5298 const quint32 *glyphIndexes = glyphRun_d->glyphIndexData;
5299 const QPointF *glyphPositions = glyphRun_d->glyphPositionData;
5300
5301 int count = qMin(a: glyphRun_d->glyphIndexDataSize, b: glyphRun_d->glyphPositionDataSize);
5302 QVarLengthArray<QFixedPoint, 128> fixedPointPositions(count);
5303
5304 QRawFontPrivate *fontD = QRawFontPrivate::get(font);
5305 bool engineRequiresPretransformedGlyphPositions = d->extended
5306 ? d->extended->requiresPretransformedGlyphPositions(fontEngine: fontD->fontEngine, m: d->state->matrix)
5307 : d->engine->type() != QPaintEngine::CoreGraphics && !d->state->matrix.isAffine();
5308
5309 for (int i=0; i<count; ++i) {
5310 QPointF processedPosition = position + glyphPositions[i];
5311 if (engineRequiresPretransformedGlyphPositions)
5312 processedPosition = d->state->transform().map(p: processedPosition);
5313 fixedPointPositions[i] = QFixedPoint::fromPointF(p: processedPosition);
5314 }
5315
5316 d->drawGlyphs(decorationPosition: engineRequiresPretransformedGlyphPositions
5317 ? d->state->transform().map(p: position)
5318 : position,
5319 glyphArray: glyphIndexes,
5320 positionArray: fixedPointPositions.data(),
5321 glyphCount: count,
5322 fontEngine: fontD->fontEngine,
5323 overline: glyphRun.overline(),
5324 underline: glyphRun.underline(),
5325 strikeOut: glyphRun.strikeOut());
5326}
5327
5328void QPainterPrivate::drawGlyphs(const QPointF &decorationPosition,
5329 const quint32 *glyphArray,
5330 QFixedPoint *positions,
5331 int glyphCount,
5332 QFontEngine *fontEngine,
5333 bool overline,
5334 bool underline,
5335 bool strikeOut)
5336{
5337 Q_Q(QPainter);
5338
5339 updateState(state);
5340
5341 if (extended != nullptr && state->matrix.isAffine()) {
5342 QStaticTextItem staticTextItem;
5343 staticTextItem.color = state->pen.color();
5344 staticTextItem.font = state->font;
5345 staticTextItem.setFontEngine(fontEngine);
5346 staticTextItem.numGlyphs = glyphCount;
5347 staticTextItem.glyphs = reinterpret_cast<glyph_t *>(const_cast<glyph_t *>(glyphArray));
5348 staticTextItem.glyphPositions = positions;
5349 // The font property is meaningless, the fontengine must be used directly:
5350 staticTextItem.usesRawFont = true;
5351
5352 extended->drawStaticTextItem(&staticTextItem);
5353 } else {
5354 QTextItemInt textItem;
5355 textItem.fontEngine = fontEngine;
5356
5357 QVarLengthArray<QFixed, 128> advances(glyphCount);
5358 QVarLengthArray<QGlyphJustification, 128> glyphJustifications(glyphCount);
5359 QVarLengthArray<QGlyphAttributes, 128> glyphAttributes(glyphCount);
5360 memset(s: glyphAttributes.data(), c: 0, n: glyphAttributes.size() * sizeof(QGlyphAttributes));
5361 memset(s: static_cast<void *>(advances.data()), c: 0, n: advances.size() * sizeof(QFixed));
5362 memset(s: static_cast<void *>(glyphJustifications.data()), c: 0, n: glyphJustifications.size() * sizeof(QGlyphJustification));
5363
5364 textItem.glyphs.numGlyphs = glyphCount;
5365 textItem.glyphs.glyphs = const_cast<glyph_t *>(glyphArray);
5366 textItem.glyphs.offsets = positions;
5367 textItem.glyphs.advances = advances.data();
5368 textItem.glyphs.justifications = glyphJustifications.data();
5369 textItem.glyphs.attributes = glyphAttributes.data();
5370
5371 engine->drawTextItem(p: QPointF(0, 0), textItem);
5372 }
5373
5374 qt_draw_decoration_for_glyphs(painter: q,
5375 decorationPosition,
5376 glyphArray,
5377 positions,
5378 glyphCount,
5379 fontEngine,
5380 underline,
5381 overline,
5382 strikeOut);
5383}
5384#endif // QT_NO_RAWFONT
5385
5386/*!
5387
5388 \fn void QPainter::drawStaticText(const QPoint &topLeftPosition, const QStaticText &staticText)
5389 \since 4.7
5390 \overload
5391
5392 Draws the \a staticText at the \a topLeftPosition.
5393
5394 \note The y-position is used as the top of the font.
5395
5396*/
5397
5398/*!
5399 \fn void QPainter::drawStaticText(int left, int top, const QStaticText &staticText)
5400 \since 4.7
5401 \overload
5402
5403 Draws the \a staticText at coordinates \a left and \a top.
5404
5405 \note The y-position is used as the top of the font.
5406*/
5407
5408/*!
5409 \fn void QPainter::drawText(const QPointF &position, const QString &text)
5410
5411 Draws the given \a text with the currently defined text direction,
5412 beginning at the given \a position.
5413
5414 This function does not handle the newline character (\\n), as it cannot
5415 break text into multiple lines, and it cannot display the newline character.
5416 Use the QPainter::drawText() overload that takes a rectangle instead
5417 if you want to draw multiple lines of text with the newline character, or
5418 if you want the text to be wrapped.
5419
5420 By default, QPainter draws text anti-aliased.
5421
5422 \note The y-position is used as the baseline of the font.
5423
5424 \sa setFont(), setPen()
5425*/
5426
5427void QPainter::drawText(const QPointF &p, const QString &str)
5428{
5429 drawText(p, str, tf: 0, justificationPadding: 0);
5430}
5431
5432/*!
5433 \since 4.7
5434
5435 Draws the given \a staticText at the given \a topLeftPosition.
5436
5437 The text will be drawn using the font and the transformation set on the painter. If the
5438 font and/or transformation set on the painter are different from the ones used to initialize
5439 the layout of the QStaticText, then the layout will have to be recalculated. Use
5440 QStaticText::prepare() to initialize \a staticText with the font and transformation with which
5441 it will later be drawn.
5442
5443 If \a topLeftPosition is not the same as when \a staticText was initialized, or when it was
5444 last drawn, then there will be a slight overhead when translating the text to its new position.
5445
5446 \note If the painter's transformation is not affine, then \a staticText will be drawn using
5447 regular calls to drawText(), losing any potential for performance improvement.
5448
5449 \note The y-position is used as the top of the font.
5450
5451 \sa QStaticText
5452*/
5453void QPainter::drawStaticText(const QPointF &topLeftPosition, const QStaticText &staticText)
5454{
5455 Q_D(QPainter);
5456 if (!d->engine || staticText.text().isEmpty() || pen().style() == Qt::NoPen)
5457 return;
5458
5459 QStaticTextPrivate *staticText_d =
5460 const_cast<QStaticTextPrivate *>(QStaticTextPrivate::get(q: &staticText));
5461
5462 QFontPrivate *fp = QFontPrivate::get(font: font());
5463 QFontPrivate *stfp = QFontPrivate::get(font: staticText_d->font);
5464 if (font() != staticText_d->font || fp == nullptr || stfp == nullptr || fp->dpi != stfp->dpi) {
5465 staticText_d->font = font();
5466 staticText_d->needsRelayout = true;
5467 } else if (stfp->engineData == nullptr || stfp->engineData->fontCacheId != QFontCache::instance()->id()) {
5468 staticText_d->needsRelayout = true;
5469 }
5470
5471 QFontEngine *fe = staticText_d->font.d->engineForScript(script: QChar::Script_Common);
5472 if (fe->type() == QFontEngine::Multi)
5473 fe = static_cast<QFontEngineMulti *>(fe)->engine(at: 0);
5474
5475 // If we don't have an extended paint engine, if the painter is projected,
5476 // or if the font engine does not support the matrix, we go through standard
5477 // code path
5478 if (d->extended == nullptr
5479 || !d->state->matrix.isAffine()
5480 || !fe->supportsTransformation(transform: d->state->matrix)) {
5481 staticText_d->paintText(pos: topLeftPosition, p: this, pen: pen().color());
5482 return;
5483 }
5484
5485 bool engineRequiresPretransform = d->extended->requiresPretransformedGlyphPositions(fontEngine: fe, m: d->state->matrix);
5486 if (staticText_d->untransformedCoordinates && engineRequiresPretransform) {
5487 // The coordinates are untransformed, and the engine can't deal with that
5488 // nativly, so we have to pre-transform the static text.
5489 staticText_d->untransformedCoordinates = false;
5490 staticText_d->needsRelayout = true;
5491 } else if (!staticText_d->untransformedCoordinates && !engineRequiresPretransform) {
5492 // The coordinates are already transformed, but the engine can handle that
5493 // nativly, so undo the transform of the static text.
5494 staticText_d->untransformedCoordinates = true;
5495 staticText_d->needsRelayout = true;
5496 }
5497
5498 // Don't recalculate entire layout because of translation, rather add the dx and dy
5499 // into the position to move each text item the correct distance.
5500 QPointF transformedPosition = topLeftPosition;
5501 if (!staticText_d->untransformedCoordinates)
5502 transformedPosition = transformedPosition * d->state->matrix;
5503 QTransform oldMatrix;
5504
5505 // The translation has been applied to transformedPosition. Remove translation
5506 // component from matrix.
5507 if (d->state->matrix.isTranslating() && !staticText_d->untransformedCoordinates) {
5508 qreal m11 = d->state->matrix.m11();
5509 qreal m12 = d->state->matrix.m12();
5510 qreal m13 = d->state->matrix.m13();
5511 qreal m21 = d->state->matrix.m21();
5512 qreal m22 = d->state->matrix.m22();
5513 qreal m23 = d->state->matrix.m23();
5514 qreal m33 = d->state->matrix.m33();
5515
5516 oldMatrix = d->state->matrix;
5517 d->state->matrix.setMatrix(m11, m12, m13,
5518 m21, m22, m23,
5519 m31: 0.0, m32: 0.0, m33);
5520 }
5521
5522 // If the transform is not identical to the text transform,
5523 // we have to relayout the text (for other transformations than plain translation)
5524 bool staticTextNeedsReinit = staticText_d->needsRelayout;
5525 if (!staticText_d->untransformedCoordinates && staticText_d->matrix != d->state->matrix) {
5526 staticText_d->matrix = d->state->matrix;
5527 staticTextNeedsReinit = true;
5528 }
5529
5530 // Recreate the layout of the static text because the matrix or font has changed
5531 if (staticTextNeedsReinit)
5532 staticText_d->init();
5533
5534 if (transformedPosition != staticText_d->position) { // Translate to actual position
5535 QFixed fx = QFixed::fromReal(r: transformedPosition.x());
5536 QFixed fy = QFixed::fromReal(r: transformedPosition.y());
5537 QFixed oldX = QFixed::fromReal(r: staticText_d->position.x());
5538 QFixed oldY = QFixed::fromReal(r: staticText_d->position.y());
5539 for (int item=0; item<staticText_d->itemCount;++item) {
5540 QStaticTextItem *textItem = staticText_d->items + item;
5541 for (int i=0; i<textItem->numGlyphs; ++i) {
5542 textItem->glyphPositions[i].x += fx - oldX;
5543 textItem->glyphPositions[i].y += fy - oldY;
5544 }
5545 textItem->userDataNeedsUpdate = true;
5546 }
5547
5548 staticText_d->position = transformedPosition;
5549 }
5550
5551 QPen oldPen = d->state->pen;
5552 QColor currentColor = oldPen.color();
5553 static const QColor bodyIndicator(0, 0, 0, 0);
5554 for (int i=0; i<staticText_d->itemCount; ++i) {
5555 QStaticTextItem *item = staticText_d->items + i;
5556 if (item->color.isValid() && currentColor != item->color
5557 && item->color != bodyIndicator) {
5558 setPen(item->color);
5559 currentColor = item->color;
5560 } else if (item->color == bodyIndicator) {
5561 setPen(oldPen);
5562 currentColor = oldPen.color();
5563 }
5564 d->extended->drawStaticTextItem(item);
5565
5566 qt_draw_decoration_for_glyphs(painter: this,
5567 decorationPosition: topLeftPosition,
5568 glyphArray: item->glyphs,
5569 positions: item->glyphPositions,
5570 glyphCount: item->numGlyphs,
5571 fontEngine: item->fontEngine(),
5572 underline: staticText_d->font.underline(),
5573 overline: staticText_d->font.overline(),
5574 strikeOut: staticText_d->font.strikeOut());
5575 }
5576 if (currentColor != oldPen.color())
5577 setPen(oldPen);
5578
5579 if (!staticText_d->untransformedCoordinates && oldMatrix.isTranslating())
5580 d->state->matrix = oldMatrix;
5581}
5582
5583/*!
5584 \internal
5585*/
5586void QPainter::drawText(const QPointF &p, const QString &str, int tf, int justificationPadding)
5587{
5588#ifdef QT_DEBUG_DRAW
5589 if (qt_show_painter_debug_output)
5590 printf("QPainter::drawText(), pos=[%.2f,%.2f], str='%s'\n", p.x(), p.y(), str.toLatin1().constData());
5591#endif
5592
5593 Q_D(QPainter);
5594
5595 if (!d->engine || str.isEmpty() || pen().style() == Qt::NoPen)
5596 return;
5597
5598 QStackTextEngine engine(str, d->state->font);
5599 engine.option.setTextDirection(d->state->layoutDirection);
5600 if (tf & (Qt::TextForceLeftToRight|Qt::TextForceRightToLeft)) {
5601 engine.ignoreBidi = true;
5602 engine.option.setTextDirection((tf & Qt::TextForceLeftToRight) ? Qt::LeftToRight : Qt::RightToLeft);
5603 }
5604 engine.itemize();
5605 QScriptLine line;
5606 line.length = str.size();
5607 engine.shapeLine(line);
5608
5609 int nItems = engine.layoutData->items.size();
5610 QVarLengthArray<int> visualOrder(nItems);
5611 QVarLengthArray<uchar> levels(nItems);
5612 for (int i = 0; i < nItems; ++i)
5613 levels[i] = engine.layoutData->items[i].analysis.bidiLevel;
5614 QTextEngine::bidiReorder(numRuns: nItems, levels: levels.data(), visualOrder: visualOrder.data());
5615
5616 if (justificationPadding > 0) {
5617 engine.option.setAlignment(Qt::AlignJustify);
5618 engine.forceJustification = true;
5619 // this works because justify() is only interested in the difference between width and textWidth
5620 line.width = justificationPadding;
5621 engine.justify(si: line);
5622 }
5623 QFixed x = QFixed::fromReal(r: p.x());
5624
5625 for (int i = 0; i < nItems; ++i) {
5626 int item = visualOrder[i];
5627 const QScriptItem &si = engine.layoutData->items.at(i: item);
5628 if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
5629 x += si.width;
5630 continue;
5631 }
5632 QFont f = engine.font(si);
5633 QTextItemInt gf(si, &f);
5634 gf.glyphs = engine.shapedGlyphs(si: &si);
5635 gf.chars = engine.layoutData->string.unicode() + si.position;
5636 gf.num_chars = engine.length(item);
5637 if (engine.forceJustification) {
5638 for (int j=0; j<gf.glyphs.numGlyphs; ++j)
5639 gf.width += gf.glyphs.effectiveAdvance(item: j);
5640 } else {
5641 gf.width = si.width;
5642 }
5643 gf.logClusters = engine.logClusters(si: &si);
5644
5645 drawTextItem(p: QPointF(x.toReal(), p.y()), ti: gf);
5646
5647 x += gf.width;
5648 }
5649}
5650
5651void QPainter::drawText(const QRect &r, int flags, const QString &str, QRect *br)
5652{
5653#ifdef QT_DEBUG_DRAW
5654 if (qt_show_painter_debug_output)
5655 printf("QPainter::drawText(), r=[%d,%d,%d,%d], flags=%d, str='%s'\n",
5656 r.x(), r.y(), r.width(), r.height(), flags, str.toLatin1().constData());
5657#endif
5658
5659 Q_D(QPainter);
5660
5661 if (!d->engine || str.size() == 0 || pen().style() == Qt::NoPen)
5662 return;
5663
5664 if (!d->extended)
5665 d->updateState(state&: d->state);
5666
5667 QRectF bounds;
5668 qt_format_text(font: d->state->font, r: r, tf: flags, option: nullptr, str, brect: br ? &bounds : nullptr, tabstops: 0, tabarray: nullptr, tabarraylen: 0, painter: this);
5669 if (br)
5670 *br = bounds.toAlignedRect();
5671}
5672
5673/*!
5674 \fn void QPainter::drawText(const QPoint &position, const QString &text)
5675
5676 \overload
5677
5678 Draws the given \a text with the currently defined text direction,
5679 beginning at the given \a position.
5680
5681 By default, QPainter draws text anti-aliased.
5682
5683 \note The y-position is used as the baseline of the font.
5684
5685 \sa setFont(), setPen()
5686*/
5687
5688/*!
5689 \fn void QPainter::drawText(const QRectF &rectangle, int flags, const QString &text, QRectF *boundingRect)
5690 \overload
5691
5692 Draws the given \a text within the provided \a rectangle.
5693 The \a rectangle along with alignment \a flags defines the anchors for the \a text.
5694
5695 \table 100%
5696 \row
5697 \li \inlineimage qpainter-text.png
5698 \li
5699 \snippet code/src_gui_painting_qpainter.cpp 17
5700 \endtable
5701
5702 The \a boundingRect (if not null) is set to what the bounding rectangle
5703 should be in order to enclose the whole text. For example, in the following
5704 image, the dotted line represents \a boundingRect as calculated by the
5705 function, and the dashed line represents \a rectangle:
5706
5707 \table 100%
5708 \row
5709 \li \inlineimage qpainter-text-bounds.png
5710 \li \snippet code/src_gui_painting_qpainter.cpp drawText
5711 \endtable
5712
5713 The \a flags argument is a bitwise OR of the following flags:
5714
5715 \list
5716 \li Qt::AlignLeft
5717 \li Qt::AlignRight
5718 \li Qt::AlignHCenter
5719 \li Qt::AlignJustify
5720 \li Qt::AlignTop
5721 \li Qt::AlignBottom
5722 \li Qt::AlignVCenter
5723 \li Qt::AlignCenter
5724 \li Qt::TextDontClip
5725 \li Qt::TextSingleLine
5726 \li Qt::TextExpandTabs
5727 \li Qt::TextShowMnemonic
5728 \li Qt::TextWordWrap
5729 \li Qt::TextIncludeTrailingSpaces
5730 \endlist
5731
5732 \sa Qt::AlignmentFlag, Qt::TextFlag, boundingRect(), layoutDirection()
5733
5734 By default, QPainter draws text anti-aliased.
5735
5736 \note The y-coordinate of \a rectangle is used as the top of the font.
5737*/
5738void QPainter::drawText(const QRectF &r, int flags, const QString &str, QRectF *br)
5739{
5740#ifdef QT_DEBUG_DRAW
5741 if (qt_show_painter_debug_output)
5742 printf("QPainter::drawText(), r=[%.2f,%.2f,%.2f,%.2f], flags=%d, str='%s'\n",
5743 r.x(), r.y(), r.width(), r.height(), flags, str.toLatin1().constData());
5744#endif
5745
5746 Q_D(QPainter);
5747
5748 if (!d->engine || str.size() == 0 || pen().style() == Qt::NoPen)
5749 return;
5750
5751 if (!d->extended)
5752 d->updateState(state&: d->state);
5753
5754 qt_format_text(font: d->state->font, r: r, tf: flags, option: nullptr, str, brect: br, tabstops: 0, tabarray: nullptr, tabarraylen: 0, painter: this);
5755}
5756
5757/*!
5758 \fn void QPainter::drawText(const QRect &rectangle, int flags, const QString &text, QRect *boundingRect)
5759 \overload
5760
5761 Draws the given \a text within the provided \a rectangle according
5762 to the specified \a flags.
5763
5764 The \a boundingRect (if not null) is set to the what the bounding rectangle
5765 should be in order to enclose the whole text. For example, in the following
5766 image, the dotted line represents \a boundingRect as calculated by the
5767 function, and the dashed line represents \a rectangle:
5768
5769 \table 100%
5770 \row
5771 \li \inlineimage qpainter-text-bounds.png
5772 \li \snippet code/src_gui_painting_qpainter.cpp drawText
5773 \endtable
5774
5775 By default, QPainter draws text anti-aliased.
5776
5777 \note The y-coordinate of \a rectangle is used as the top of the font.
5778
5779 \sa setFont(), setPen()
5780*/
5781
5782/*!
5783 \fn void QPainter::drawText(int x, int y, const QString &text)
5784
5785 \overload
5786
5787 Draws the given \a text at position (\a{x}, \a{y}), using the painter's
5788 currently defined text direction.
5789
5790 By default, QPainter draws text anti-aliased.
5791
5792 \note The y-position is used as the baseline of the font.
5793
5794 \sa setFont(), setPen()
5795*/
5796
5797/*!
5798 \fn void QPainter::drawText(int x, int y, int width, int height, int flags,
5799 const QString &text, QRect *boundingRect)
5800
5801 \overload
5802
5803 Draws the given \a text within the rectangle with origin (\a{x},
5804 \a{y}), \a width and \a height.
5805
5806 The \a boundingRect (if not null) is set to the what the bounding rectangle
5807 should be in order to enclose the whole text. For example, in the following
5808 image, the dotted line represents \a boundingRect as calculated by the
5809 function, and the dashed line represents the rectangle defined by
5810 \a x, \a y, \a width and \a height:
5811
5812 \table 100%
5813 \row
5814 \li \inlineimage qpainter-text-bounds.png
5815 \li \snippet code/src_gui_painting_qpainter.cpp drawText
5816 \endtable
5817
5818 The \a flags argument is a bitwise OR of the following flags:
5819
5820 \list
5821 \li Qt::AlignLeft
5822 \li Qt::AlignRight
5823 \li Qt::AlignHCenter
5824 \li Qt::AlignJustify
5825 \li Qt::AlignTop
5826 \li Qt::AlignBottom
5827 \li Qt::AlignVCenter
5828 \li Qt::AlignCenter
5829 \li Qt::TextSingleLine
5830 \li Qt::TextExpandTabs
5831 \li Qt::TextShowMnemonic
5832 \li Qt::TextWordWrap
5833 \endlist
5834
5835 By default, QPainter draws text anti-aliased.
5836
5837 \note The y-position is used as the top of the font.
5838
5839 \sa Qt::AlignmentFlag, Qt::TextFlag, setFont(), setPen()
5840*/
5841
5842/*!
5843 \fn void QPainter::drawText(const QRectF &rectangle, const QString &text,
5844 const QTextOption &option)
5845 \overload
5846
5847 Draws the given \a text in the \a rectangle specified using the \a option
5848 to control its positioning, direction, and orientation. The options given
5849 in \a option override those set on the QPainter object itself.
5850
5851 By default, QPainter draws text anti-aliased.
5852
5853 \note The y-coordinate of \a rectangle is used as the top of the font.
5854
5855 \sa setFont(), setPen()
5856*/
5857void QPainter::drawText(const QRectF &r, const QString &text, const QTextOption &o)
5858{
5859#ifdef QT_DEBUG_DRAW
5860 if (qt_show_painter_debug_output)
5861 printf("QPainter::drawText(), r=[%.2f,%.2f,%.2f,%.2f], str='%s'\n",
5862 r.x(), r.y(), r.width(), r.height(), text.toLatin1().constData());
5863#endif
5864
5865 Q_D(QPainter);
5866
5867 if (!d->engine || text.size() == 0 || pen().style() == Qt::NoPen)
5868 return;
5869
5870 if (!d->extended)
5871 d->updateState(state&: d->state);
5872
5873 qt_format_text(font: d->state->font, r: r, tf: 0, option: &o, str: text, brect: nullptr, tabstops: 0, tabarray: nullptr, tabarraylen: 0, painter: this);
5874}
5875
5876/*!
5877 \fn void QPainter::drawTextItem(int x, int y, const QTextItem &ti)
5878
5879 \internal
5880 \overload
5881*/
5882
5883/*!
5884 \fn void QPainter::drawTextItem(const QPoint &p, const QTextItem &ti)
5885
5886 \internal
5887 \overload
5888
5889 Draws the text item \a ti at position \a p.
5890*/
5891
5892/*!
5893 \fn void QPainter::drawTextItem(const QPointF &p, const QTextItem &ti)
5894
5895 \internal
5896 \since 4.1
5897
5898 Draws the text item \a ti at position \a p.
5899
5900 This method ignores the painters background mode and
5901 color. drawText and qt_format_text have to do it themselves, as
5902 only they know the extents of the complete string.
5903
5904 It ignores the font set on the painter as the text item has one of its own.
5905
5906 The underline and strikeout parameters of the text items font are
5907 ignored as well. You'll need to pass in the correct flags to get
5908 underlining and strikeout.
5909*/
5910
5911static QPixmap generateWavyPixmap(qreal maxRadius, const QPen &pen)
5912{
5913 const qreal radiusBase = qMax(a: qreal(1), b: maxRadius);
5914
5915 QString key = "WaveUnderline-"_L1
5916 % pen.color().name()
5917 % HexString<qreal>(radiusBase)
5918 % HexString<qreal>(pen.widthF());
5919
5920 QPixmap pixmap;
5921 if (QPixmapCache::find(key, pixmap: &pixmap))
5922 return pixmap;
5923
5924 const qreal halfPeriod = qMax(a: qreal(2), b: qreal(radiusBase * 1.61803399)); // the golden ratio
5925 const int width = qCeil(v: 100 / (2 * halfPeriod)) * (2 * halfPeriod);
5926 const qreal radius = qFloor(v: radiusBase * 2) / 2.;
5927
5928 QPainterPath path;
5929
5930 qreal xs = 0;
5931 qreal ys = radius;
5932
5933 while (xs < width) {
5934 xs += halfPeriod;
5935 ys = -ys;
5936 path.quadTo(ctrlPtx: xs - halfPeriod / 2, ctrlPty: ys, endPtx: xs, endPty: 0);
5937 }
5938
5939 pixmap = QPixmap(width, radius * 2);
5940 pixmap.fill(fillColor: Qt::transparent);
5941 {
5942 QPen wavePen = pen;
5943 wavePen.setCapStyle(Qt::SquareCap);
5944
5945 // This is to protect against making the line too fat, as happens on OS X
5946 // due to it having a rather thick width for the regular underline.
5947 const qreal maxPenWidth = .8 * radius;
5948 if (wavePen.widthF() > maxPenWidth)
5949 wavePen.setWidthF(maxPenWidth);
5950
5951 QPainter imgPainter(&pixmap);
5952 imgPainter.setPen(wavePen);
5953 imgPainter.setRenderHint(hint: QPainter::Antialiasing);
5954 imgPainter.translate(dx: 0, dy: radius);
5955 imgPainter.drawPath(path);
5956 }
5957
5958 QPixmapCache::insert(key, pixmap);
5959
5960 return pixmap;
5961}
5962
5963static void drawTextItemDecoration(QPainter *painter, const QPointF &pos, const QFontEngine *fe, QTextEngine *textEngine,
5964 QTextCharFormat::UnderlineStyle underlineStyle,
5965 QTextItem::RenderFlags flags, qreal width,
5966 const QTextCharFormat &charFormat)
5967{
5968 if (underlineStyle == QTextCharFormat::NoUnderline
5969 && !(flags & (QTextItem::StrikeOut | QTextItem::Overline)))
5970 return;
5971
5972 const QPen oldPen = painter->pen();
5973 const QBrush oldBrush = painter->brush();
5974 painter->setBrush(Qt::NoBrush);
5975 QPen pen = oldPen;
5976 pen.setStyle(Qt::SolidLine);
5977 pen.setWidthF(fe->lineThickness().toReal());
5978 pen.setCapStyle(Qt::FlatCap);
5979
5980 QLineF line(qFloor(v: pos.x()), pos.y(), qFloor(v: pos.x() + width), pos.y());
5981
5982 const qreal underlineOffset = fe->underlinePosition().toReal();
5983
5984 if (underlineStyle == QTextCharFormat::SpellCheckUnderline) {
5985 QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme();
5986 if (theme)
5987 underlineStyle = QTextCharFormat::UnderlineStyle(theme->themeHint(hint: QPlatformTheme::SpellCheckUnderlineStyle).toInt());
5988 if (underlineStyle == QTextCharFormat::SpellCheckUnderline) // still not resolved
5989 underlineStyle = QTextCharFormat::WaveUnderline;
5990 }
5991
5992 if (underlineStyle == QTextCharFormat::WaveUnderline) {
5993 painter->save();
5994 painter->translate(dx: 0, dy: pos.y() + 1);
5995 qreal maxHeight = fe->descent().toReal() - qreal(1);
5996
5997 QColor uc = charFormat.underlineColor();
5998 if (uc.isValid())
5999 pen.setColor(uc);
6000
6001 // Adapt wave to underlineOffset or pen width, whatever is larger, to make it work on all platforms
6002 const QPixmap wave = generateWavyPixmap(maxRadius: qMin(a: qMax(a: underlineOffset, b: pen.widthF()), b: maxHeight / qreal(2.)), pen);
6003 const int descent = qFloor(v: maxHeight);
6004
6005 painter->setBrushOrigin(x: painter->brushOrigin().x(), y: 0);
6006 painter->fillRect(x: pos.x(), y: 0, w: qCeil(v: width), h: qMin(a: wave.height(), b: descent), b: wave);
6007 painter->restore();
6008 } else if (underlineStyle != QTextCharFormat::NoUnderline) {
6009 // Deliberately ceil the offset to avoid the underline coming too close to
6010 // the text above it, but limit it to stay within descent.
6011 qreal adjustedUnderlineOffset = std::ceil(x: underlineOffset) + 0.5;
6012 if (underlineOffset <= fe->descent().toReal())
6013 adjustedUnderlineOffset = qMin(a: adjustedUnderlineOffset, b: fe->descent().toReal() - qreal(0.5));
6014 const qreal underlinePos = pos.y() + adjustedUnderlineOffset;
6015 QColor uc = charFormat.underlineColor();
6016 if (uc.isValid())
6017 pen.setColor(uc);
6018
6019 pen.setStyle((Qt::PenStyle)(underlineStyle));
6020 painter->setPen(pen);
6021 QLineF underline(line.x1(), underlinePos, line.x2(), underlinePos);
6022 if (textEngine)
6023 textEngine->addUnderline(painter, line: underline);
6024 else
6025 painter->drawLine(l: underline);
6026 }
6027
6028 pen.setStyle(Qt::SolidLine);
6029 pen.setColor(oldPen.color());
6030
6031 if (flags & QTextItem::StrikeOut) {
6032 QLineF strikeOutLine = line;
6033 strikeOutLine.translate(adx: 0., ady: - fe->ascent().toReal() / 3.);
6034 QColor uc = charFormat.underlineColor();
6035 if (uc.isValid())
6036 pen.setColor(uc);
6037 painter->setPen(pen);
6038 if (textEngine)
6039 textEngine->addStrikeOut(painter, line: strikeOutLine);
6040 else
6041 painter->drawLine(l: strikeOutLine);
6042 }
6043
6044 if (flags & QTextItem::Overline) {
6045 QLineF overline = line;
6046 overline.translate(adx: 0., ady: - fe->ascent().toReal());
6047 QColor uc = charFormat.underlineColor();
6048 if (uc.isValid())
6049 pen.setColor(uc);
6050 painter->setPen(pen);
6051 if (textEngine)
6052 textEngine->addOverline(painter, line: overline);
6053 else
6054 painter->drawLine(l: overline);
6055 }
6056
6057 painter->setPen(oldPen);
6058 painter->setBrush(oldBrush);
6059}
6060
6061static void qt_draw_decoration_for_glyphs(QPainter *painter,
6062 const QPointF &decorationPosition,
6063 const glyph_t *glyphArray,
6064 const QFixedPoint *positions,
6065 int glyphCount,
6066 QFontEngine *fontEngine,
6067 bool underline,
6068 bool overline,
6069 bool strikeOut)
6070{
6071 if (!underline && !overline && !strikeOut)
6072 return;
6073
6074 QTextItem::RenderFlags flags;
6075 if (underline)
6076 flags |= QTextItem::Underline;
6077 if (overline)
6078 flags |= QTextItem::Overline;
6079 if (strikeOut)
6080 flags |= QTextItem::StrikeOut;
6081
6082 bool rtl = positions[glyphCount - 1].x < positions[0].x;
6083 QFixed baseline = positions[0].y;
6084 glyph_metrics_t gm = fontEngine->boundingBox(glyph: glyphArray[rtl ? 0 : glyphCount - 1]);
6085
6086 qreal width = rtl
6087 ? (positions[0].x + gm.xoff - positions[glyphCount - 1].x).toReal()
6088 : (positions[glyphCount - 1].x + gm.xoff - positions[0].x).toReal();
6089
6090 drawTextItemDecoration(painter,
6091 pos: QPointF(decorationPosition.x(), baseline.toReal()),
6092 fe: fontEngine,
6093 textEngine: nullptr, // textEngine
6094 underlineStyle: underline ? QTextCharFormat::SingleUnderline
6095 : QTextCharFormat::NoUnderline,
6096 flags,
6097 width,
6098 charFormat: QTextCharFormat());
6099}
6100
6101void QPainter::drawTextItem(const QPointF &p, const QTextItem &ti)
6102{
6103 Q_D(QPainter);
6104
6105 d->drawTextItem(p, ti: ti, textEngine: static_cast<QTextEngine *>(nullptr));
6106}
6107
6108void QPainterPrivate::drawTextItem(const QPointF &p, const QTextItem &_ti, QTextEngine *textEngine)
6109{
6110#ifdef QT_DEBUG_DRAW
6111 if (qt_show_painter_debug_output)
6112 printf("QPainter::drawTextItem(), pos=[%.f,%.f], str='%s'\n",
6113 p.x(), p.y(), qPrintable(_ti.text()));
6114#endif
6115
6116 Q_Q(QPainter);
6117
6118 if (!engine)
6119 return;
6120
6121 QTextItemInt &ti = const_cast<QTextItemInt &>(static_cast<const QTextItemInt &>(_ti));
6122
6123 if (!extended && state->bgMode == Qt::OpaqueMode) {
6124 QRectF rect(p.x(), p.y() - ti.ascent.toReal(), ti.width.toReal(), (ti.ascent + ti.descent).toReal());
6125 q->fillRect(rect, state->bgBrush);
6126 }
6127
6128 if (q->pen().style() == Qt::NoPen)
6129 return;
6130
6131 const QPainter::RenderHints oldRenderHints = state->renderHints;
6132 if (!(state->renderHints & QPainter::Antialiasing) && state->matrix.type() >= QTransform::TxScale) {
6133 // draw antialias decoration (underline/overline/strikeout) with
6134 // transformed text
6135
6136 bool aa = true;
6137 const QTransform &m = state->matrix;
6138 if (state->matrix.type() < QTransform::TxShear) {
6139 bool isPlain90DegreeRotation =
6140 (qFuzzyIsNull(d: m.m11())
6141 && qFuzzyIsNull(d: m.m12() - qreal(1))
6142 && qFuzzyIsNull(d: m.m21() + qreal(1))
6143 && qFuzzyIsNull(d: m.m22())
6144 )
6145 ||
6146 (qFuzzyIsNull(d: m.m11() + qreal(1))
6147 && qFuzzyIsNull(d: m.m12())
6148 && qFuzzyIsNull(d: m.m21())
6149 && qFuzzyIsNull(d: m.m22() + qreal(1))
6150 )
6151 ||
6152 (qFuzzyIsNull(d: m.m11())
6153 && qFuzzyIsNull(d: m.m12() + qreal(1))
6154 && qFuzzyIsNull(d: m.m21() - qreal(1))
6155 && qFuzzyIsNull(d: m.m22())
6156 )
6157 ;
6158 aa = !isPlain90DegreeRotation;
6159 }
6160 if (aa)
6161 q->setRenderHint(hint: QPainter::Antialiasing, on: true);
6162 }
6163
6164 if (!extended)
6165 updateState(state);
6166
6167 if (!ti.glyphs.numGlyphs) {
6168 drawTextItemDecoration(painter: q, pos: p, fe: ti.fontEngine, textEngine, underlineStyle: ti.underlineStyle,
6169 flags: ti.flags, width: ti.width.toReal(), charFormat: ti.charFormat);
6170 } else if (ti.fontEngine->type() == QFontEngine::Multi) {
6171 QFontEngineMulti *multi = static_cast<QFontEngineMulti *>(ti.fontEngine);
6172
6173 const QGlyphLayout &glyphs = ti.glyphs;
6174 int which = glyphs.glyphs[0] >> 24;
6175
6176 qreal x = p.x();
6177 qreal y = p.y();
6178
6179 bool rtl = ti.flags & QTextItem::RightToLeft;
6180 if (rtl)
6181 x += ti.width.toReal();
6182
6183 int start = 0;
6184 int end, i;
6185 for (end = 0; end < ti.glyphs.numGlyphs; ++end) {
6186 const int e = glyphs.glyphs[end] >> 24;
6187 if (e == which)
6188 continue;
6189
6190
6191 multi->ensureEngineAt(at: which);
6192 QTextItemInt ti2 = ti.midItem(fontEngine: multi->engine(at: which), firstGlyphIndex: start, numGlyphs: end - start);
6193 ti2.width = 0;
6194 // set the high byte to zero and calc the width
6195 for (i = start; i < end; ++i) {
6196 glyphs.glyphs[i] = glyphs.glyphs[i] & 0xffffff;
6197 ti2.width += ti.glyphs.effectiveAdvance(item: i);
6198 }
6199
6200 if (rtl)
6201 x -= ti2.width.toReal();
6202
6203 if (extended)
6204 extended->drawTextItem(p: QPointF(x, y), textItem: ti2);
6205 else
6206 engine->drawTextItem(p: QPointF(x, y), textItem: ti2);
6207 drawTextItemDecoration(painter: q, pos: QPointF(x, y), fe: ti2.fontEngine, textEngine, underlineStyle: ti2.underlineStyle,
6208 flags: ti2.flags, width: ti2.width.toReal(), charFormat: ti2.charFormat);
6209
6210 if (!rtl)
6211 x += ti2.width.toReal();
6212
6213 // reset the high byte for all glyphs and advance to the next sub-string
6214 const int hi = which << 24;
6215 for (i = start; i < end; ++i) {
6216 glyphs.glyphs[i] = hi | glyphs.glyphs[i];
6217 }
6218
6219 // change engine
6220 start = end;
6221 which = e;
6222 }
6223
6224 multi->ensureEngineAt(at: which);
6225 QTextItemInt ti2 = ti.midItem(fontEngine: multi->engine(at: which), firstGlyphIndex: start, numGlyphs: end - start);
6226 ti2.width = 0;
6227 // set the high byte to zero and calc the width
6228 for (i = start; i < end; ++i) {
6229 glyphs.glyphs[i] = glyphs.glyphs[i] & 0xffffff;
6230 ti2.width += ti.glyphs.effectiveAdvance(item: i);
6231 }
6232
6233 if (rtl)
6234 x -= ti2.width.toReal();
6235
6236 if (extended)
6237 extended->drawTextItem(p: QPointF(x, y), textItem: ti2);
6238 else
6239 engine->drawTextItem(p: QPointF(x,y), textItem: ti2);
6240 drawTextItemDecoration(painter: q, pos: QPointF(x, y), fe: ti2.fontEngine, textEngine, underlineStyle: ti2.underlineStyle,
6241 flags: ti2.flags, width: ti2.width.toReal(), charFormat: ti2.charFormat);
6242
6243 // reset the high byte for all glyphs
6244 const int hi = which << 24;
6245 for (i = start; i < end; ++i)
6246 glyphs.glyphs[i] = hi | glyphs.glyphs[i];
6247
6248 } else {
6249 if (extended)
6250 extended->drawTextItem(p, textItem: ti);
6251 else
6252 engine->drawTextItem(p, textItem: ti);
6253 drawTextItemDecoration(painter: q, pos: p, fe: ti.fontEngine, textEngine, underlineStyle: ti.underlineStyle,
6254 flags: ti.flags, width: ti.width.toReal(), charFormat: ti.charFormat);
6255 }
6256
6257 if (state->renderHints != oldRenderHints) {
6258 state->renderHints = oldRenderHints;
6259 if (extended)
6260 extended->renderHintsChanged();
6261 else
6262 state->dirtyFlags |= QPaintEngine::DirtyHints;
6263 }
6264}
6265
6266/*!
6267 \fn QRectF QPainter::boundingRect(const QRectF &rectangle, int flags, const QString &text)
6268
6269 Returns the bounding rectangle of the \a text as it will appear
6270 when drawn inside the given \a rectangle with the specified \a
6271 flags using the currently set font(); i.e the function tells you
6272 where the drawText() function will draw when given the same
6273 arguments.
6274
6275 If the \a text does not fit within the given \a rectangle using
6276 the specified \a flags, the function returns the required
6277 rectangle.
6278
6279 The \a flags argument is a bitwise OR of the following flags:
6280 \list
6281 \li Qt::AlignLeft
6282 \li Qt::AlignRight
6283 \li Qt::AlignHCenter
6284 \li Qt::AlignTop
6285 \li Qt::AlignBottom
6286 \li Qt::AlignVCenter
6287 \li Qt::AlignCenter
6288 \li Qt::TextSingleLine
6289 \li Qt::TextExpandTabs
6290 \li Qt::TextShowMnemonic
6291 \li Qt::TextWordWrap
6292 \li Qt::TextIncludeTrailingSpaces
6293 \endlist
6294 If several of the horizontal or several of the vertical alignment
6295 flags are set, the resulting alignment is undefined.
6296
6297 \sa drawText(), Qt::Alignment, Qt::TextFlag
6298*/
6299
6300/*!
6301 \fn QRect QPainter::boundingRect(const QRect &rectangle, int flags,
6302 const QString &text)
6303
6304 \overload
6305
6306 Returns the bounding rectangle of the \a text as it will appear
6307 when drawn inside the given \a rectangle with the specified \a
6308 flags using the currently set font().
6309*/
6310
6311/*!
6312 \fn QRect QPainter::boundingRect(int x, int y, int w, int h, int flags,
6313 const QString &text);
6314
6315 \overload
6316
6317 Returns the bounding rectangle of the given \a text as it will
6318 appear when drawn inside the rectangle beginning at the point
6319 (\a{x}, \a{y}) with width \a w and height \a h.
6320*/
6321QRect QPainter::boundingRect(const QRect &rect, int flags, const QString &str)
6322{
6323 if (str.isEmpty())
6324 return QRect(rect.x(),rect.y(), 0,0);
6325 QRect brect;
6326 drawText(r: rect, flags: flags | Qt::TextDontPrint, str, br: &brect);
6327 return brect;
6328}
6329
6330
6331
6332QRectF QPainter::boundingRect(const QRectF &rect, int flags, const QString &str)
6333{
6334 if (str.isEmpty())
6335 return QRectF(rect.x(),rect.y(), 0,0);
6336 QRectF brect;
6337 drawText(r: rect, flags: flags | Qt::TextDontPrint, str, br: &brect);
6338 return brect;
6339}
6340
6341/*!
6342 \fn QRectF QPainter::boundingRect(const QRectF &rectangle,
6343 const QString &text, const QTextOption &option)
6344
6345 \overload
6346
6347 Instead of specifying flags as a bitwise OR of the
6348 Qt::AlignmentFlag and Qt::TextFlag, this overloaded function takes
6349 an \a option argument. The QTextOption class provides a
6350 description of general rich text properties.
6351
6352 \sa QTextOption
6353*/
6354QRectF QPainter::boundingRect(const QRectF &r, const QString &text, const QTextOption &o)
6355{
6356 Q_D(QPainter);
6357
6358 if (!d->engine || text.size() == 0)
6359 return QRectF(r.x(),r.y(), 0,0);
6360
6361 QRectF br;
6362 qt_format_text(font: d->state->font, r: r, tf: Qt::TextDontPrint, option: &o, str: text, brect: &br, tabstops: 0, tabarray: nullptr, tabarraylen: 0, painter: this);
6363 return br;
6364}
6365
6366/*!
6367 \fn void QPainter::drawTiledPixmap(const QRectF &rectangle, const QPixmap &pixmap, const QPointF &position)
6368
6369 Draws a tiled \a pixmap, inside the given \a rectangle with its
6370 origin at the given \a position.
6371
6372 Calling drawTiledPixmap() is similar to calling drawPixmap()
6373 several times to fill (tile) an area with a pixmap, but is
6374 potentially much more efficient depending on the underlying window
6375 system.
6376
6377 drawTiledPixmap() will produce the same visual tiling pattern on
6378 high-dpi displays (with devicePixelRatio > 1), compared to normal-
6379 dpi displays. Set the devicePixelRatio on the \a pixmap to control
6380 the tile size. For example, setting it to 2 halves the tile width
6381 and height (on both 1x and 2x displays), and produces high-resolution
6382 output on 2x displays.
6383
6384 The \a position offset is always in the painter coordinate system,
6385 indepentent of display devicePixelRatio.
6386
6387 \sa drawPixmap()
6388*/
6389void QPainter::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &sp)
6390{
6391#ifdef QT_DEBUG_DRAW
6392 if (qt_show_painter_debug_output)
6393 printf("QPainter::drawTiledPixmap(), target=[%.2f,%.2f,%.2f,%.2f], pix=[%d,%d], offset=[%.2f,%.2f]\n",
6394 r.x(), r.y(), r.width(), r.height(),
6395 pixmap.width(), pixmap.height(),
6396 sp.x(), sp.y());
6397#endif
6398
6399 Q_D(QPainter);
6400 if (!d->engine || pixmap.isNull() || r.isEmpty())
6401 return;
6402
6403#ifndef QT_NO_DEBUG
6404 qt_painter_thread_test(devType: d->device->devType(), engineType: d->engine->type(), what: "drawTiledPixmap()");
6405#endif
6406
6407 qreal sw = pixmap.width();
6408 qreal sh = pixmap.height();
6409 qreal sx = sp.x();
6410 qreal sy = sp.y();
6411 if (sx < 0)
6412 sx = qRound(d: sw) - qRound(d: -sx) % qRound(d: sw);
6413 else
6414 sx = qRound(d: sx) % qRound(d: sw);
6415 if (sy < 0)
6416 sy = qRound(d: sh) - -qRound(d: sy) % qRound(d: sh);
6417 else
6418 sy = qRound(d: sy) % qRound(d: sh);
6419
6420
6421 if (d->extended) {
6422 d->extended->drawTiledPixmap(r, pixmap, s: QPointF(sx, sy));
6423 return;
6424 }
6425
6426 if (d->state->bgMode == Qt::OpaqueMode && pixmap.isQBitmap())
6427 fillRect(r, d->state->bgBrush);
6428
6429 d->updateState(state&: d->state);
6430 if ((d->state->matrix.type() > QTransform::TxTranslate
6431 && !d->engine->hasFeature(feature: QPaintEngine::PixmapTransform))
6432 || (d->state->opacity != 1.0 && !d->engine->hasFeature(feature: QPaintEngine::ConstantOpacity)))
6433 {
6434 save();
6435 setBackgroundMode(Qt::TransparentMode);
6436 setRenderHint(hint: Antialiasing, on: renderHints() & SmoothPixmapTransform);
6437 setBrush(QBrush(d->state->pen.color(), pixmap));
6438 setPen(Qt::NoPen);
6439
6440 // If there is no rotation involved we have to make sure we use the
6441 // antialiased and not the aliased coordinate system by rounding the coordinates.
6442 if (d->state->matrix.type() <= QTransform::TxScale) {
6443 const QPointF p = roundInDeviceCoordinates(p: r.topLeft(), m: d->state->matrix);
6444
6445 if (d->state->matrix.type() <= QTransform::TxTranslate) {
6446 sx = qRound(d: sx);
6447 sy = qRound(d: sy);
6448 }
6449
6450 setBrushOrigin(QPointF(r.x()-sx, r.y()-sy));
6451 drawRect(rect: QRectF(p, r.size()));
6452 } else {
6453 setBrushOrigin(QPointF(r.x()-sx, r.y()-sy));
6454 drawRect(rect: r);
6455 }
6456 restore();
6457 return;
6458 }
6459
6460 qreal x = r.x();
6461 qreal y = r.y();
6462 if (d->state->matrix.type() == QTransform::TxTranslate
6463 && !d->engine->hasFeature(feature: QPaintEngine::PixmapTransform)) {
6464 x += d->state->matrix.dx();
6465 y += d->state->matrix.dy();
6466 }
6467
6468 d->engine->drawTiledPixmap(r: QRectF(x, y, r.width(), r.height()), pixmap, s: QPointF(sx, sy));
6469}
6470
6471/*!
6472 \fn void QPainter::drawTiledPixmap(const QRect &rectangle, const QPixmap &pixmap,
6473 const QPoint &position = QPoint())
6474 \overload
6475
6476 Draws a tiled \a pixmap, inside the given \a rectangle with its
6477 origin at the given \a position.
6478*/
6479
6480/*!
6481 \fn void QPainter::drawTiledPixmap(int x, int y, int width, int height, const
6482 QPixmap &pixmap, int sx, int sy);
6483 \overload
6484
6485 Draws a tiled \a pixmap in the specified rectangle.
6486
6487 (\a{x}, \a{y}) specifies the top-left point in the paint device
6488 that is to be drawn onto; with the given \a width and \a
6489 height. (\a{sx}, \a{sy}) specifies the top-left point in the \a
6490 pixmap that is to be drawn; this defaults to (0, 0).
6491*/
6492
6493#ifndef QT_NO_PICTURE
6494
6495/*!
6496 \fn void QPainter::drawPicture(const QPointF &point, const QPicture &picture)
6497
6498 Replays the given \a picture at the given \a point.
6499
6500 The QPicture class is a paint device that records and replays
6501 QPainter commands. A picture serializes the painter commands to an
6502 IO device in a platform-independent format. Everything that can be
6503 painted on a widget or pixmap can also be stored in a picture.
6504
6505 This function does exactly the same as QPicture::play() when
6506 called with \a point = QPointF(0, 0).
6507
6508 \note The state of the painter is preserved by this function.
6509
6510 \table 100%
6511 \row
6512 \li
6513 \snippet code/src_gui_painting_qpainter.cpp 18
6514 \endtable
6515
6516 \sa QPicture::play()
6517*/
6518
6519void QPainter::drawPicture(const QPointF &p, const QPicture &picture)
6520{
6521 Q_D(QPainter);
6522
6523 if (!d->engine) {
6524 qWarning(msg: "QPainter::drawPicture: Painter not active");
6525 return;
6526 }
6527
6528 if (!d->extended)
6529 d->updateState(state&: d->state);
6530
6531 save();
6532 translate(offset: p);
6533 const_cast<QPicture *>(&picture)->play(p: this);
6534 restore();
6535}
6536
6537/*!
6538 \fn void QPainter::drawPicture(const QPoint &point, const QPicture &picture)
6539 \overload
6540
6541 Replays the given \a picture at the given \a point.
6542*/
6543
6544/*!
6545 \fn void QPainter::drawPicture(int x, int y, const QPicture &picture)
6546 \overload
6547
6548 Draws the given \a picture at point (\a x, \a y).
6549*/
6550
6551#endif // QT_NO_PICTURE
6552
6553/*!
6554 \fn void QPainter::eraseRect(const QRectF &rectangle)
6555
6556 Erases the area inside the given \a rectangle. Equivalent to
6557 calling
6558 \snippet code/src_gui_painting_qpainter.cpp 19
6559
6560 \sa fillRect()
6561*/
6562void QPainter::eraseRect(const QRectF &r)
6563{
6564 Q_D(QPainter);
6565
6566 fillRect(r, d->state->bgBrush);
6567}
6568
6569static inline bool needsResolving(const QBrush &brush)
6570{
6571 Qt::BrushStyle s = brush.style();
6572 return ((s == Qt::LinearGradientPattern || s == Qt::RadialGradientPattern ||
6573 s == Qt::ConicalGradientPattern) &&
6574 (brush.gradient()->coordinateMode() == QGradient::ObjectBoundingMode ||
6575 brush.gradient()->coordinateMode() == QGradient::ObjectMode));
6576}
6577
6578/*!
6579 \fn void QPainter::eraseRect(const QRect &rectangle)
6580 \overload
6581
6582 Erases the area inside the given \a rectangle.
6583*/
6584
6585/*!
6586 \fn void QPainter::eraseRect(int x, int y, int width, int height)
6587 \overload
6588
6589 Erases the area inside the rectangle beginning at (\a x, \a y)
6590 with the given \a width and \a height.
6591*/
6592
6593
6594/*!
6595 \fn void QPainter::fillRect(int x, int y, int width, int height, Qt::BrushStyle style)
6596 \overload
6597
6598 Fills the rectangle beginning at (\a{x}, \a{y}) with the given \a
6599 width and \a height, using the brush \a style specified.
6600
6601 \since 4.5
6602*/
6603
6604/*!
6605 \fn void QPainter::fillRect(const QRect &rectangle, Qt::BrushStyle style)
6606 \overload
6607
6608 Fills the given \a rectangle with the brush \a style specified.
6609
6610 \since 4.5
6611*/
6612
6613/*!
6614 \fn void QPainter::fillRect(const QRectF &rectangle, Qt::BrushStyle style)
6615 \overload
6616
6617 Fills the given \a rectangle with the brush \a style specified.
6618
6619 \since 4.5
6620*/
6621
6622/*!
6623 \fn void QPainter::fillRect(const QRectF &rectangle, const QBrush &brush)
6624
6625 Fills the given \a rectangle with the \a brush specified.
6626
6627 Alternatively, you can specify a QColor instead of a QBrush; the
6628 QBrush constructor (taking a QColor argument) will automatically
6629 create a solid pattern brush.
6630
6631 \sa drawRect()
6632*/
6633void QPainter::fillRect(const QRectF &r, const QBrush &brush)
6634{
6635 Q_D(QPainter);
6636
6637 if (!d->engine) {
6638 qWarning(msg: "QPainter::fillRect: Painter not active");
6639 return;
6640 }
6641
6642 if (d->extended && !needsEmulation(brush)) {
6643 d->extended->fillRect(rect: r, brush);
6644 return;
6645 }
6646
6647 QPen oldPen = pen();
6648 QBrush oldBrush = this->brush();
6649 setPen(Qt::NoPen);
6650 if (brush.style() == Qt::SolidPattern) {
6651 d->colorBrush.setStyle(Qt::SolidPattern);
6652 d->colorBrush.setColor(brush.color());
6653 setBrush(d->colorBrush);
6654 } else {
6655 setBrush(brush);
6656 }
6657
6658 drawRect(rect: r);
6659 setBrush(oldBrush);
6660 setPen(oldPen);
6661}
6662
6663/*!
6664 \fn void QPainter::fillRect(const QRect &rectangle, const QBrush &brush)
6665 \overload
6666
6667 Fills the given \a rectangle with the specified \a brush.
6668*/
6669
6670void QPainter::fillRect(const QRect &r, const QBrush &brush)
6671{
6672 Q_D(QPainter);
6673
6674 if (!d->engine) {
6675 qWarning(msg: "QPainter::fillRect: Painter not active");
6676 return;
6677 }
6678
6679 if (d->extended && !needsEmulation(brush)) {
6680 d->extended->fillRect(rect: r, brush);
6681 return;
6682 }
6683
6684 QPen oldPen = pen();
6685 QBrush oldBrush = this->brush();
6686 setPen(Qt::NoPen);
6687 if (brush.style() == Qt::SolidPattern) {
6688 d->colorBrush.setStyle(Qt::SolidPattern);
6689 d->colorBrush.setColor(brush.color());
6690 setBrush(d->colorBrush);
6691 } else {
6692 setBrush(brush);
6693 }
6694
6695 drawRect(r);
6696 setBrush(oldBrush);
6697 setPen(oldPen);
6698}
6699
6700
6701
6702/*!
6703 \fn void QPainter::fillRect(const QRect &rectangle, const QColor &color)
6704 \overload
6705
6706 Fills the given \a rectangle with the \a color specified.
6707
6708 \since 4.5
6709*/
6710void QPainter::fillRect(const QRect &r, const QColor &color)
6711{
6712 Q_D(QPainter);
6713
6714 if (!d->engine) {
6715 qWarning(msg: "QPainter::fillRect: Painter not active");
6716 return;
6717 }
6718
6719 if (d->extended) {
6720 d->extended->fillRect(rect: r, color);
6721 return;
6722 }
6723
6724 fillRect(r, brush: QBrush(color));
6725}
6726
6727
6728/*!
6729 \fn void QPainter::fillRect(const QRectF &rectangle, const QColor &color)
6730 \overload
6731
6732 Fills the given \a rectangle with the \a color specified.
6733
6734 \since 4.5
6735*/
6736void QPainter::fillRect(const QRectF &r, const QColor &color)
6737{
6738 Q_D(QPainter);
6739
6740 if (!d->engine)
6741 return;
6742
6743 if (d->extended) {
6744 d->extended->fillRect(rect: r, color);
6745 return;
6746 }
6747
6748 fillRect(r, brush: QBrush(color));
6749}
6750
6751/*!
6752 \fn void QPainter::fillRect(int x, int y, int width, int height, const QBrush &brush)
6753
6754 \overload
6755
6756 Fills the rectangle beginning at (\a{x}, \a{y}) with the given \a
6757 width and \a height, using the given \a brush.
6758*/
6759
6760/*!
6761 \fn void QPainter::fillRect(int x, int y, int width, int height, const QColor &color)
6762
6763 \overload
6764
6765 Fills the rectangle beginning at (\a{x}, \a{y}) with the given \a
6766 width and \a height, using the given \a color.
6767
6768 \since 4.5
6769*/
6770
6771/*!
6772 \fn void QPainter::fillRect(int x, int y, int width, int height, Qt::GlobalColor color)
6773
6774 \overload
6775
6776 Fills the rectangle beginning at (\a{x}, \a{y}) with the given \a
6777 width and \a height, using the given \a color.
6778
6779 \since 4.5
6780*/
6781
6782/*!
6783 \fn void QPainter::fillRect(const QRect &rectangle, Qt::GlobalColor color);
6784
6785 \overload
6786
6787 Fills the given \a rectangle with the specified \a color.
6788
6789 \since 4.5
6790*/
6791
6792/*!
6793 \fn void QPainter::fillRect(const QRectF &rectangle, Qt::GlobalColor color);
6794
6795 \overload
6796
6797 Fills the given \a rectangle with the specified \a color.
6798
6799 \since 4.5
6800*/
6801
6802/*!
6803 \fn void QPainter::fillRect(int x, int y, int width, int height, QGradient::Preset preset)
6804
6805 \overload
6806
6807 Fills the rectangle beginning at (\a{x}, \a{y}) with the given \a
6808 width and \a height, using the given gradient \a preset.
6809
6810 \since 5.12
6811*/
6812
6813/*!
6814 \fn void QPainter::fillRect(const QRect &rectangle, QGradient::Preset preset);
6815
6816 \overload
6817
6818 Fills the given \a rectangle with the specified gradient \a preset.
6819
6820 \since 5.12
6821*/
6822
6823/*!
6824 \fn void QPainter::fillRect(const QRectF &rectangle, QGradient::Preset preset);
6825
6826 \overload
6827
6828 Fills the given \a rectangle with the specified gradient \a preset.
6829
6830 \since 5.12
6831*/
6832
6833/*!
6834 Sets the given render \a hint on the painter if \a on is true;
6835 otherwise clears the render hint.
6836
6837 \sa setRenderHints(), renderHints(), {QPainter#Rendering
6838 Quality}{Rendering Quality}
6839*/
6840void QPainter::setRenderHint(RenderHint hint, bool on)
6841{
6842#ifdef QT_DEBUG_DRAW
6843 if (qt_show_painter_debug_output)
6844 printf("QPainter::setRenderHint: hint=%x, %s\n", hint, on ? "on" : "off");
6845#endif
6846
6847#ifndef QT_NO_DEBUG
6848 static const bool antialiasingDisabled = qEnvironmentVariableIntValue(varName: "QT_NO_ANTIALIASING");
6849 if (hint == QPainter::Antialiasing && antialiasingDisabled)
6850 return;
6851#endif
6852
6853 setRenderHints(hints: hint, on);
6854}
6855
6856/*!
6857 \since 4.2
6858
6859 Sets the given render \a hints on the painter if \a on is true;
6860 otherwise clears the render hints.
6861
6862 \sa setRenderHint(), renderHints(), {QPainter#Rendering
6863 Quality}{Rendering Quality}
6864*/
6865
6866void QPainter::setRenderHints(RenderHints hints, bool on)
6867{
6868 Q_D(QPainter);
6869
6870 if (!d->engine) {
6871 qWarning(msg: "QPainter::setRenderHint: Painter must be active to set rendering hints");
6872 return;
6873 }
6874
6875 if (on)
6876 d->state->renderHints |= hints;
6877 else
6878 d->state->renderHints &= ~hints;
6879
6880 if (d->extended)
6881 d->extended->renderHintsChanged();
6882 else
6883 d->state->dirtyFlags |= QPaintEngine::DirtyHints;
6884}
6885
6886/*!
6887 Returns a flag that specifies the rendering hints that are set for
6888 this painter.
6889
6890 \sa testRenderHint(), {QPainter#Rendering Quality}{Rendering Quality}
6891*/
6892QPainter::RenderHints QPainter::renderHints() const
6893{
6894 Q_D(const QPainter);
6895
6896 if (!d->engine)
6897 return { };
6898
6899 return d->state->renderHints;
6900}
6901
6902/*!
6903 \fn bool QPainter::testRenderHint(RenderHint hint) const
6904 \since 4.3
6905
6906 Returns \c true if \a hint is set; otherwise returns \c false.
6907
6908 \sa renderHints(), setRenderHint()
6909*/
6910
6911/*!
6912 Returns \c true if view transformation is enabled; otherwise returns
6913 false.
6914
6915 \sa setViewTransformEnabled(), worldTransform()
6916*/
6917
6918bool QPainter::viewTransformEnabled() const
6919{
6920 Q_D(const QPainter);
6921 if (!d->engine) {
6922 qWarning(msg: "QPainter::viewTransformEnabled: Painter not active");
6923 return false;
6924 }
6925 return d->state->VxF;
6926}
6927
6928/*!
6929 \fn void QPainter::setWindow(const QRect &rectangle)
6930
6931 Sets the painter's window to the given \a rectangle, and enables
6932 view transformations.
6933
6934 The window rectangle is part of the view transformation. The
6935 window specifies the logical coordinate system. Its sister, the
6936 viewport(), specifies the device coordinate system.
6937
6938 The default window rectangle is the same as the device's
6939 rectangle.
6940
6941 \sa window(), viewTransformEnabled(), {Coordinate
6942 System#Window-Viewport Conversion}{Window-Viewport Conversion}
6943*/
6944
6945/*!
6946 \fn void QPainter::setWindow(int x, int y, int width, int height)
6947 \overload
6948
6949 Sets the painter's window to the rectangle beginning at (\a x, \a
6950 y) and the given \a width and \a height.
6951*/
6952
6953void QPainter::setWindow(const QRect &r)
6954{
6955#ifdef QT_DEBUG_DRAW
6956 if (qt_show_painter_debug_output)
6957 printf("QPainter::setWindow(), [%d,%d,%d,%d]\n", r.x(), r.y(), r.width(), r.height());
6958#endif
6959
6960 Q_D(QPainter);
6961
6962 if (!d->engine) {
6963 qWarning(msg: "QPainter::setWindow: Painter not active");
6964 return;
6965 }
6966
6967 d->state->wx = r.x();
6968 d->state->wy = r.y();
6969 d->state->ww = r.width();
6970 d->state->wh = r.height();
6971
6972 d->state->VxF = true;
6973 d->updateMatrix();
6974}
6975
6976/*!
6977 Returns the window rectangle.
6978
6979 \sa setWindow(), setViewTransformEnabled()
6980*/
6981
6982QRect QPainter::window() const
6983{
6984 Q_D(const QPainter);
6985 if (!d->engine) {
6986 qWarning(msg: "QPainter::window: Painter not active");
6987 return QRect();
6988 }
6989 return QRect(d->state->wx, d->state->wy, d->state->ww, d->state->wh);
6990}
6991
6992/*!
6993 \fn void QPainter::setViewport(const QRect &rectangle)
6994
6995 Sets the painter's viewport rectangle to the given \a rectangle,
6996 and enables view transformations.
6997
6998 The viewport rectangle is part of the view transformation. The
6999 viewport specifies the device coordinate system. Its sister, the
7000 window(), specifies the logical coordinate system.
7001
7002 The default viewport rectangle is the same as the device's
7003 rectangle.
7004
7005 \sa viewport(), viewTransformEnabled(), {Coordinate
7006 System#Window-Viewport Conversion}{Window-Viewport Conversion}
7007*/
7008
7009/*!
7010 \fn void QPainter::setViewport(int x, int y, int width, int height)
7011 \overload
7012
7013 Sets the painter's viewport rectangle to be the rectangle
7014 beginning at (\a x, \a y) with the given \a width and \a height.
7015*/
7016
7017void QPainter::setViewport(const QRect &r)
7018{
7019#ifdef QT_DEBUG_DRAW
7020 if (qt_show_painter_debug_output)
7021 printf("QPainter::setViewport(), [%d,%d,%d,%d]\n", r.x(), r.y(), r.width(), r.height());
7022#endif
7023
7024 Q_D(QPainter);
7025
7026 if (!d->engine) {
7027 qWarning(msg: "QPainter::setViewport: Painter not active");
7028 return;
7029 }
7030
7031 d->state->vx = r.x();
7032 d->state->vy = r.y();
7033 d->state->vw = r.width();
7034 d->state->vh = r.height();
7035
7036 d->state->VxF = true;
7037 d->updateMatrix();
7038}
7039
7040/*!
7041 Returns the viewport rectangle.
7042
7043 \sa setViewport(), setViewTransformEnabled()
7044*/
7045
7046QRect QPainter::viewport() const
7047{
7048 Q_D(const QPainter);
7049 if (!d->engine) {
7050 qWarning(msg: "QPainter::viewport: Painter not active");
7051 return QRect();
7052 }
7053 return QRect(d->state->vx, d->state->vy, d->state->vw, d->state->vh);
7054}
7055
7056/*!
7057 Enables view transformations if \a enable is true, or disables
7058 view transformations if \a enable is false.
7059
7060 \sa viewTransformEnabled(), {Coordinate System#Window-Viewport
7061 Conversion}{Window-Viewport Conversion}
7062*/
7063
7064void QPainter::setViewTransformEnabled(bool enable)
7065{
7066#ifdef QT_DEBUG_DRAW
7067 if (qt_show_painter_debug_output)
7068 printf("QPainter::setViewTransformEnabled(), enable=%d\n", enable);
7069#endif
7070
7071 Q_D(QPainter);
7072
7073 if (!d->engine) {
7074 qWarning(msg: "QPainter::setViewTransformEnabled: Painter not active");
7075 return;
7076 }
7077
7078 if (enable == d->state->VxF)
7079 return;
7080
7081 d->state->VxF = enable;
7082 d->updateMatrix();
7083}
7084
7085void qt_format_text(const QFont &fnt, const QRectF &_r,
7086 int tf, const QString& str, QRectF *brect,
7087 int tabstops, int *ta, int tabarraylen,
7088 QPainter *painter)
7089{
7090 qt_format_text(font: fnt, _r,
7091 tf, option: nullptr, str, brect,
7092 tabstops, tabarray: ta, tabarraylen,
7093 painter);
7094}
7095void qt_format_text(const QFont &fnt, const QRectF &_r,
7096 int tf, const QTextOption *option, const QString& str, QRectF *brect,
7097 int tabstops, int *ta, int tabarraylen,
7098 QPainter *painter)
7099{
7100
7101 Q_ASSERT( !((tf & ~Qt::TextDontPrint)!=0 && option!=nullptr) ); // we either have an option or flags
7102
7103 if (_r.isEmpty() && !(tf & Qt::TextDontClip)) {
7104 if (!brect)
7105 return;
7106 else
7107 tf |= Qt::TextDontPrint;
7108 }
7109
7110 if (option) {
7111 tf |= option->alignment();
7112 if (option->wrapMode() != QTextOption::NoWrap)
7113 tf |= Qt::TextWordWrap;
7114
7115 if (option->flags() & QTextOption::IncludeTrailingSpaces)
7116 tf |= Qt::TextIncludeTrailingSpaces;
7117
7118 if (option->tabStopDistance() >= 0 || !option->tabArray().isEmpty())
7119 tf |= Qt::TextExpandTabs;
7120 }
7121
7122 // we need to copy r here to protect against the case (&r == brect).
7123 QRectF r(_r);
7124
7125 bool dontclip = (tf & Qt::TextDontClip);
7126 bool wordwrap = (tf & Qt::TextWordWrap) || (tf & Qt::TextWrapAnywhere);
7127 bool singleline = (tf & Qt::TextSingleLine);
7128 bool showmnemonic = (tf & Qt::TextShowMnemonic);
7129 bool hidemnmemonic = (tf & Qt::TextHideMnemonic);
7130
7131 Qt::LayoutDirection layout_direction;
7132 if (tf & Qt::TextForceLeftToRight)
7133 layout_direction = Qt::LeftToRight;
7134 else if (tf & Qt::TextForceRightToLeft)
7135 layout_direction = Qt::RightToLeft;
7136 else if (option)
7137 layout_direction = option->textDirection();
7138 else if (painter)
7139 layout_direction = painter->layoutDirection();
7140 else
7141 layout_direction = Qt::LeftToRight;
7142
7143 tf = QGuiApplicationPrivate::visualAlignment(direction: layout_direction, alignment: QFlag(tf));
7144
7145 bool isRightToLeft = layout_direction == Qt::RightToLeft;
7146 bool expandtabs = ((tf & Qt::TextExpandTabs) &&
7147 (((tf & Qt::AlignLeft) && !isRightToLeft) ||
7148 ((tf & Qt::AlignRight) && isRightToLeft)));
7149
7150 if (!painter)
7151 tf |= Qt::TextDontPrint;
7152
7153 uint maxUnderlines = 0;
7154
7155 QFontMetricsF fm(fnt);
7156 QString text = str;
7157 int offset = 0;
7158start_lengthVariant:
7159 bool hasMoreLengthVariants = false;
7160 // compatible behaviour to the old implementation. Replace
7161 // tabs by spaces
7162 int old_offset = offset;
7163 for (; offset < text.size(); offset++) {
7164 QChar chr = text.at(i: offset);
7165 if (chr == u'\r' || (singleline && chr == u'\n')) {
7166 text[offset] = u' ';
7167 } else if (chr == u'\n') {
7168 text[offset] = QChar::LineSeparator;
7169 } else if (chr == u'&') {
7170 ++maxUnderlines;
7171 } else if (chr == u'\t') {
7172 if (!expandtabs) {
7173 text[offset] = u' ';
7174 } else if (!tabarraylen && !tabstops) {
7175 tabstops = qRound(d: fm.horizontalAdvance(u'x')*8);
7176 }
7177 } else if (chr == u'\x9c') {
7178 // string with multiple length variants
7179 hasMoreLengthVariants = true;
7180 break;
7181 }
7182 }
7183
7184 QList<QTextLayout::FormatRange> underlineFormats;
7185 int length = offset - old_offset;
7186 if ((hidemnmemonic || showmnemonic) && maxUnderlines > 0) {
7187 QChar *cout = text.data() + old_offset;
7188 QChar *cout0 = cout;
7189 QChar *cin = cout;
7190 int l = length;
7191 while (l) {
7192 if (*cin == u'&') {
7193 ++cin;
7194 --length;
7195 --l;
7196 if (!l)
7197 break;
7198 if (*cin != u'&' && !hidemnmemonic && !(tf & Qt::TextDontPrint)) {
7199 QTextLayout::FormatRange range;
7200 range.start = cout - cout0;
7201 range.length = 1;
7202 range.format.setFontUnderline(true);
7203 underlineFormats.append(t: range);
7204 }
7205#ifdef Q_OS_MAC
7206 } else if (hidemnmemonic && *cin == u'(' && l >= 4 &&
7207 cin[1] == u'&' && cin[2] != u'&' &&
7208 cin[3] == u')') {
7209 int n = 0;
7210 while ((cout - n) > cout0 && (cout - n - 1)->isSpace())
7211 ++n;
7212 cout -= n;
7213 cin += 4;
7214 length -= n + 4;
7215 l -= 4;
7216 continue;
7217#endif //Q_OS_MAC
7218 }
7219 *cout = *cin;
7220 ++cout;
7221 ++cin;
7222 --l;
7223 }
7224 }
7225
7226 qreal height = 0;
7227 qreal width = 0;
7228
7229 QString finalText = text.mid(position: old_offset, n: length);
7230 QStackTextEngine engine(finalText, fnt);
7231 if (option) {
7232 engine.option = *option;
7233 }
7234
7235 if (engine.option.tabStopDistance() < 0 && tabstops > 0)
7236 engine.option.setTabStopDistance(tabstops);
7237
7238 if (engine.option.tabs().isEmpty() && ta) {
7239 QList<qreal> tabs;
7240 tabs.reserve(asize: tabarraylen);
7241 for (int i = 0; i < tabarraylen; i++)
7242 tabs.append(t: qreal(ta[i]));
7243 engine.option.setTabArray(tabs);
7244 }
7245
7246 engine.option.setTextDirection(layout_direction);
7247 if (tf & Qt::AlignJustify)
7248 engine.option.setAlignment(Qt::AlignJustify);
7249 else
7250 engine.option.setAlignment(Qt::AlignLeft); // do not do alignment twice
7251
7252 if (!option && (tf & Qt::TextWrapAnywhere))
7253 engine.option.setWrapMode(QTextOption::WrapAnywhere);
7254
7255 if (tf & Qt::TextJustificationForced)
7256 engine.forceJustification = true;
7257 QTextLayout textLayout(&engine);
7258 textLayout.setCacheEnabled(true);
7259 textLayout.setFormats(underlineFormats);
7260
7261 if (finalText.isEmpty()) {
7262 height = fm.height();
7263 width = 0;
7264 tf |= Qt::TextDontPrint;
7265 } else {
7266 qreal lineWidth = 0x01000000;
7267 if (wordwrap || (tf & Qt::TextJustificationForced))
7268 lineWidth = qMax<qreal>(a: 0, b: r.width());
7269 if (!wordwrap)
7270 tf |= Qt::TextIncludeTrailingSpaces;
7271 textLayout.beginLayout();
7272
7273 qreal leading = fm.leading();
7274 height = -leading;
7275
7276 while (1) {
7277 QTextLine l = textLayout.createLine();
7278 if (!l.isValid())
7279 break;
7280
7281 l.setLineWidth(lineWidth);
7282 height += leading;
7283
7284 // Make sure lines are positioned on whole pixels
7285 height = qCeil(v: height);
7286 l.setPosition(QPointF(0., height));
7287 height += textLayout.engine()->lines[l.lineNumber()].height().toReal();
7288 width = qMax(a: width, b: l.naturalTextWidth());
7289 if (!dontclip && !brect && height >= r.height())
7290 break;
7291 }
7292 textLayout.endLayout();
7293 }
7294
7295 qreal yoff = 0;
7296 qreal xoff = 0;
7297 if (tf & Qt::AlignBottom)
7298 yoff = r.height() - height;
7299 else if (tf & Qt::AlignVCenter)
7300 yoff = (r.height() - height)/2;
7301
7302 if (tf & Qt::AlignRight)
7303 xoff = r.width() - width;
7304 else if (tf & Qt::AlignHCenter)
7305 xoff = (r.width() - width)/2;
7306
7307 QRectF bounds = QRectF(r.x() + xoff, r.y() + yoff, width, height);
7308
7309 if (hasMoreLengthVariants && !(tf & Qt::TextLongestVariant) && !r.contains(r: bounds)) {
7310 offset++;
7311 goto start_lengthVariant;
7312 }
7313 if (brect)
7314 *brect = bounds;
7315
7316 if (!(tf & Qt::TextDontPrint)) {
7317 bool restore = false;
7318 if (!dontclip && !r.contains(r: bounds)) {
7319 restore = true;
7320 painter->save();
7321 painter->setClipRect(rect: r, op: Qt::IntersectClip);
7322 }
7323
7324 for (int i = 0; i < textLayout.lineCount(); i++) {
7325 QTextLine line = textLayout.lineAt(i);
7326 QTextEngine *eng = textLayout.engine();
7327 eng->enableDelayDecorations();
7328
7329 qreal advance = line.horizontalAdvance();
7330 xoff = 0;
7331 if (tf & Qt::AlignRight) {
7332 xoff = r.width() - advance -
7333 eng->leadingSpaceWidth(line: eng->lines[line.lineNumber()]).toReal();
7334 }
7335 else if (tf & Qt::AlignHCenter)
7336 xoff = (r.width() - advance) / 2;
7337
7338 line.draw(painter, position: QPointF(r.x() + xoff, r.y() + yoff));
7339 eng->drawDecorations(painter);
7340 }
7341
7342 if (restore) {
7343 painter->restore();
7344 }
7345 }
7346}
7347
7348/*!
7349 Sets the layout direction used by the painter when drawing text,
7350 to the specified \a direction.
7351
7352 The default is Qt::LayoutDirectionAuto, which will implicitly determine the
7353 direction from the text drawn.
7354
7355 \sa QTextOption::setTextDirection(), layoutDirection(), drawText(), {QPainter#Settings}{Settings}
7356*/
7357void QPainter::setLayoutDirection(Qt::LayoutDirection direction)
7358{
7359 Q_D(QPainter);
7360 if (d->state)
7361 d->state->layoutDirection = direction;
7362}
7363
7364/*!
7365 Returns the layout direction used by the painter when drawing text.
7366
7367 \sa QTextOption::textDirection(), setLayoutDirection(), drawText(), {QPainter#Settings}{Settings}
7368*/
7369Qt::LayoutDirection QPainter::layoutDirection() const
7370{
7371 Q_D(const QPainter);
7372 return d->state ? d->state->layoutDirection : Qt::LayoutDirectionAuto;
7373}
7374
7375QPainterState::QPainterState(const QPainterState *s)
7376 : brushOrigin(s->brushOrigin), font(s->font), deviceFont(s->deviceFont),
7377 pen(s->pen), brush(s->brush), bgBrush(s->bgBrush),
7378 clipRegion(s->clipRegion), clipPath(s->clipPath),
7379 clipOperation(s->clipOperation),
7380 renderHints(s->renderHints), clipInfo(s->clipInfo),
7381 worldMatrix(s->worldMatrix), matrix(s->matrix), redirectionMatrix(s->redirectionMatrix),
7382 wx(s->wx), wy(s->wy), ww(s->ww), wh(s->wh),
7383 vx(s->vx), vy(s->vy), vw(s->vw), vh(s->vh),
7384 opacity(s->opacity), WxF(s->WxF), VxF(s->VxF),
7385 clipEnabled(s->clipEnabled), bgMode(s->bgMode), painter(s->painter),
7386 layoutDirection(s->layoutDirection),
7387 composition_mode(s->composition_mode),
7388 emulationSpecifier(s->emulationSpecifier), changeFlags(0)
7389{
7390 dirtyFlags = s->dirtyFlags;
7391}
7392
7393QPainterState::QPainterState()
7394 : brushOrigin(0, 0), WxF(false), VxF(false), clipEnabled(true),
7395 layoutDirection(QGuiApplication::layoutDirection())
7396{
7397}
7398
7399QPainterState::~QPainterState()
7400{
7401}
7402
7403void QPainterState::init(QPainter *p) {
7404 bgBrush = Qt::white;
7405 bgMode = Qt::TransparentMode;
7406 WxF = false;
7407 VxF = false;
7408 clipEnabled = true;
7409 wx = wy = ww = wh = 0;
7410 vx = vy = vw = vh = 0;
7411 painter = p;
7412 pen = QPen();
7413 brushOrigin = QPointF(0, 0);
7414 brush = QBrush();
7415 font = deviceFont = QFont();
7416 clipRegion = QRegion();
7417 clipPath = QPainterPath();
7418 clipOperation = Qt::NoClip;
7419 clipInfo.clear();
7420 worldMatrix.reset();
7421 matrix.reset();
7422 layoutDirection = QGuiApplication::layoutDirection();
7423 composition_mode = QPainter::CompositionMode_SourceOver;
7424 emulationSpecifier = 0;
7425 dirtyFlags = { };
7426 changeFlags = 0;
7427 renderHints = { };
7428 opacity = 1;
7429}
7430
7431/*!
7432 \fn void QPainter::drawImage(const QRectF &target, const QImage &image, const QRectF &source,
7433 Qt::ImageConversionFlags flags)
7434
7435 Draws the rectangular portion \a source of the given \a image
7436 into the \a target rectangle in the paint device.
7437
7438 \note The image is scaled to fit the rectangle, if both the image and rectangle size disagree.
7439 \note See \l{Drawing High Resolution Versions of Pixmaps and Images} on how this is affected
7440 by QImage::devicePixelRatio().
7441
7442 If the image needs to be modified to fit in a lower-resolution
7443 result (e.g. converting from 32-bit to 8-bit), use the \a flags to
7444 specify how you would prefer this to happen.
7445
7446 \table 100%
7447 \row
7448 \li
7449 \snippet code/src_gui_painting_qpainter.cpp 20
7450 \endtable
7451
7452 \sa drawPixmap(), QImage::devicePixelRatio()
7453*/
7454
7455/*!
7456 \fn void QPainter::drawImage(const QRect &target, const QImage &image, const QRect &source,
7457 Qt::ImageConversionFlags flags)
7458 \overload
7459
7460 Draws the rectangular portion \a source of the given \a image
7461 into the \a target rectangle in the paint device.
7462
7463 \note The image is scaled to fit the rectangle, if both the image and rectangle size disagree.
7464*/
7465
7466/*!
7467 \fn void QPainter::drawImage(const QPointF &point, const QImage &image)
7468
7469 \overload
7470
7471 Draws the given \a image at the given \a point.
7472*/
7473
7474/*!
7475 \fn void QPainter::drawImage(const QPoint &point, const QImage &image)
7476
7477 \overload
7478
7479 Draws the given \a image at the given \a point.
7480*/
7481
7482/*!
7483 \fn void QPainter::drawImage(const QPointF &point, const QImage &image, const QRectF &source,
7484 Qt::ImageConversionFlags flags = Qt::AutoColor)
7485
7486 \overload
7487
7488 Draws the rectangular portion \a source of the given \a image with
7489 its origin at the given \a point.
7490*/
7491
7492/*!
7493 \fn void QPainter::drawImage(const QPoint &point, const QImage &image, const QRect &source,
7494 Qt::ImageConversionFlags flags = Qt::AutoColor)
7495 \overload
7496
7497 Draws the rectangular portion \a source of the given \a image with
7498 its origin at the given \a point.
7499*/
7500
7501/*!
7502 \fn void QPainter::drawImage(const QRectF &rectangle, const QImage &image)
7503
7504 \overload
7505
7506 Draws the given \a image into the given \a rectangle.
7507
7508 \note The image is scaled to fit the rectangle, if both the image and rectangle size disagree.
7509*/
7510
7511/*!
7512 \fn void QPainter::drawImage(const QRect &rectangle, const QImage &image)
7513
7514 \overload
7515
7516 Draws the given \a image into the given \a rectangle.
7517
7518 \note The image is scaled to fit the rectangle, if both the image and rectangle size disagree.
7519*/
7520
7521/*!
7522 \fn void QPainter::drawImage(int x, int y, const QImage &image,
7523 int sx, int sy, int sw, int sh,
7524 Qt::ImageConversionFlags flags)
7525 \overload
7526
7527 Draws an image at (\a{x}, \a{y}) by copying a part of \a image into
7528 the paint device.
7529
7530 (\a{x}, \a{y}) specifies the top-left point in the paint device that is
7531 to be drawn onto. (\a{sx}, \a{sy}) specifies the top-left point in \a
7532 image that is to be drawn. The default is (0, 0).
7533
7534 (\a{sw}, \a{sh}) specifies the size of the image that is to be drawn.
7535 The default, (0, 0) (and negative) means all the way to the
7536 bottom-right of the image.
7537*/
7538
7539/*!
7540 \class QPaintEngineState
7541 \since 4.1
7542 \inmodule QtGui
7543
7544 \brief The QPaintEngineState class provides information about the
7545 active paint engine's current state.
7546 \reentrant
7547
7548 QPaintEngineState records which properties that have changed since
7549 the last time the paint engine was updated, as well as their
7550 current value.
7551
7552 Which properties that have changed can at any time be retrieved
7553 using the state() function. This function returns an instance of
7554 the QPaintEngine::DirtyFlags type which stores an OR combination
7555 of QPaintEngine::DirtyFlag values. The QPaintEngine::DirtyFlag
7556 enum defines whether a property has changed since the last update
7557 or not.
7558
7559 If a property is marked with a dirty flag, its current value can
7560 be retrieved using the corresponding get function:
7561
7562 \target GetFunction
7563
7564 \table
7565 \header \li Property Flag \li Current Property Value
7566 \row \li QPaintEngine::DirtyBackground \li backgroundBrush()
7567 \row \li QPaintEngine::DirtyBackgroundMode \li backgroundMode()
7568 \row \li QPaintEngine::DirtyBrush \li brush()
7569 \row \li QPaintEngine::DirtyBrushOrigin \li brushOrigin()
7570 \row \li QPaintEngine::DirtyClipRegion \e or QPaintEngine::DirtyClipPath
7571 \li clipOperation()
7572 \row \li QPaintEngine::DirtyClipPath \li clipPath()
7573 \row \li QPaintEngine::DirtyClipRegion \li clipRegion()
7574 \row \li QPaintEngine::DirtyCompositionMode \li compositionMode()
7575 \row \li QPaintEngine::DirtyFont \li font()
7576 \row \li QPaintEngine::DirtyTransform \li transform()
7577 \row \li QPaintEngine::DirtyClipEnabled \li isClipEnabled()
7578 \row \li QPaintEngine::DirtyPen \li pen()
7579 \row \li QPaintEngine::DirtyHints \li renderHints()
7580 \endtable
7581
7582 The QPaintEngineState class also provide the painter() function
7583 which returns a pointer to the painter that is currently updating
7584 the paint engine.
7585
7586 An instance of this class, representing the current state of the
7587 active paint engine, is passed as argument to the
7588 QPaintEngine::updateState() function. The only situation in which
7589 you will have to use this class directly is when implementing your
7590 own paint engine.
7591
7592 \sa QPaintEngine
7593*/
7594
7595
7596/*!
7597 \fn QPaintEngine::DirtyFlags QPaintEngineState::state() const
7598
7599 Returns a combination of flags identifying the set of properties
7600 that need to be updated when updating the paint engine's state
7601 (i.e. during a call to the QPaintEngine::updateState() function).
7602
7603 \sa QPaintEngine::updateState()
7604*/
7605
7606
7607/*!
7608 Returns the pen in the current paint engine state.
7609
7610 This variable should only be used when the state() returns a
7611 combination which includes the QPaintEngine::DirtyPen flag.
7612
7613 \sa state(), QPaintEngine::updateState()
7614*/
7615
7616QPen QPaintEngineState::pen() const
7617{
7618 return static_cast<const QPainterState *>(this)->pen;
7619}
7620
7621/*!
7622 Returns the brush in the current paint engine state.
7623
7624 This variable should only be used when the state() returns a
7625 combination which includes the QPaintEngine::DirtyBrush flag.
7626
7627 \sa state(), QPaintEngine::updateState()
7628*/
7629
7630QBrush QPaintEngineState::brush() const
7631{
7632 return static_cast<const QPainterState *>(this)->brush;
7633}
7634
7635/*!
7636 Returns the brush origin in the current paint engine state.
7637
7638 This variable should only be used when the state() returns a
7639 combination which includes the QPaintEngine::DirtyBrushOrigin flag.
7640
7641 \sa state(), QPaintEngine::updateState()
7642*/
7643
7644QPointF QPaintEngineState::brushOrigin() const
7645{
7646 return static_cast<const QPainterState *>(this)->brushOrigin;
7647}
7648
7649/*!
7650 Returns the background brush in the current paint engine state.
7651
7652 This variable should only be used when the state() returns a
7653 combination which includes the QPaintEngine::DirtyBackground flag.
7654
7655 \sa state(), QPaintEngine::updateState()
7656*/
7657
7658QBrush QPaintEngineState::backgroundBrush() const
7659{
7660 return static_cast<const QPainterState *>(this)->bgBrush;
7661}
7662
7663/*!
7664 Returns the background mode in the current paint engine
7665 state.
7666
7667 This variable should only be used when the state() returns a
7668 combination which includes the QPaintEngine::DirtyBackgroundMode flag.
7669
7670 \sa state(), QPaintEngine::updateState()
7671*/
7672
7673Qt::BGMode QPaintEngineState::backgroundMode() const
7674{
7675 return static_cast<const QPainterState *>(this)->bgMode;
7676}
7677
7678/*!
7679 Returns the font in the current paint engine
7680 state.
7681
7682 This variable should only be used when the state() returns a
7683 combination which includes the QPaintEngine::DirtyFont flag.
7684
7685 \sa state(), QPaintEngine::updateState()
7686*/
7687
7688QFont QPaintEngineState::font() const
7689{
7690 return static_cast<const QPainterState *>(this)->font;
7691}
7692
7693/*!
7694 \since 4.3
7695
7696 Returns the matrix in the current paint engine state.
7697
7698 This variable should only be used when the state() returns a
7699 combination which includes the QPaintEngine::DirtyTransform flag.
7700
7701 \sa state(), QPaintEngine::updateState()
7702*/
7703
7704
7705QTransform QPaintEngineState::transform() const
7706{
7707 const QPainterState *st = static_cast<const QPainterState *>(this);
7708
7709 return st->matrix;
7710}
7711
7712
7713/*!
7714 Returns the clip operation in the current paint engine
7715 state.
7716
7717 This variable should only be used when the state() returns a
7718 combination which includes either the QPaintEngine::DirtyClipPath
7719 or the QPaintEngine::DirtyClipRegion flag.
7720
7721 \sa state(), QPaintEngine::updateState()
7722*/
7723
7724Qt::ClipOperation QPaintEngineState::clipOperation() const
7725{
7726 return static_cast<const QPainterState *>(this)->clipOperation;
7727}
7728
7729/*!
7730 \since 4.3
7731
7732 Returns whether the coordinate of the fill have been specified
7733 as bounded by the current rendering operation and have to be
7734 resolved (about the currently rendered primitive).
7735*/
7736bool QPaintEngineState::brushNeedsResolving() const
7737{
7738 const QBrush &brush = static_cast<const QPainterState *>(this)->brush;
7739 return needsResolving(brush);
7740}
7741
7742
7743/*!
7744 \since 4.3
7745
7746 Returns whether the coordinate of the stroke have been specified
7747 as bounded by the current rendering operation and have to be
7748 resolved (about the currently rendered primitive).
7749*/
7750bool QPaintEngineState::penNeedsResolving() const
7751{
7752 const QPen &pen = static_cast<const QPainterState *>(this)->pen;
7753 return needsResolving(brush: pen.brush());
7754}
7755
7756/*!
7757 Returns the clip region in the current paint engine state.
7758
7759 This variable should only be used when the state() returns a
7760 combination which includes the QPaintEngine::DirtyClipRegion flag.
7761
7762 \sa state(), QPaintEngine::updateState()
7763*/
7764
7765QRegion QPaintEngineState::clipRegion() const
7766{
7767 return static_cast<const QPainterState *>(this)->clipRegion;
7768}
7769
7770/*!
7771 Returns the clip path in the current paint engine state.
7772
7773 This variable should only be used when the state() returns a
7774 combination which includes the QPaintEngine::DirtyClipPath flag.
7775
7776 \sa state(), QPaintEngine::updateState()
7777*/
7778
7779QPainterPath QPaintEngineState::clipPath() const
7780{
7781 return static_cast<const QPainterState *>(this)->clipPath;
7782}
7783
7784/*!
7785 Returns whether clipping is enabled or not in the current paint
7786 engine state.
7787
7788 This variable should only be used when the state() returns a
7789 combination which includes the QPaintEngine::DirtyClipEnabled
7790 flag.
7791
7792 \sa state(), QPaintEngine::updateState()
7793*/
7794
7795bool QPaintEngineState::isClipEnabled() const
7796{
7797 return static_cast<const QPainterState *>(this)->clipEnabled;
7798}
7799
7800/*!
7801 Returns the render hints in the current paint engine state.
7802
7803 This variable should only be used when the state() returns a
7804 combination which includes the QPaintEngine::DirtyHints
7805 flag.
7806
7807 \sa state(), QPaintEngine::updateState()
7808*/
7809
7810QPainter::RenderHints QPaintEngineState::renderHints() const
7811{
7812 return static_cast<const QPainterState *>(this)->renderHints;
7813}
7814
7815/*!
7816 Returns the composition mode in the current paint engine state.
7817
7818 This variable should only be used when the state() returns a
7819 combination which includes the QPaintEngine::DirtyCompositionMode
7820 flag.
7821
7822 \sa state(), QPaintEngine::updateState()
7823*/
7824
7825QPainter::CompositionMode QPaintEngineState::compositionMode() const
7826{
7827 return static_cast<const QPainterState *>(this)->composition_mode;
7828}
7829
7830
7831/*!
7832 Returns a pointer to the painter currently updating the paint
7833 engine.
7834*/
7835
7836QPainter *QPaintEngineState::painter() const
7837{
7838 return static_cast<const QPainterState *>(this)->painter;
7839}
7840
7841
7842/*!
7843 \since 4.2
7844
7845 Returns the opacity in the current paint engine state.
7846*/
7847
7848qreal QPaintEngineState::opacity() const
7849{
7850 return static_cast<const QPainterState *>(this)->opacity;
7851}
7852
7853/*!
7854 \since 4.3
7855
7856 Sets the world transformation matrix.
7857 If \a combine is true, the specified \a transform is combined with
7858 the current matrix; otherwise it replaces the current matrix.
7859
7860 \sa transform(), setWorldTransform()
7861*/
7862
7863void QPainter::setTransform(const QTransform &transform, bool combine )
7864{
7865 setWorldTransform(matrix: transform, combine);
7866}
7867
7868/*!
7869 Alias for worldTransform().
7870 Returns the world transformation matrix.
7871
7872 \sa worldTransform()
7873*/
7874
7875const QTransform & QPainter::transform() const
7876{
7877 return worldTransform();
7878}
7879
7880
7881/*!
7882 Returns the matrix that transforms from logical coordinates to
7883 device coordinates of the platform dependent paint device.
7884
7885 This function is \e only needed when using platform painting
7886 commands on the platform dependent handle (Qt::HANDLE), and the
7887 platform does not do transformations nativly.
7888
7889 The QPaintEngine::PaintEngineFeature enum can be queried to
7890 determine whether the platform performs the transformations or
7891 not.
7892
7893 \sa worldTransform(), QPaintEngine::hasFeature(),
7894*/
7895
7896const QTransform & QPainter::deviceTransform() const
7897{
7898 Q_D(const QPainter);
7899 if (!d->engine) {
7900 qWarning(msg: "QPainter::deviceTransform: Painter not active");
7901 return d->fakeState()->transform;
7902 }
7903 return d->state->matrix;
7904}
7905
7906
7907/*!
7908 Resets any transformations that were made using translate(),
7909 scale(), shear(), rotate(), setWorldTransform(), setViewport()
7910 and setWindow().
7911
7912 \sa {Coordinate Transformations}
7913*/
7914
7915void QPainter::resetTransform()
7916{
7917 Q_D(QPainter);
7918#ifdef QT_DEBUG_DRAW
7919 if (qt_show_painter_debug_output)
7920 printf("QPainter::resetMatrix()\n");
7921#endif
7922 if (!d->engine) {
7923 qWarning(msg: "QPainter::resetMatrix: Painter not active");
7924 return;
7925 }
7926
7927 d->state->wx = d->state->wy = d->state->vx = d->state->vy = 0; // default view origins
7928 d->state->ww = d->state->vw = d->device->metric(metric: QPaintDevice::PdmWidth);
7929 d->state->wh = d->state->vh = d->device->metric(metric: QPaintDevice::PdmHeight);
7930 d->state->worldMatrix = QTransform();
7931 setWorldMatrixEnabled(false);
7932 setViewTransformEnabled(false);
7933 if (d->extended)
7934 d->extended->transformChanged();
7935 else
7936 d->state->dirtyFlags |= QPaintEngine::DirtyTransform;
7937}
7938
7939/*!
7940 Sets the world transformation matrix.
7941 If \a combine is true, the specified \a matrix is combined with the current matrix;
7942 otherwise it replaces the current matrix.
7943
7944 \sa transform(), setTransform()
7945*/
7946
7947void QPainter::setWorldTransform(const QTransform &matrix, bool combine )
7948{
7949 Q_D(QPainter);
7950
7951 if (!d->engine) {
7952 qWarning(msg: "QPainter::setWorldTransform: Painter not active");
7953 return;
7954 }
7955
7956 if (combine)
7957 d->state->worldMatrix = matrix * d->state->worldMatrix; // combines
7958 else
7959 d->state->worldMatrix = matrix; // set new matrix
7960
7961 d->state->WxF = true;
7962 d->updateMatrix();
7963}
7964
7965/*!
7966 Returns the world transformation matrix.
7967*/
7968
7969const QTransform & QPainter::worldTransform() const
7970{
7971 Q_D(const QPainter);
7972 if (!d->engine) {
7973 qWarning(msg: "QPainter::worldTransform: Painter not active");
7974 return d->fakeState()->transform;
7975 }
7976 return d->state->worldMatrix;
7977}
7978
7979/*!
7980 Returns the transformation matrix combining the current
7981 window/viewport and world transformation.
7982
7983 \sa setWorldTransform(), setWindow(), setViewport()
7984*/
7985
7986QTransform QPainter::combinedTransform() const
7987{
7988 Q_D(const QPainter);
7989 if (!d->engine) {
7990 qWarning(msg: "QPainter::combinedTransform: Painter not active");
7991 return QTransform();
7992 }
7993 return d->state->worldMatrix * d->viewTransform() * d->hidpiScaleTransform();
7994}
7995
7996/*!
7997 \since 4.7
7998
7999 This function is used to draw \a pixmap, or a sub-rectangle of \a pixmap,
8000 at multiple positions with different scale, rotation and opacity. \a
8001 fragments is an array of \a fragmentCount elements specifying the
8002 parameters used to draw each pixmap fragment. The \a hints
8003 parameter can be used to pass in drawing hints.
8004
8005 This function is potentially faster than multiple calls to drawPixmap(),
8006 since the backend can optimize state changes.
8007
8008 \sa QPainter::PixmapFragment, QPainter::PixmapFragmentHint
8009*/
8010
8011void QPainter::drawPixmapFragments(const PixmapFragment *fragments, int fragmentCount,
8012 const QPixmap &pixmap, PixmapFragmentHints hints)
8013{
8014 Q_D(QPainter);
8015
8016 if (!d->engine || pixmap.isNull())
8017 return;
8018
8019#ifndef QT_NO_DEBUG
8020 for (int i = 0; i < fragmentCount; ++i) {
8021 QRectF sourceRect(fragments[i].sourceLeft, fragments[i].sourceTop,
8022 fragments[i].width, fragments[i].height);
8023 if (!(QRectF(pixmap.rect()).contains(r: sourceRect)))
8024 qWarning(msg: "QPainter::drawPixmapFragments - the source rect is not contained by the pixmap's rectangle");
8025 }
8026#endif
8027
8028 if (d->engine->isExtended()) {
8029 d->extended->drawPixmapFragments(fragments, fragmentCount, pixmap, hints);
8030 } else {
8031 qreal oldOpacity = opacity();
8032 QTransform oldTransform = transform();
8033
8034 for (int i = 0; i < fragmentCount; ++i) {
8035 QTransform transform = oldTransform;
8036 qreal xOffset = 0;
8037 qreal yOffset = 0;
8038 if (fragments[i].rotation == 0) {
8039 xOffset = fragments[i].x;
8040 yOffset = fragments[i].y;
8041 } else {
8042 transform.translate(dx: fragments[i].x, dy: fragments[i].y);
8043 transform.rotate(a: fragments[i].rotation);
8044 }
8045 setOpacity(oldOpacity * fragments[i].opacity);
8046 setTransform(transform);
8047
8048 qreal w = fragments[i].scaleX * fragments[i].width;
8049 qreal h = fragments[i].scaleY * fragments[i].height;
8050 QRectF sourceRect(fragments[i].sourceLeft, fragments[i].sourceTop,
8051 fragments[i].width, fragments[i].height);
8052 drawPixmap(r: QRectF(-0.5 * w + xOffset, -0.5 * h + yOffset, w, h), pm: pixmap, sr: sourceRect);
8053 }
8054
8055 setOpacity(oldOpacity);
8056 setTransform(transform: oldTransform);
8057 }
8058}
8059
8060/*!
8061 \since 4.7
8062 \class QPainter::PixmapFragment
8063 \inmodule QtGui
8064
8065 \brief This class is used in conjunction with the
8066 QPainter::drawPixmapFragments() function to specify how a pixmap, or
8067 sub-rect of a pixmap, is drawn.
8068
8069 The \a sourceLeft, \a sourceTop, \a width and \a height variables are used
8070 as a source rectangle within the pixmap passed into the
8071 QPainter::drawPixmapFragments() function. The variables \a x, \a y, \a
8072 width and \a height are used to calculate the target rectangle that is
8073 drawn. \a x and \a y denotes the center of the target rectangle. The \a
8074 width and \a height in the target rectangle is scaled by the \a scaleX and
8075 \a scaleY values. The resulting target rectangle is then rotated \a
8076 rotation degrees around the \a x, \a y center point.
8077
8078 \sa QPainter::drawPixmapFragments()
8079*/
8080
8081/*!
8082 \since 4.7
8083
8084 This is a convenience function that returns a QPainter::PixmapFragment that is
8085 initialized with the \a pos, \a sourceRect, \a scaleX, \a scaleY, \a
8086 rotation, \a opacity parameters.
8087*/
8088
8089QPainter::PixmapFragment QPainter::PixmapFragment::create(const QPointF &pos, const QRectF &sourceRect,
8090 qreal scaleX, qreal scaleY, qreal rotation,
8091 qreal opacity)
8092{
8093 PixmapFragment fragment = {.x: pos.x(), .y: pos.y(), .sourceLeft: sourceRect.x(), .sourceTop: sourceRect.y(), .width: sourceRect.width(),
8094 .height: sourceRect.height(), .scaleX: scaleX, .scaleY: scaleY, .rotation: rotation, .opacity: opacity};
8095 return fragment;
8096}
8097
8098/*!
8099 \variable QPainter::PixmapFragment::x
8100 \brief the x coordinate of center point in the target rectangle.
8101*/
8102
8103/*!
8104 \variable QPainter::PixmapFragment::y
8105 \brief the y coordinate of the center point in the target rectangle.
8106*/
8107
8108/*!
8109 \variable QPainter::PixmapFragment::sourceLeft
8110 \brief the left coordinate of the source rectangle.
8111*/
8112
8113/*!
8114 \variable QPainter::PixmapFragment::sourceTop
8115 \brief the top coordinate of the source rectangle.
8116*/
8117
8118/*!
8119 \variable QPainter::PixmapFragment::width
8120
8121 \brief the width of the source rectangle and is used to calculate the width
8122 of the target rectangle.
8123*/
8124
8125/*!
8126 \variable QPainter::PixmapFragment::height
8127
8128 \brief the height of the source rectangle and is used to calculate the
8129 height of the target rectangle.
8130*/
8131
8132/*!
8133 \variable QPainter::PixmapFragment::scaleX
8134 \brief the horizontal scale of the target rectangle.
8135*/
8136
8137/*!
8138 \variable QPainter::PixmapFragment::scaleY
8139 \brief the vertical scale of the target rectangle.
8140*/
8141
8142/*!
8143 \variable QPainter::PixmapFragment::rotation
8144
8145 \brief the rotation of the target rectangle in degrees. The target
8146 rectangle is rotated after it has been scaled.
8147*/
8148
8149/*!
8150 \variable QPainter::PixmapFragment::opacity
8151
8152 \brief the opacity of the target rectangle, where 0.0 is fully transparent
8153 and 1.0 is fully opaque.
8154*/
8155
8156/*!
8157 \since 4.7
8158
8159 \enum QPainter::PixmapFragmentHint
8160
8161 \value OpaqueHint Indicates that the pixmap fragments to be drawn are
8162 opaque. Opaque fragments are potentially faster to draw.
8163
8164 \sa QPainter::drawPixmapFragments(), QPainter::PixmapFragment
8165*/
8166
8167void qt_draw_helper(QPainterPrivate *p, const QPainterPath &path, QPainterPrivate::DrawOperation operation)
8168{
8169 p->draw_helper(originalPath: path, op: operation);
8170}
8171
8172QT_END_NAMESPACE
8173
8174#include "moc_qpainter.cpp"
8175

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