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

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