1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the QtGui module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 3 requirements |
23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
24 | ** |
25 | ** GNU General Public License Usage |
26 | ** Alternatively, this file may be used under the terms of the GNU |
27 | ** General Public License version 2.0 or (at your option) the GNU General |
28 | ** Public license version 3 or any later version approved by the KDE Free |
29 | ** Qt Foundation. The licenses are as published by the Free Software |
30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
31 | ** included in the packaging of this file. Please review the following |
32 | ** information to ensure the GNU General Public License requirements will |
33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
34 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
35 | ** |
36 | ** $QT_END_LICENSE$ |
37 | ** |
38 | ****************************************************************************/ |
39 | |
40 | // QtCore |
41 | #include <qdebug.h> |
42 | #include <qmath.h> |
43 | #include <qmutex.h> |
44 | |
45 | // QtGui |
46 | #include "qbitmap.h" |
47 | #include "qimage.h" |
48 | #include "qpaintdevice.h" |
49 | #include "qpaintengine.h" |
50 | #include "qpainter.h" |
51 | #include "qpainter_p.h" |
52 | #include "qpainterpath.h" |
53 | #include "qpicture.h" |
54 | #include "qpixmapcache.h" |
55 | #include "qpolygon.h" |
56 | #include "qtextlayout.h" |
57 | #include "qthread.h" |
58 | #include "qvarlengtharray.h" |
59 | #include "qstatictext.h" |
60 | #include "qglyphrun.h" |
61 | |
62 | #include <qpa/qplatformtheme.h> |
63 | #include <qpa/qplatformintegration.h> |
64 | |
65 | #include <private/qfontengine_p.h> |
66 | #include <private/qpaintengine_p.h> |
67 | #include <private/qemulationpaintengine_p.h> |
68 | #include <private/qpainterpath_p.h> |
69 | #include <private/qtextengine_p.h> |
70 | #include <private/qpaintengine_raster_p.h> |
71 | #include <private/qmath_p.h> |
72 | #include <private/qstatictext_p.h> |
73 | #include <private/qglyphrun_p.h> |
74 | #include <private/qhexstring_p.h> |
75 | #include <private/qguiapplication_p.h> |
76 | #include <private/qrawfont_p.h> |
77 | |
78 | QT_BEGIN_NAMESPACE |
79 | |
80 | #define QGradient_StretchToDevice 0x10000000 |
81 | #define QPaintEngine_OpaqueBackground 0x40000000 |
82 | |
83 | // #define QT_DEBUG_DRAW |
84 | #ifdef QT_DEBUG_DRAW |
85 | bool qt_show_painter_debug_output = true; |
86 | #endif |
87 | |
88 | extern QPixmap qt_pixmapForBrush(int style, bool invert); |
89 | |
90 | void qt_format_text(const QFont &font, |
91 | const QRectF &_r, int tf, const QTextOption *option, const QString& str, QRectF *brect, |
92 | int tabstops, int* tabarray, int tabarraylen, |
93 | QPainter *painter); |
94 | static void drawTextItemDecoration(QPainter *painter, const QPointF &pos, const QFontEngine *fe, QTextEngine *textEngine, |
95 | QTextCharFormat::UnderlineStyle underlineStyle, |
96 | QTextItem::RenderFlags flags, qreal width, |
97 | const QTextCharFormat &charFormat); |
98 | // Helper function to calculate left most position, width and flags for decoration drawing |
99 | Q_GUI_EXPORT void qt_draw_decoration_for_glyphs(QPainter *painter, const glyph_t *glyphArray, |
100 | const QFixedPoint *positions, int glyphCount, |
101 | QFontEngine *fontEngine, const QFont &font, |
102 | const QTextCharFormat &charFormat); |
103 | |
104 | static inline QGradient::CoordinateMode coordinateMode(const QBrush &brush) |
105 | { |
106 | switch (brush.style()) { |
107 | case Qt::LinearGradientPattern: |
108 | case Qt::RadialGradientPattern: |
109 | case Qt::ConicalGradientPattern: |
110 | return brush.gradient()->coordinateMode(); |
111 | default: |
112 | ; |
113 | } |
114 | return QGradient::LogicalMode; |
115 | } |
116 | |
117 | extern bool qHasPixmapTexture(const QBrush &); |
118 | |
119 | static inline bool is_brush_transparent(const QBrush &brush) { |
120 | Qt::BrushStyle s = brush.style(); |
121 | if (s != Qt::TexturePattern) |
122 | return s >= Qt::Dense1Pattern && s <= Qt::DiagCrossPattern; |
123 | if (qHasPixmapTexture(brush)) |
124 | return brush.texture().isQBitmap() || brush.texture().hasAlphaChannel(); |
125 | else { |
126 | const QImage texture = brush.textureImage(); |
127 | return texture.hasAlphaChannel() || (texture.depth() == 1 && texture.colorCount() == 0); |
128 | } |
129 | } |
130 | |
131 | static inline bool is_pen_transparent(const QPen &pen) { |
132 | return pen.style() > Qt::SolidLine || is_brush_transparent(pen.brush()); |
133 | } |
134 | |
135 | /* Discards the emulation flags that are not relevant for line drawing |
136 | and returns the result |
137 | */ |
138 | static inline uint line_emulation(uint emulation) |
139 | { |
140 | return emulation & (QPaintEngine::PrimitiveTransform |
141 | | QPaintEngine::AlphaBlend |
142 | | QPaintEngine::Antialiasing |
143 | | QPaintEngine::BrushStroke |
144 | | QPaintEngine::ConstantOpacity |
145 | | QGradient_StretchToDevice |
146 | | QPaintEngine::ObjectBoundingModeGradients |
147 | | QPaintEngine_OpaqueBackground); |
148 | } |
149 | |
150 | #ifndef QT_NO_DEBUG |
151 | static bool qt_painter_thread_test(int devType, int engineType, const char *what) |
152 | { |
153 | const QPlatformIntegration *platformIntegration = QGuiApplicationPrivate::platformIntegration(); |
154 | switch (devType) { |
155 | case QInternal::Image: |
156 | case QInternal::Printer: |
157 | case QInternal::Picture: |
158 | // can be drawn onto these devices safely from any thread |
159 | break; |
160 | default: |
161 | if (QThread::currentThread() != qApp->thread() |
162 | // pixmaps cannot be targets unless threaded pixmaps are supported |
163 | && (devType != QInternal::Pixmap || !platformIntegration->hasCapability(QPlatformIntegration::ThreadedPixmaps)) |
164 | // framebuffer objects and such cannot be targets unless threaded GL is supported |
165 | && (devType != QInternal::OpenGL || !platformIntegration->hasCapability(QPlatformIntegration::ThreadedOpenGL)) |
166 | // widgets cannot be targets except for QGLWidget |
167 | && (devType != QInternal::Widget || !platformIntegration->hasCapability(QPlatformIntegration::ThreadedOpenGL) |
168 | || (engineType != QPaintEngine::OpenGL && engineType != QPaintEngine::OpenGL2))) { |
169 | qWarning("QPainter: It is not safe to use %s outside the GUI thread" , what); |
170 | return false; |
171 | } |
172 | break; |
173 | } |
174 | return true; |
175 | } |
176 | #endif |
177 | |
178 | void QPainterPrivate::checkEmulation() |
179 | { |
180 | Q_ASSERT(extended); |
181 | bool doEmulation = false; |
182 | if (state->bgMode == Qt::OpaqueMode) |
183 | doEmulation = true; |
184 | |
185 | const QGradient *bg = state->brush.gradient(); |
186 | if (bg && bg->coordinateMode() > QGradient::LogicalMode) |
187 | doEmulation = true; |
188 | |
189 | const QGradient *pg = qpen_brush(state->pen).gradient(); |
190 | if (pg && pg->coordinateMode() > QGradient::LogicalMode) |
191 | doEmulation = true; |
192 | |
193 | if (state->brush.style() == Qt::TexturePattern) { |
194 | if (qHasPixmapTexture(state->brush)) |
195 | doEmulation |= !qFuzzyCompare(state->brush.texture().devicePixelRatioF(), 1.0); |
196 | else |
197 | doEmulation |= !qFuzzyCompare(state->brush.textureImage().devicePixelRatioF(), 1.0); |
198 | } |
199 | |
200 | if (doEmulation && extended->flags() & QPaintEngineEx::DoNotEmulate) |
201 | return; |
202 | |
203 | if (doEmulation) { |
204 | if (extended != emulationEngine) { |
205 | if (!emulationEngine) |
206 | emulationEngine = new QEmulationPaintEngine(extended); |
207 | extended = emulationEngine; |
208 | extended->setState(state); |
209 | } |
210 | } else if (emulationEngine == extended) { |
211 | extended = emulationEngine->real_engine; |
212 | } |
213 | } |
214 | |
215 | |
216 | QPainterPrivate::~QPainterPrivate() |
217 | { |
218 | delete emulationEngine; |
219 | qDeleteAll(states); |
220 | delete dummyState; |
221 | } |
222 | |
223 | |
224 | QTransform QPainterPrivate::viewTransform() const |
225 | { |
226 | if (state->VxF) { |
227 | qreal scaleW = qreal(state->vw)/qreal(state->ww); |
228 | qreal scaleH = qreal(state->vh)/qreal(state->wh); |
229 | return QTransform(scaleW, 0, 0, scaleH, |
230 | state->vx - state->wx*scaleW, state->vy - state->wy*scaleH); |
231 | } |
232 | return QTransform(); |
233 | } |
234 | |
235 | qreal QPainterPrivate::effectiveDevicePixelRatio() const |
236 | { |
237 | // Special cases for devices that does not support PdmDevicePixelRatio go here: |
238 | if (device->devType() == QInternal::Printer) |
239 | return qreal(1); |
240 | |
241 | return qMax(qreal(1), device->devicePixelRatioF()); |
242 | } |
243 | |
244 | QTransform QPainterPrivate::hidpiScaleTransform() const |
245 | { |
246 | const qreal devicePixelRatio = effectiveDevicePixelRatio(); |
247 | return QTransform::fromScale(devicePixelRatio, devicePixelRatio); |
248 | } |
249 | |
250 | /* |
251 | \internal |
252 | Returns \c true if using a shared painter; otherwise false. |
253 | */ |
254 | bool QPainterPrivate::attachPainterPrivate(QPainter *q, QPaintDevice *pdev) |
255 | { |
256 | Q_ASSERT(q); |
257 | Q_ASSERT(pdev); |
258 | |
259 | QPainter *sp = pdev->sharedPainter(); |
260 | if (!sp) |
261 | return false; |
262 | |
263 | // Save the current state of the shared painter and assign |
264 | // the current d_ptr to the shared painter's d_ptr. |
265 | sp->save(); |
266 | if (!sp->d_ptr->d_ptrs) { |
267 | // Allocate space for 4 d-pointers (enough for up to 4 sub-sequent |
268 | // redirections within the same paintEvent(), which should be enough |
269 | // in 99% of all cases). E.g: A renders B which renders C which renders D. |
270 | sp->d_ptr->d_ptrs_size = 4; |
271 | sp->d_ptr->d_ptrs = (QPainterPrivate **)malloc(4 * sizeof(QPainterPrivate *)); |
272 | Q_CHECK_PTR(sp->d_ptr->d_ptrs); |
273 | } else if (sp->d_ptr->refcount - 1 == sp->d_ptr->d_ptrs_size) { |
274 | // However, to support corner cases we grow the array dynamically if needed. |
275 | sp->d_ptr->d_ptrs_size <<= 1; |
276 | const int newSize = sp->d_ptr->d_ptrs_size * sizeof(QPainterPrivate *); |
277 | sp->d_ptr->d_ptrs = q_check_ptr((QPainterPrivate **)realloc(sp->d_ptr->d_ptrs, newSize)); |
278 | } |
279 | sp->d_ptr->d_ptrs[++sp->d_ptr->refcount - 2] = q->d_ptr.data(); |
280 | q->d_ptr.take(); |
281 | q->d_ptr.reset(sp->d_ptr.data()); |
282 | |
283 | Q_ASSERT(q->d_ptr->state); |
284 | |
285 | // Now initialize the painter with correct widget properties. |
286 | q->d_ptr->initFrom(pdev); |
287 | QPoint offset; |
288 | pdev->redirected(&offset); |
289 | offset += q->d_ptr->engine->coordinateOffset(); |
290 | |
291 | // Update system rect. |
292 | q->d_ptr->state->ww = q->d_ptr->state->vw = pdev->width(); |
293 | q->d_ptr->state->wh = q->d_ptr->state->vh = pdev->height(); |
294 | |
295 | // Update matrix. |
296 | if (q->d_ptr->state->WxF) { |
297 | q->d_ptr->state->redirectionMatrix = q->d_ptr->state->matrix; |
298 | q->d_ptr->state->redirectionMatrix *= q->d_ptr->hidpiScaleTransform().inverted(); |
299 | q->d_ptr->state->redirectionMatrix.translate(-offset.x(), -offset.y()); |
300 | q->d_ptr->state->worldMatrix = QTransform(); |
301 | q->d_ptr->state->WxF = false; |
302 | } else { |
303 | q->d_ptr->state->redirectionMatrix = QTransform::fromTranslate(-offset.x(), -offset.y()); |
304 | } |
305 | q->d_ptr->updateMatrix(); |
306 | |
307 | QPaintEnginePrivate *enginePrivate = q->d_ptr->engine->d_func(); |
308 | if (enginePrivate->currentClipDevice == pdev) { |
309 | enginePrivate->systemStateChanged(); |
310 | return true; |
311 | } |
312 | |
313 | // Update system transform and clip. |
314 | enginePrivate->currentClipDevice = pdev; |
315 | enginePrivate->setSystemTransform(q->d_ptr->state->matrix); |
316 | return true; |
317 | } |
318 | |
319 | void QPainterPrivate::detachPainterPrivate(QPainter *q) |
320 | { |
321 | Q_ASSERT(refcount > 1); |
322 | Q_ASSERT(q); |
323 | |
324 | QPainterPrivate *original = d_ptrs[--refcount - 1]; |
325 | if (inDestructor) { |
326 | inDestructor = false; |
327 | if (original) |
328 | original->inDestructor = true; |
329 | } else if (!original) { |
330 | original = new QPainterPrivate(q); |
331 | } |
332 | |
333 | d_ptrs[refcount - 1] = 0; |
334 | q->restore(); |
335 | q->d_ptr.take(); |
336 | q->d_ptr.reset(original); |
337 | |
338 | if (emulationEngine) { |
339 | extended = emulationEngine->real_engine; |
340 | delete emulationEngine; |
341 | emulationEngine = 0; |
342 | } |
343 | } |
344 | |
345 | |
346 | void QPainterPrivate::draw_helper(const QPainterPath &originalPath, DrawOperation op) |
347 | { |
348 | #ifdef QT_DEBUG_DRAW |
349 | if (qt_show_painter_debug_output) { |
350 | printf("QPainter::drawHelper\n" ); |
351 | } |
352 | #endif |
353 | |
354 | if (originalPath.isEmpty()) |
355 | return; |
356 | |
357 | QPaintEngine::PaintEngineFeatures gradientStretch = |
358 | QPaintEngine::PaintEngineFeatures(QGradient_StretchToDevice |
359 | | QPaintEngine::ObjectBoundingModeGradients); |
360 | |
361 | const bool mustEmulateObjectBoundingModeGradients = extended |
362 | || ((state->emulationSpecifier & QPaintEngine::ObjectBoundingModeGradients) |
363 | && !engine->hasFeature(QPaintEngine::PatternTransform)); |
364 | |
365 | if (!(state->emulationSpecifier & ~gradientStretch) |
366 | && !mustEmulateObjectBoundingModeGradients) { |
367 | drawStretchedGradient(originalPath, op); |
368 | return; |
369 | } else if (state->emulationSpecifier & QPaintEngine_OpaqueBackground) { |
370 | drawOpaqueBackground(originalPath, op); |
371 | return; |
372 | } |
373 | |
374 | Q_Q(QPainter); |
375 | |
376 | qreal strokeOffsetX = 0, strokeOffsetY = 0; |
377 | |
378 | QPainterPath path = originalPath * state->matrix; |
379 | QRectF pathBounds = path.boundingRect(); |
380 | QRectF strokeBounds; |
381 | bool doStroke = (op & StrokeDraw) && (state->pen.style() != Qt::NoPen); |
382 | if (doStroke) { |
383 | qreal penWidth = state->pen.widthF(); |
384 | if (penWidth == 0) { |
385 | strokeOffsetX = 1; |
386 | strokeOffsetY = 1; |
387 | } else { |
388 | // In case of complex xform |
389 | if (state->matrix.type() > QTransform::TxScale) { |
390 | QPainterPathStroker stroker; |
391 | stroker.setWidth(penWidth); |
392 | stroker.setJoinStyle(state->pen.joinStyle()); |
393 | stroker.setCapStyle(state->pen.capStyle()); |
394 | QPainterPath stroke = stroker.createStroke(originalPath); |
395 | strokeBounds = (stroke * state->matrix).boundingRect(); |
396 | } else { |
397 | strokeOffsetX = qAbs(penWidth * state->matrix.m11() / 2.0); |
398 | strokeOffsetY = qAbs(penWidth * state->matrix.m22() / 2.0); |
399 | } |
400 | } |
401 | } |
402 | |
403 | QRect absPathRect; |
404 | if (!strokeBounds.isEmpty()) { |
405 | absPathRect = strokeBounds.intersected(QRectF(0, 0, device->width(), device->height())).toAlignedRect(); |
406 | } else { |
407 | absPathRect = pathBounds.adjusted(-strokeOffsetX, -strokeOffsetY, strokeOffsetX, strokeOffsetY) |
408 | .intersected(QRectF(0, 0, device->width(), device->height())).toAlignedRect(); |
409 | } |
410 | |
411 | if (q->hasClipping()) { |
412 | bool hasPerspectiveTransform = false; |
413 | for (const QPainterClipInfo &info : qAsConst(state->clipInfo)) { |
414 | if (info.matrix.type() == QTransform::TxProject) { |
415 | hasPerspectiveTransform = true; |
416 | break; |
417 | } |
418 | } |
419 | // avoid mapping QRegions with perspective transforms |
420 | if (!hasPerspectiveTransform) { |
421 | // The trick with txinv and invMatrix is done in order to |
422 | // avoid transforming the clip to logical coordinates, and |
423 | // then back to device coordinates. This is a problem with |
424 | // QRegion/QRect based clips, since they use integer |
425 | // coordinates and converting to/from logical coordinates will |
426 | // lose precision. |
427 | bool old_txinv = txinv; |
428 | QTransform old_invMatrix = invMatrix; |
429 | txinv = true; |
430 | invMatrix = QTransform(); |
431 | QPainterPath clipPath = q->clipPath(); |
432 | QRectF r = clipPath.boundingRect().intersected(absPathRect); |
433 | absPathRect = r.toAlignedRect(); |
434 | txinv = old_txinv; |
435 | invMatrix = old_invMatrix; |
436 | } |
437 | } |
438 | |
439 | // qDebug("\nQPainterPrivate::draw_helper(), x=%d, y=%d, w=%d, h=%d", |
440 | // devMinX, devMinY, device->width(), device->height()); |
441 | // qDebug() << " - matrix" << state->matrix; |
442 | // qDebug() << " - originalPath.bounds" << originalPath.boundingRect(); |
443 | // qDebug() << " - path.bounds" << path.boundingRect(); |
444 | |
445 | if (absPathRect.width() <= 0 || absPathRect.height() <= 0) |
446 | return; |
447 | |
448 | QImage image(absPathRect.width(), absPathRect.height(), QImage::Format_ARGB32_Premultiplied); |
449 | image.fill(0); |
450 | |
451 | QPainter p(&image); |
452 | |
453 | p.d_ptr->helper_device = helper_device; |
454 | |
455 | p.setOpacity(state->opacity); |
456 | p.translate(-absPathRect.x(), -absPathRect.y()); |
457 | p.setTransform(state->matrix, true); |
458 | p.setPen(doStroke ? state->pen : QPen(Qt::NoPen)); |
459 | p.setBrush((op & FillDraw) ? state->brush : QBrush(Qt::NoBrush)); |
460 | p.setBackground(state->bgBrush); |
461 | p.setBackgroundMode(state->bgMode); |
462 | p.setBrushOrigin(state->brushOrigin); |
463 | |
464 | p.setRenderHint(QPainter::Antialiasing, state->renderHints & QPainter::Antialiasing); |
465 | p.setRenderHint(QPainter::SmoothPixmapTransform, |
466 | state->renderHints & QPainter::SmoothPixmapTransform); |
467 | |
468 | p.drawPath(originalPath); |
469 | |
470 | #ifndef QT_NO_DEBUG |
471 | static bool do_fallback_overlay = !qEnvironmentVariableIsEmpty("QT_PAINT_FALLBACK_OVERLAY" ); |
472 | if (do_fallback_overlay) { |
473 | QImage block(8, 8, QImage::Format_ARGB32_Premultiplied); |
474 | QPainter pt(&block); |
475 | pt.fillRect(0, 0, 8, 8, QColor(196, 0, 196)); |
476 | pt.drawLine(0, 0, 8, 8); |
477 | pt.end(); |
478 | p.resetTransform(); |
479 | p.setCompositionMode(QPainter::CompositionMode_SourceAtop); |
480 | p.setOpacity(0.5); |
481 | p.fillRect(0, 0, image.width(), image.height(), QBrush(block)); |
482 | } |
483 | #endif |
484 | |
485 | p.end(); |
486 | |
487 | q->save(); |
488 | state->matrix = QTransform(); |
489 | if (extended) { |
490 | extended->transformChanged(); |
491 | } else { |
492 | state->dirtyFlags |= QPaintEngine::DirtyTransform; |
493 | updateState(state); |
494 | } |
495 | engine->drawImage(absPathRect, |
496 | image, |
497 | QRectF(0, 0, absPathRect.width(), absPathRect.height()), |
498 | Qt::OrderedDither | Qt::OrderedAlphaDither); |
499 | q->restore(); |
500 | } |
501 | |
502 | void QPainterPrivate::drawOpaqueBackground(const QPainterPath &path, DrawOperation op) |
503 | { |
504 | Q_Q(QPainter); |
505 | |
506 | q->setBackgroundMode(Qt::TransparentMode); |
507 | |
508 | if (op & FillDraw && state->brush.style() != Qt::NoBrush) { |
509 | q->fillPath(path, state->bgBrush.color()); |
510 | q->fillPath(path, state->brush); |
511 | } |
512 | |
513 | if (op & StrokeDraw && state->pen.style() != Qt::NoPen) { |
514 | q->strokePath(path, QPen(state->bgBrush.color(), state->pen.width())); |
515 | q->strokePath(path, state->pen); |
516 | } |
517 | |
518 | q->setBackgroundMode(Qt::OpaqueMode); |
519 | } |
520 | |
521 | static inline QBrush stretchGradientToUserSpace(const QBrush &brush, const QRectF &boundingRect) |
522 | { |
523 | Q_ASSERT(brush.style() >= Qt::LinearGradientPattern |
524 | && brush.style() <= Qt::ConicalGradientPattern); |
525 | |
526 | QTransform gradientToUser(boundingRect.width(), 0, 0, boundingRect.height(), |
527 | boundingRect.x(), boundingRect.y()); |
528 | |
529 | QGradient g = *brush.gradient(); |
530 | g.setCoordinateMode(QGradient::LogicalMode); |
531 | |
532 | QBrush b(g); |
533 | if (brush.gradient()->coordinateMode() == QGradient::ObjectMode) |
534 | b.setTransform(b.transform() * gradientToUser); |
535 | else |
536 | b.setTransform(gradientToUser * b.transform()); |
537 | return b; |
538 | } |
539 | |
540 | void QPainterPrivate::drawStretchedGradient(const QPainterPath &path, DrawOperation op) |
541 | { |
542 | Q_Q(QPainter); |
543 | |
544 | const qreal sw = helper_device->width(); |
545 | const qreal sh = helper_device->height(); |
546 | |
547 | bool changedPen = false; |
548 | bool changedBrush = false; |
549 | bool needsFill = false; |
550 | |
551 | const QPen pen = state->pen; |
552 | const QBrush brush = state->brush; |
553 | |
554 | const QGradient::CoordinateMode penMode = coordinateMode(pen.brush()); |
555 | const QGradient::CoordinateMode brushMode = coordinateMode(brush); |
556 | |
557 | QRectF boundingRect; |
558 | |
559 | // Draw the xformed fill if the brush is a stretch gradient. |
560 | if ((op & FillDraw) && brush.style() != Qt::NoBrush) { |
561 | if (brushMode == QGradient::StretchToDeviceMode) { |
562 | q->setPen(Qt::NoPen); |
563 | changedPen = pen.style() != Qt::NoPen; |
564 | q->scale(sw, sh); |
565 | updateState(state); |
566 | |
567 | const qreal isw = 1.0 / sw; |
568 | const qreal ish = 1.0 / sh; |
569 | QTransform inv(isw, 0, 0, ish, 0, 0); |
570 | engine->drawPath(path * inv); |
571 | q->scale(isw, ish); |
572 | } else { |
573 | needsFill = true; |
574 | |
575 | if (brushMode == QGradient::ObjectBoundingMode || brushMode == QGradient::ObjectMode) { |
576 | Q_ASSERT(engine->hasFeature(QPaintEngine::PatternTransform)); |
577 | boundingRect = path.boundingRect(); |
578 | q->setBrush(stretchGradientToUserSpace(brush, boundingRect)); |
579 | changedBrush = true; |
580 | } |
581 | } |
582 | } |
583 | |
584 | if ((op & StrokeDraw) && pen.style() != Qt::NoPen) { |
585 | // Draw the xformed outline if the pen is a stretch gradient. |
586 | if (penMode == QGradient::StretchToDeviceMode) { |
587 | q->setPen(Qt::NoPen); |
588 | changedPen = true; |
589 | |
590 | if (needsFill) { |
591 | updateState(state); |
592 | engine->drawPath(path); |
593 | } |
594 | |
595 | q->scale(sw, sh); |
596 | q->setBrush(pen.brush()); |
597 | changedBrush = true; |
598 | updateState(state); |
599 | |
600 | QPainterPathStroker stroker; |
601 | stroker.setDashPattern(pen.style()); |
602 | stroker.setWidth(pen.widthF()); |
603 | stroker.setJoinStyle(pen.joinStyle()); |
604 | stroker.setCapStyle(pen.capStyle()); |
605 | stroker.setMiterLimit(pen.miterLimit()); |
606 | QPainterPath stroke = stroker.createStroke(path); |
607 | |
608 | const qreal isw = 1.0 / sw; |
609 | const qreal ish = 1.0 / sh; |
610 | QTransform inv(isw, 0, 0, ish, 0, 0); |
611 | engine->drawPath(stroke * inv); |
612 | q->scale(isw, ish); |
613 | } else { |
614 | if (!needsFill && brush.style() != Qt::NoBrush) { |
615 | q->setBrush(Qt::NoBrush); |
616 | changedBrush = true; |
617 | } |
618 | |
619 | if (penMode == QGradient::ObjectBoundingMode || penMode == QGradient::ObjectMode) { |
620 | Q_ASSERT(engine->hasFeature(QPaintEngine::PatternTransform)); |
621 | |
622 | // avoid computing the bounding rect twice |
623 | if (!needsFill || (brushMode != QGradient::ObjectBoundingMode && brushMode != QGradient::ObjectMode)) |
624 | boundingRect = path.boundingRect(); |
625 | |
626 | QPen p = pen; |
627 | p.setBrush(stretchGradientToUserSpace(pen.brush(), boundingRect)); |
628 | q->setPen(p); |
629 | changedPen = true; |
630 | } else if (changedPen) { |
631 | q->setPen(pen); |
632 | changedPen = false; |
633 | } |
634 | |
635 | updateState(state); |
636 | engine->drawPath(path); |
637 | } |
638 | } else if (needsFill) { |
639 | if (pen.style() != Qt::NoPen) { |
640 | q->setPen(Qt::NoPen); |
641 | changedPen = true; |
642 | } |
643 | |
644 | updateState(state); |
645 | engine->drawPath(path); |
646 | } |
647 | |
648 | if (changedPen) |
649 | q->setPen(pen); |
650 | if (changedBrush) |
651 | q->setBrush(brush); |
652 | } |
653 | |
654 | |
655 | void QPainterPrivate::updateMatrix() |
656 | { |
657 | state->matrix = state->WxF ? state->worldMatrix : QTransform(); |
658 | if (state->VxF) |
659 | state->matrix *= viewTransform(); |
660 | |
661 | txinv = false; // no inverted matrix |
662 | state->matrix *= state->redirectionMatrix; |
663 | if (extended) |
664 | extended->transformChanged(); |
665 | else |
666 | state->dirtyFlags |= QPaintEngine::DirtyTransform; |
667 | |
668 | state->matrix *= hidpiScaleTransform(); |
669 | |
670 | // printf("VxF=%d, WxF=%d\n", state->VxF, state->WxF); |
671 | // qDebug() << " --- using matrix" << state->matrix << redirection_offset; |
672 | } |
673 | |
674 | /*! \internal */ |
675 | void QPainterPrivate::updateInvMatrix() |
676 | { |
677 | Q_ASSERT(txinv == false); |
678 | txinv = true; // creating inverted matrix |
679 | invMatrix = state->matrix.inverted(); |
680 | } |
681 | |
682 | extern bool qt_isExtendedRadialGradient(const QBrush &brush); |
683 | |
684 | void QPainterPrivate::updateEmulationSpecifier(QPainterState *s) |
685 | { |
686 | bool alpha = false; |
687 | bool linearGradient = false; |
688 | bool radialGradient = false; |
689 | bool extendedRadialGradient = false; |
690 | bool conicalGradient = false; |
691 | bool patternBrush = false; |
692 | bool xform = false; |
693 | bool complexXform = false; |
694 | |
695 | bool skip = true; |
696 | |
697 | // Pen and brush properties (we have to check both if one changes because the |
698 | // one that's unchanged can still be in a state which requires emulation) |
699 | if (s->state() & (QPaintEngine::DirtyPen | QPaintEngine::DirtyBrush | QPaintEngine::DirtyHints)) { |
700 | // Check Brush stroke emulation |
701 | if (!s->pen.isSolid() && !engine->hasFeature(QPaintEngine::BrushStroke)) |
702 | s->emulationSpecifier |= QPaintEngine::BrushStroke; |
703 | else |
704 | s->emulationSpecifier &= ~QPaintEngine::BrushStroke; |
705 | |
706 | skip = false; |
707 | |
708 | QBrush penBrush = (qpen_style(s->pen) == Qt::NoPen) ? QBrush(Qt::NoBrush) : qpen_brush(s->pen); |
709 | Qt::BrushStyle brushStyle = qbrush_style(s->brush); |
710 | Qt::BrushStyle penBrushStyle = qbrush_style(penBrush); |
711 | alpha = (penBrushStyle != Qt::NoBrush |
712 | && (penBrushStyle < Qt::LinearGradientPattern && penBrush.color().alpha() != 255) |
713 | && !penBrush.isOpaque()) |
714 | || (brushStyle != Qt::NoBrush |
715 | && (brushStyle < Qt::LinearGradientPattern && s->brush.color().alpha() != 255) |
716 | && !s->brush.isOpaque()); |
717 | linearGradient = ((penBrushStyle == Qt::LinearGradientPattern) || |
718 | (brushStyle == Qt::LinearGradientPattern)); |
719 | radialGradient = ((penBrushStyle == Qt::RadialGradientPattern) || |
720 | (brushStyle == Qt::RadialGradientPattern)); |
721 | extendedRadialGradient = radialGradient && (qt_isExtendedRadialGradient(penBrush) || qt_isExtendedRadialGradient(s->brush)); |
722 | conicalGradient = ((penBrushStyle == Qt::ConicalGradientPattern) || |
723 | (brushStyle == Qt::ConicalGradientPattern)); |
724 | patternBrush = (((penBrushStyle > Qt::SolidPattern |
725 | && penBrushStyle < Qt::LinearGradientPattern) |
726 | || penBrushStyle == Qt::TexturePattern) || |
727 | ((brushStyle > Qt::SolidPattern |
728 | && brushStyle < Qt::LinearGradientPattern) |
729 | || brushStyle == Qt::TexturePattern)); |
730 | |
731 | bool penTextureAlpha = false; |
732 | if (penBrush.style() == Qt::TexturePattern) |
733 | penTextureAlpha = qHasPixmapTexture(penBrush) |
734 | ? (penBrush.texture().depth() > 1) && penBrush.texture().hasAlpha() |
735 | : penBrush.textureImage().hasAlphaChannel(); |
736 | bool brushTextureAlpha = false; |
737 | if (s->brush.style() == Qt::TexturePattern) { |
738 | brushTextureAlpha = qHasPixmapTexture(s->brush) |
739 | ? (s->brush.texture().depth() > 1) && s->brush.texture().hasAlpha() |
740 | : s->brush.textureImage().hasAlphaChannel(); |
741 | } |
742 | if (((penBrush.style() == Qt::TexturePattern && penTextureAlpha) |
743 | || (s->brush.style() == Qt::TexturePattern && brushTextureAlpha)) |
744 | && !engine->hasFeature(QPaintEngine::MaskedBrush)) |
745 | s->emulationSpecifier |= QPaintEngine::MaskedBrush; |
746 | else |
747 | s->emulationSpecifier &= ~QPaintEngine::MaskedBrush; |
748 | } |
749 | |
750 | if (s->state() & (QPaintEngine::DirtyHints |
751 | | QPaintEngine::DirtyOpacity |
752 | | QPaintEngine::DirtyBackgroundMode)) { |
753 | skip = false; |
754 | } |
755 | |
756 | if (skip) |
757 | return; |
758 | |
759 | #if 0 |
760 | qDebug("QPainterPrivate::updateEmulationSpecifier, state=%p\n" |
761 | " - alpha: %d\n" |
762 | " - linearGradient: %d\n" |
763 | " - radialGradient: %d\n" |
764 | " - conicalGradient: %d\n" |
765 | " - patternBrush: %d\n" |
766 | " - hints: %x\n" |
767 | " - xform: %d\n" , |
768 | s, |
769 | alpha, |
770 | linearGradient, |
771 | radialGradient, |
772 | conicalGradient, |
773 | patternBrush, |
774 | uint(s->renderHints), |
775 | xform); |
776 | #endif |
777 | |
778 | // XForm properties |
779 | if (s->state() & QPaintEngine::DirtyTransform) { |
780 | xform = !s->matrix.isIdentity(); |
781 | complexXform = !s->matrix.isAffine(); |
782 | } else if (s->matrix.type() >= QTransform::TxTranslate) { |
783 | xform = true; |
784 | complexXform = !s->matrix.isAffine(); |
785 | } |
786 | |
787 | const bool brushXform = (s->brush.transform().type() != QTransform::TxNone); |
788 | const bool penXform = (s->pen.brush().transform().type() != QTransform::TxNone); |
789 | |
790 | const bool patternXform = patternBrush && (xform || brushXform || penXform); |
791 | |
792 | // Check alphablending |
793 | if (alpha && !engine->hasFeature(QPaintEngine::AlphaBlend)) |
794 | s->emulationSpecifier |= QPaintEngine::AlphaBlend; |
795 | else |
796 | s->emulationSpecifier &= ~QPaintEngine::AlphaBlend; |
797 | |
798 | // Linear gradient emulation |
799 | if (linearGradient && !engine->hasFeature(QPaintEngine::LinearGradientFill)) |
800 | s->emulationSpecifier |= QPaintEngine::LinearGradientFill; |
801 | else |
802 | s->emulationSpecifier &= ~QPaintEngine::LinearGradientFill; |
803 | |
804 | // Radial gradient emulation |
805 | if (extendedRadialGradient || (radialGradient && !engine->hasFeature(QPaintEngine::RadialGradientFill))) |
806 | s->emulationSpecifier |= QPaintEngine::RadialGradientFill; |
807 | else |
808 | s->emulationSpecifier &= ~QPaintEngine::RadialGradientFill; |
809 | |
810 | // Conical gradient emulation |
811 | if (conicalGradient && !engine->hasFeature(QPaintEngine::ConicalGradientFill)) |
812 | s->emulationSpecifier |= QPaintEngine::ConicalGradientFill; |
813 | else |
814 | s->emulationSpecifier &= ~QPaintEngine::ConicalGradientFill; |
815 | |
816 | // Pattern brushes |
817 | if (patternBrush && !engine->hasFeature(QPaintEngine::PatternBrush)) |
818 | s->emulationSpecifier |= QPaintEngine::PatternBrush; |
819 | else |
820 | s->emulationSpecifier &= ~QPaintEngine::PatternBrush; |
821 | |
822 | // Pattern XForms |
823 | if (patternXform && !engine->hasFeature(QPaintEngine::PatternTransform)) |
824 | s->emulationSpecifier |= QPaintEngine::PatternTransform; |
825 | else |
826 | s->emulationSpecifier &= ~QPaintEngine::PatternTransform; |
827 | |
828 | // Primitive XForms |
829 | if (xform && !engine->hasFeature(QPaintEngine::PrimitiveTransform)) |
830 | s->emulationSpecifier |= QPaintEngine::PrimitiveTransform; |
831 | else |
832 | s->emulationSpecifier &= ~QPaintEngine::PrimitiveTransform; |
833 | |
834 | // Perspective XForms |
835 | if (complexXform && !engine->hasFeature(QPaintEngine::PerspectiveTransform)) |
836 | s->emulationSpecifier |= QPaintEngine::PerspectiveTransform; |
837 | else |
838 | s->emulationSpecifier &= ~QPaintEngine::PerspectiveTransform; |
839 | |
840 | // Constant opacity |
841 | if (state->opacity != 1 && !engine->hasFeature(QPaintEngine::ConstantOpacity)) |
842 | s->emulationSpecifier |= QPaintEngine::ConstantOpacity; |
843 | else |
844 | s->emulationSpecifier &= ~QPaintEngine::ConstantOpacity; |
845 | |
846 | bool gradientStretch = false; |
847 | bool objectBoundingMode = false; |
848 | if (linearGradient || conicalGradient || radialGradient) { |
849 | QGradient::CoordinateMode brushMode = coordinateMode(s->brush); |
850 | QGradient::CoordinateMode penMode = coordinateMode(s->pen.brush()); |
851 | |
852 | gradientStretch |= (brushMode == QGradient::StretchToDeviceMode); |
853 | gradientStretch |= (penMode == QGradient::StretchToDeviceMode); |
854 | |
855 | objectBoundingMode |= (brushMode == QGradient::ObjectBoundingMode || brushMode == QGradient::ObjectMode); |
856 | objectBoundingMode |= (penMode == QGradient::ObjectBoundingMode || penMode == QGradient::ObjectMode); |
857 | } |
858 | if (gradientStretch) |
859 | s->emulationSpecifier |= QGradient_StretchToDevice; |
860 | else |
861 | s->emulationSpecifier &= ~QGradient_StretchToDevice; |
862 | |
863 | if (objectBoundingMode && !engine->hasFeature(QPaintEngine::ObjectBoundingModeGradients)) |
864 | s->emulationSpecifier |= QPaintEngine::ObjectBoundingModeGradients; |
865 | else |
866 | s->emulationSpecifier &= ~QPaintEngine::ObjectBoundingModeGradients; |
867 | |
868 | // Opaque backgrounds... |
869 | if (s->bgMode == Qt::OpaqueMode && |
870 | (is_pen_transparent(s->pen) || is_brush_transparent(s->brush))) |
871 | s->emulationSpecifier |= QPaintEngine_OpaqueBackground; |
872 | else |
873 | s->emulationSpecifier &= ~QPaintEngine_OpaqueBackground; |
874 | |
875 | #if 0 |
876 | //won't be correct either way because the device can already have |
877 | // something rendered to it in which case subsequent emulation |
878 | // on a fully transparent qimage and then blitting the results |
879 | // won't produce correct results |
880 | // Blend modes |
881 | if (state->composition_mode > QPainter::CompositionMode_Xor && |
882 | !engine->hasFeature(QPaintEngine::BlendModes)) |
883 | s->emulationSpecifier |= QPaintEngine::BlendModes; |
884 | else |
885 | s->emulationSpecifier &= ~QPaintEngine::BlendModes; |
886 | #endif |
887 | } |
888 | |
889 | void QPainterPrivate::updateStateImpl(QPainterState *newState) |
890 | { |
891 | // ### we might have to call QPainter::begin() here... |
892 | if (!engine->state) { |
893 | engine->state = newState; |
894 | engine->setDirty(QPaintEngine::AllDirty); |
895 | } |
896 | |
897 | if (engine->state->painter() != newState->painter) |
898 | // ### this could break with clip regions vs paths. |
899 | engine->setDirty(QPaintEngine::AllDirty); |
900 | |
901 | // Upon restore, revert all changes since last save |
902 | else if (engine->state != newState) |
903 | newState->dirtyFlags |= QPaintEngine::DirtyFlags(static_cast<QPainterState *>(engine->state)->changeFlags); |
904 | |
905 | // We need to store all changes made so that restore can deal with them |
906 | else |
907 | newState->changeFlags |= newState->dirtyFlags; |
908 | |
909 | updateEmulationSpecifier(newState); |
910 | |
911 | // Unset potential dirty background mode |
912 | newState->dirtyFlags &= ~(QPaintEngine::DirtyBackgroundMode |
913 | | QPaintEngine::DirtyBackground); |
914 | |
915 | engine->state = newState; |
916 | engine->updateState(*newState); |
917 | engine->clearDirty(QPaintEngine::AllDirty); |
918 | |
919 | } |
920 | |
921 | void QPainterPrivate::updateState(QPainterState *newState) |
922 | { |
923 | |
924 | if (!newState) { |
925 | engine->state = newState; |
926 | } else if (newState->state() || engine->state!=newState) { |
927 | updateStateImpl(newState); |
928 | } |
929 | } |
930 | |
931 | |
932 | /*! |
933 | \class QPainter |
934 | \brief The QPainter class performs low-level painting on widgets and |
935 | other paint devices. |
936 | |
937 | \inmodule QtGui |
938 | \ingroup painting |
939 | |
940 | \reentrant |
941 | |
942 | QPainter provides highly optimized functions to do most of the |
943 | drawing GUI programs require. It can draw everything from simple |
944 | lines to complex shapes like pies and chords. It can also draw |
945 | aligned text and pixmaps. Normally, it draws in a "natural" |
946 | coordinate system, but it can also do view and world |
947 | transformation. QPainter can operate on any object that inherits |
948 | the QPaintDevice class. |
949 | |
950 | The common use of QPainter is inside a widget's paint event: |
951 | Construct and customize (e.g. set the pen or the brush) the |
952 | painter. Then draw. Remember to destroy the QPainter object after |
953 | drawing. For example: |
954 | |
955 | \snippet code/src_gui_painting_qpainter.cpp 0 |
956 | |
957 | The core functionality of QPainter is drawing, but the class also |
958 | provide several functions that allows you to customize QPainter's |
959 | settings and its rendering quality, and others that enable |
960 | clipping. In addition you can control how different shapes are |
961 | merged together by specifying the painter's composition mode. |
962 | |
963 | The isActive() function indicates whether the painter is active. A |
964 | painter is activated by the begin() function and the constructor |
965 | that takes a QPaintDevice argument. The end() function, and the |
966 | destructor, deactivates it. |
967 | |
968 | Together with the QPaintDevice and QPaintEngine classes, QPainter |
969 | form the basis for Qt's paint system. QPainter is the class used |
970 | to perform drawing operations. QPaintDevice represents a device |
971 | that can be painted on using a QPainter. QPaintEngine provides the |
972 | interface that the painter uses to draw onto different types of |
973 | devices. If the painter is active, device() returns the paint |
974 | device on which the painter paints, and paintEngine() returns the |
975 | paint engine that the painter is currently operating on. For more |
976 | information, see the \l {Paint System}. |
977 | |
978 | Sometimes it is desirable to make someone else paint on an unusual |
979 | QPaintDevice. QPainter supports a static function to do this, |
980 | setRedirected(). |
981 | |
982 | \warning When the paintdevice is a widget, QPainter can only be |
983 | used inside a paintEvent() function or in a function called by |
984 | paintEvent(). |
985 | |
986 | \tableofcontents |
987 | |
988 | \section1 Settings |
989 | |
990 | There are several settings that you can customize to make QPainter |
991 | draw according to your preferences: |
992 | |
993 | \list |
994 | |
995 | \li font() is the font used for drawing text. If the painter |
996 | isActive(), you can retrieve information about the currently set |
997 | font, and its metrics, using the fontInfo() and fontMetrics() |
998 | functions respectively. |
999 | |
1000 | \li brush() defines the color or pattern that is used for filling |
1001 | shapes. |
1002 | |
1003 | \li pen() defines the color or stipple that is used for drawing |
1004 | lines or boundaries. |
1005 | |
1006 | \li backgroundMode() defines whether there is a background() or |
1007 | not, i.e it is either Qt::OpaqueMode or Qt::TransparentMode. |
1008 | |
1009 | \li background() only applies when backgroundMode() is \l |
1010 | Qt::OpaqueMode and pen() is a stipple. In that case, it |
1011 | describes the color of the background pixels in the stipple. |
1012 | |
1013 | \li brushOrigin() defines the origin of the tiled brushes, normally |
1014 | the origin of widget's background. |
1015 | |
1016 | \li viewport(), window(), worldTransform() make up the painter's coordinate |
1017 | transformation system. For more information, see the \l |
1018 | {Coordinate Transformations} section and the \l {Coordinate |
1019 | System} documentation. |
1020 | |
1021 | \li hasClipping() tells whether the painter clips at all. (The paint |
1022 | device clips, too.) If the painter clips, it clips to clipRegion(). |
1023 | |
1024 | \li layoutDirection() defines the layout direction used by the |
1025 | painter when drawing text. |
1026 | |
1027 | \li worldMatrixEnabled() tells whether world transformation is enabled. |
1028 | |
1029 | \li viewTransformEnabled() tells whether view transformation is |
1030 | enabled. |
1031 | |
1032 | \endlist |
1033 | |
1034 | Note that some of these settings mirror settings in some paint |
1035 | devices, e.g. QWidget::font(). The QPainter::begin() function (or |
1036 | equivalently the QPainter constructor) copies these attributes |
1037 | from the paint device. |
1038 | |
1039 | You can at any time save the QPainter's state by calling the |
1040 | save() function which saves all the available settings on an |
1041 | internal stack. The restore() function pops them back. |
1042 | |
1043 | \section1 Drawing |
1044 | |
1045 | QPainter provides functions to draw most primitives: drawPoint(), |
1046 | drawPoints(), drawLine(), drawRect(), drawRoundedRect(), |
1047 | drawEllipse(), drawArc(), drawPie(), drawChord(), drawPolyline(), |
1048 | drawPolygon(), drawConvexPolygon() and drawCubicBezier(). The two |
1049 | convenience functions, drawRects() and drawLines(), draw the given |
1050 | number of rectangles or lines in the given array of \l |
1051 | {QRect}{QRects} or \l {QLine}{QLines} using the current pen and |
1052 | brush. |
1053 | |
1054 | The QPainter class also provides the fillRect() function which |
1055 | fills the given QRect, with the given QBrush, and the eraseRect() |
1056 | function that erases the area inside the given rectangle. |
1057 | |
1058 | All of these functions have both integer and floating point |
1059 | versions. |
1060 | |
1061 | \table 100% |
1062 | \row |
1063 | \li \inlineimage qpainter-basicdrawing.png |
1064 | \li |
1065 | \b {Basic Drawing Example} |
1066 | |
1067 | The \l {painting/basicdrawing}{Basic Drawing} example shows how to |
1068 | display basic graphics primitives in a variety of styles using the |
1069 | QPainter class. |
1070 | |
1071 | \endtable |
1072 | |
1073 | If you need to draw a complex shape, especially if you need to do |
1074 | so repeatedly, consider creating a QPainterPath and drawing it |
1075 | using drawPath(). |
1076 | |
1077 | \table 100% |
1078 | \row |
1079 | \li |
1080 | \b {Painter Paths example} |
1081 | |
1082 | The QPainterPath class provides a container for painting |
1083 | operations, enabling graphical shapes to be constructed and |
1084 | reused. |
1085 | |
1086 | The \l {painting/painterpaths}{Painter Paths} example shows how |
1087 | painter paths can be used to build complex shapes for rendering. |
1088 | |
1089 | \li \inlineimage qpainter-painterpaths.png |
1090 | \endtable |
1091 | |
1092 | QPainter also provides the fillPath() function which fills the |
1093 | given QPainterPath with the given QBrush, and the strokePath() |
1094 | function that draws the outline of the given path (i.e. strokes |
1095 | the path). |
1096 | |
1097 | See also the \l {painting/deform}{Vector Deformation} example which |
1098 | shows how to use advanced vector techniques to draw text using a |
1099 | QPainterPath, the \l {painting/gradients}{Gradients} example which shows |
1100 | the different types of gradients that are available in Qt, and the \l |
1101 | {painting/pathstroke}{Path Stroking} example which shows Qt's built-in |
1102 | dash patterns and shows how custom patterns can be used to extend |
1103 | the range of available patterns. |
1104 | |
1105 | \table |
1106 | \header |
1107 | \li \l {painting/deform}{Vector Deformation} |
1108 | \li \l {painting/gradients}{Gradients} |
1109 | \li \l {painting/pathstroke}{Path Stroking} |
1110 | \row |
1111 | \li \inlineimage qpainter-vectordeformation.png |
1112 | \li \inlineimage qpainter-gradients.png |
1113 | \li \inlineimage qpainter-pathstroking.png |
1114 | \endtable |
1115 | |
1116 | Text drawing is done using drawText(). When you need |
1117 | fine-grained positioning, boundingRect() tells you where a given |
1118 | drawText() command will draw. |
1119 | |
1120 | \section1 Drawing Pixmaps and Images |
1121 | |
1122 | There are functions to draw pixmaps/images, namely drawPixmap(), |
1123 | drawImage() and drawTiledPixmap(). Both drawPixmap() and drawImage() |
1124 | produce the same result, except that drawPixmap() is faster |
1125 | on-screen while drawImage() may be faster on a QPrinter or other |
1126 | devices. |
1127 | |
1128 | There is a drawPicture() function that draws the contents of an |
1129 | entire QPicture. The drawPicture() function is the only function |
1130 | that disregards all the painter's settings as QPicture has its own |
1131 | settings. |
1132 | |
1133 | \section2 Drawing High Resolution Versions of Pixmaps and Images |
1134 | |
1135 | High resolution versions of pixmaps have a \e{device pixel ratio} value larger |
1136 | than 1 (see QImageReader, QPixmap::devicePixelRatio()). Should it match the value |
1137 | of the underlying QPaintDevice, it is drawn directly onto the device with no |
1138 | additional transformation applied. |
1139 | |
1140 | This is for example the case when drawing a QPixmap of 64x64 pixels size with |
1141 | a device pixel ratio of 2 onto a high DPI screen which also has |
1142 | a device pixel ratio of 2. Note that the pixmap is then effectively 32x32 |
1143 | pixels in \e{user space}. Code paths in Qt that calculate layout geometry |
1144 | based on the pixmap size will use this size. The net effect of this is that |
1145 | the pixmap is displayed as high DPI pixmap rather than a large pixmap. |
1146 | |
1147 | \section1 Rendering Quality |
1148 | |
1149 | To get the optimal rendering result using QPainter, you should use |
1150 | the platform independent QImage as paint device; i.e. using QImage |
1151 | will ensure that the result has an identical pixel representation |
1152 | on any platform. |
1153 | |
1154 | The QPainter class also provides a means of controlling the |
1155 | rendering quality through its RenderHint enum and the support for |
1156 | floating point precision: All the functions for drawing primitives |
1157 | has a floating point version. These are often used in combination |
1158 | with the \l {RenderHint}{QPainter::Antialiasing} render hint. |
1159 | |
1160 | \table 100% |
1161 | \row |
1162 | \li \inlineimage qpainter-concentriccircles.png |
1163 | \li |
1164 | \b {Concentric Circles Example} |
1165 | |
1166 | The \l {painting/concentriccircles}{Concentric Circles} example |
1167 | shows the improved rendering quality that can be obtained using |
1168 | floating point precision and anti-aliasing when drawing custom |
1169 | widgets. |
1170 | |
1171 | The application's main window displays several widgets which are |
1172 | drawn using the various combinations of precision and |
1173 | anti-aliasing. |
1174 | |
1175 | \endtable |
1176 | |
1177 | The RenderHint enum specifies flags to QPainter that may or may |
1178 | not be respected by any given engine. \l |
1179 | {RenderHint}{QPainter::Antialiasing} indicates that the engine |
1180 | should antialias edges of primitives if possible, \l |
1181 | {RenderHint}{QPainter::TextAntialiasing} indicates that the engine |
1182 | should antialias text if possible, and the \l |
1183 | {RenderHint}{QPainter::SmoothPixmapTransform} indicates that the |
1184 | engine should use a smooth pixmap transformation algorithm. |
1185 | |
1186 | The renderHints() function returns a flag that specifies the |
1187 | rendering hints that are set for this painter. Use the |
1188 | setRenderHint() function to set or clear the currently set |
1189 | RenderHints. |
1190 | |
1191 | \section1 Coordinate Transformations |
1192 | |
1193 | Normally, the QPainter operates on the device's own coordinate |
1194 | system (usually pixels), but QPainter has good support for |
1195 | coordinate transformations. |
1196 | |
1197 | \table |
1198 | \header |
1199 | \li nop \li rotate() \li scale() \li translate() |
1200 | \row |
1201 | \li \inlineimage qpainter-clock.png |
1202 | \li \inlineimage qpainter-rotation.png |
1203 | \li \inlineimage qpainter-scale.png |
1204 | \li \inlineimage qpainter-translation.png |
1205 | \endtable |
1206 | |
1207 | The most commonly used transformations are scaling, rotation, |
1208 | translation and shearing. Use the scale() function to scale the |
1209 | coordinate system by a given offset, the rotate() function to |
1210 | rotate it clockwise and translate() to translate it (i.e. adding a |
1211 | given offset to the points). You can also twist the coordinate |
1212 | system around the origin using the shear() function. See the \l |
1213 | {painting/affine}{Affine Transformations} example for a visualization of |
1214 | a sheared coordinate system. |
1215 | |
1216 | See also the \l {painting/transformations}{Transformations} |
1217 | example which shows how transformations influence the way that |
1218 | QPainter renders graphics primitives. In particular it shows how |
1219 | the order of transformations affects the result. |
1220 | |
1221 | \table 100% |
1222 | \row |
1223 | \li |
1224 | \b {Affine Transformations Example} |
1225 | |
1226 | The \l {painting/affine}{Affine Transformations} example shows Qt's |
1227 | ability to perform affine transformations on painting |
1228 | operations. The demo also allows the user to experiment with the |
1229 | transformation operations and see the results immediately. |
1230 | |
1231 | \li \inlineimage qpainter-affinetransformations.png |
1232 | \endtable |
1233 | |
1234 | All the tranformation operations operate on the transformation |
1235 | worldTransform(). A matrix transforms a point in the plane to another |
1236 | point. For more information about the transformation matrix, see |
1237 | the \l {Coordinate System} and QTransform documentation. |
1238 | |
1239 | The setWorldTransform() function can replace or add to the currently |
1240 | set worldTransform(). The resetTransform() function resets any |
1241 | transformations that were made using translate(), scale(), |
1242 | shear(), rotate(), setWorldTransform(), setViewport() and setWindow() |
1243 | functions. The deviceTransform() returns the matrix that transforms |
1244 | from logical coordinates to device coordinates of the platform |
1245 | dependent paint device. The latter function is only needed when |
1246 | using platform painting commands on the platform dependent handle, |
1247 | and the platform does not do transformations nativly. |
1248 | |
1249 | When drawing with QPainter, we specify points using logical |
1250 | coordinates which then are converted into the physical coordinates |
1251 | of the paint device. The mapping of the logical coordinates to the |
1252 | physical coordinates are handled by QPainter's combinedTransform(), a |
1253 | combination of viewport() and window() and worldTransform(). The |
1254 | viewport() represents the physical coordinates specifying an |
1255 | arbitrary rectangle, the window() describes the same rectangle in |
1256 | logical coordinates, and the worldTransform() is identical with the |
1257 | transformation matrix. |
1258 | |
1259 | See also \l {Coordinate System} |
1260 | |
1261 | \section1 Clipping |
1262 | |
1263 | QPainter can clip any drawing operation to a rectangle, a region, |
1264 | or a vector path. The current clip is available using the |
1265 | functions clipRegion() and clipPath(). Whether paths or regions are |
1266 | preferred (faster) depends on the underlying paintEngine(). For |
1267 | example, the QImage paint engine prefers paths while the X11 paint |
1268 | engine prefers regions. Setting a clip is done in the painters |
1269 | logical coordinates. |
1270 | |
1271 | After QPainter's clipping, the paint device may also clip. For |
1272 | example, most widgets clip away the pixels used by child widgets, |
1273 | and most printers clip away an area near the edges of the paper. |
1274 | This additional clipping is not reflected by the return value of |
1275 | clipRegion() or hasClipping(). |
1276 | |
1277 | \section1 Composition Modes |
1278 | \target Composition Modes |
1279 | |
1280 | QPainter provides the CompositionMode enum which defines the |
1281 | Porter-Duff rules for digital image compositing; it describes a |
1282 | model for combining the pixels in one image, the source, with the |
1283 | pixels in another image, the destination. |
1284 | |
1285 | The two most common forms of composition are \l |
1286 | {QPainter::CompositionMode}{Source} and \l |
1287 | {QPainter::CompositionMode}{SourceOver}. \l |
1288 | {QPainter::CompositionMode}{Source} is used to draw opaque objects |
1289 | onto a paint device. In this mode, each pixel in the source |
1290 | replaces the corresponding pixel in the destination. In \l |
1291 | {QPainter::CompositionMode}{SourceOver} composition mode, the |
1292 | source object is transparent and is drawn on top of the |
1293 | destination. |
1294 | |
1295 | Note that composition transformation operates pixelwise. For that |
1296 | reason, there is a difference between using the graphic primitive |
1297 | itself and its bounding rectangle: The bounding rect contains |
1298 | pixels with alpha == 0 (i.e the pixels surrounding the |
1299 | primitive). These pixels will overwrite the other image's pixels, |
1300 | effectively clearing those, while the primitive only overwrites |
1301 | its own area. |
1302 | |
1303 | \table 100% |
1304 | \row |
1305 | \li \inlineimage qpainter-compositiondemo.png |
1306 | |
1307 | \li |
1308 | \b {Composition Modes Example} |
1309 | |
1310 | The \l {painting/composition}{Composition Modes} example, available in |
1311 | Qt's examples directory, allows you to experiment with the various |
1312 | composition modes and see the results immediately. |
1313 | |
1314 | \endtable |
1315 | |
1316 | \section1 Limitations |
1317 | \target Limitations |
1318 | |
1319 | If you are using coordinates with Qt's raster-based paint engine, it is |
1320 | important to note that, while coordinates greater than +/- 2\sup 15 can |
1321 | be used, any painting performed with coordinates outside this range is not |
1322 | guaranteed to be shown; the drawing may be clipped. This is due to the |
1323 | use of \c{short int} in the implementation. |
1324 | |
1325 | The outlines generated by Qt's stroker are only an approximation when dealing |
1326 | with curved shapes. It is in most cases impossible to represent the outline of |
1327 | a bezier curve segment using another bezier curve segment, and so Qt approximates |
1328 | the curve outlines by using several smaller curves. For performance reasons there |
1329 | is a limit to how many curves Qt uses for these outlines, and thus when using |
1330 | large pen widths or scales the outline error increases. To generate outlines with |
1331 | smaller errors it is possible to use the QPainterPathStroker class, which has the |
1332 | setCurveThreshold member function which let's the user specify the error tolerance. |
1333 | Another workaround is to convert the paths to polygons first and then draw the |
1334 | polygons instead. |
1335 | |
1336 | \section1 Performance |
1337 | |
1338 | QPainter is a rich framework that allows developers to do a great |
1339 | variety of graphical operations, such as gradients, composition |
1340 | modes and vector graphics. And QPainter can do this across a |
1341 | variety of different hardware and software stacks. Naturally the |
1342 | underlying combination of hardware and software has some |
1343 | implications for performance, and ensuring that every single |
1344 | operation is fast in combination with all the various combinations |
1345 | of composition modes, brushes, clipping, transformation, etc, is |
1346 | close to an impossible task because of the number of |
1347 | permutations. As a compromise we have selected a subset of the |
1348 | QPainter API and backends, where performance is guaranteed to be as |
1349 | good as we can sensibly get it for the given combination of |
1350 | hardware and software. |
1351 | |
1352 | The backends we focus on as high-performance engines are: |
1353 | |
1354 | \list |
1355 | |
1356 | \li Raster - This backend implements all rendering in pure software |
1357 | and is always used to render into QImages. For optimal performance |
1358 | only use the format types QImage::Format_ARGB32_Premultiplied, |
1359 | QImage::Format_RGB32 or QImage::Format_RGB16. Any other format, |
1360 | including QImage::Format_ARGB32, has significantly worse |
1361 | performance. This engine is used by default for QWidget and QPixmap. |
1362 | |
1363 | \li OpenGL 2.0 (ES) - This backend is the primary backend for |
1364 | hardware accelerated graphics. It can be run on desktop machines |
1365 | and embedded devices supporting the OpenGL 2.0 or OpenGL/ES 2.0 |
1366 | specification. This includes most graphics chips produced in the |
1367 | last couple of years. The engine can be enabled by using QPainter |
1368 | onto a QOpenGLWidget. |
1369 | |
1370 | \endlist |
1371 | |
1372 | These operations are: |
1373 | |
1374 | \list |
1375 | |
1376 | \li Simple transformations, meaning translation and scaling, pluss |
1377 | 0, 90, 180, 270 degree rotations. |
1378 | |
1379 | \li \c drawPixmap() in combination with simple transformations and |
1380 | opacity with non-smooth transformation mode |
1381 | (\c QPainter::SmoothPixmapTransform not enabled as a render hint). |
1382 | |
1383 | \li Rectangle fills with solid color, two-color linear gradients |
1384 | and simple transforms. |
1385 | |
1386 | \li Rectangular clipping with simple transformations and intersect |
1387 | clip. |
1388 | |
1389 | \li Composition Modes \c QPainter::CompositionMode_Source and |
1390 | QPainter::CompositionMode_SourceOver. |
1391 | |
1392 | \li Rounded rectangle filling using solid color and two-color |
1393 | linear gradients fills. |
1394 | |
1395 | \li 3x3 patched pixmaps, via qDrawBorderPixmap. |
1396 | |
1397 | \endlist |
1398 | |
1399 | This list gives an indication of which features to safely use in |
1400 | an application where performance is critical. For certain setups, |
1401 | other operations may be fast too, but before making extensive use |
1402 | of them, it is recommended to benchmark and verify them on the |
1403 | system where the software will run in the end. There are also |
1404 | cases where expensive operations are ok to use, for instance when |
1405 | the result is cached in a QPixmap. |
1406 | |
1407 | \sa QPaintDevice, QPaintEngine, {Qt SVG}, {Basic Drawing Example}, |
1408 | {Drawing Utility Functions} |
1409 | */ |
1410 | |
1411 | /*! |
1412 | \enum QPainter::RenderHint |
1413 | |
1414 | Renderhints are used to specify flags to QPainter that may or |
1415 | may not be respected by any given engine. |
1416 | |
1417 | \value Antialiasing Indicates that the engine should antialias |
1418 | edges of primitives if possible. |
1419 | |
1420 | \value TextAntialiasing Indicates that the engine should antialias |
1421 | text if possible. To forcibly disable antialiasing for text, do not |
1422 | use this hint. Instead, set QFont::NoAntialias on your font's style |
1423 | strategy. |
1424 | |
1425 | \value SmoothPixmapTransform Indicates that the engine should use |
1426 | a smooth pixmap transformation algorithm (such as bilinear) rather |
1427 | than nearest neighbor. |
1428 | |
1429 | \value HighQualityAntialiasing This value is obsolete and will be ignored, |
1430 | use the Antialiasing render hint instead. |
1431 | |
1432 | \value NonCosmeticDefaultPen This value is obsolete, the default for QPen |
1433 | is now non-cosmetic. |
1434 | |
1435 | \value Qt4CompatiblePainting Compatibility hint telling the engine to use the |
1436 | same X11 based fill rules as in Qt 4, where aliased rendering is offset |
1437 | by slightly less than half a pixel. Also will treat default constructed pens |
1438 | as cosmetic. Potentially useful when porting a Qt 4 application to Qt 5. |
1439 | |
1440 | \value LosslessImageRendering Use a lossless image rendering, whenever possible. |
1441 | Currently, this hint is only used when QPainter is employed to output a PDF |
1442 | file through QPrinter or QPdfWriter, where drawImage()/drawPixmap() calls |
1443 | will encode images using a lossless compression algorithm instead of lossy |
1444 | JPEG compression. |
1445 | This value was added in Qt 5.13. |
1446 | |
1447 | \sa renderHints(), setRenderHint(), {QPainter#Rendering |
1448 | Quality}{Rendering Quality}, {Concentric Circles Example} |
1449 | |
1450 | */ |
1451 | |
1452 | /*! |
1453 | Constructs a painter. |
1454 | |
1455 | \sa begin(), end() |
1456 | */ |
1457 | |
1458 | QPainter::QPainter() |
1459 | : d_ptr(new QPainterPrivate(this)) |
1460 | { |
1461 | } |
1462 | |
1463 | /*! |
1464 | \fn QPainter::QPainter(QPaintDevice *device) |
1465 | |
1466 | Constructs a painter that begins painting the paint \a device |
1467 | immediately. |
1468 | |
1469 | This constructor is convenient for short-lived painters, e.g. in a |
1470 | QWidget::paintEvent() and should be used only once. The |
1471 | constructor calls begin() for you and the QPainter destructor |
1472 | automatically calls end(). |
1473 | |
1474 | Here's an example using begin() and end(): |
1475 | \snippet code/src_gui_painting_qpainter.cpp 1 |
1476 | |
1477 | The same example using this constructor: |
1478 | \snippet code/src_gui_painting_qpainter.cpp 2 |
1479 | |
1480 | Since the constructor cannot provide feedback when the initialization |
1481 | of the painter failed you should rather use begin() and end() to paint |
1482 | on external devices, e.g. printers. |
1483 | |
1484 | \sa begin(), end() |
1485 | */ |
1486 | |
1487 | QPainter::QPainter(QPaintDevice *pd) |
1488 | : d_ptr(0) |
1489 | { |
1490 | Q_ASSERT(pd != 0); |
1491 | if (!QPainterPrivate::attachPainterPrivate(this, pd)) { |
1492 | d_ptr.reset(new QPainterPrivate(this)); |
1493 | begin(pd); |
1494 | } |
1495 | Q_ASSERT(d_ptr); |
1496 | } |
1497 | |
1498 | /*! |
1499 | Destroys the painter. |
1500 | */ |
1501 | QPainter::~QPainter() |
1502 | { |
1503 | d_ptr->inDestructor = true; |
1504 | QT_TRY { |
1505 | if (isActive()) |
1506 | end(); |
1507 | else if (d_ptr->refcount > 1) |
1508 | d_ptr->detachPainterPrivate(this); |
1509 | } QT_CATCH(...) { |
1510 | // don't throw anything in the destructor. |
1511 | } |
1512 | if (d_ptr) { |
1513 | // Make sure we haven't messed things up. |
1514 | Q_ASSERT(d_ptr->inDestructor); |
1515 | d_ptr->inDestructor = false; |
1516 | Q_ASSERT(d_ptr->refcount == 1); |
1517 | if (d_ptr->d_ptrs) |
1518 | free(d_ptr->d_ptrs); |
1519 | } |
1520 | } |
1521 | |
1522 | /*! |
1523 | Returns the paint device on which this painter is currently |
1524 | painting, or \nullptr if the painter is not active. |
1525 | |
1526 | \sa isActive() |
1527 | */ |
1528 | |
1529 | QPaintDevice *QPainter::device() const |
1530 | { |
1531 | Q_D(const QPainter); |
1532 | if (isActive() && d->engine->d_func()->currentClipDevice) |
1533 | return d->engine->d_func()->currentClipDevice; |
1534 | return d->original_device; |
1535 | } |
1536 | |
1537 | /*! |
1538 | Returns \c true if begin() has been called and end() has not yet been |
1539 | called; otherwise returns \c false. |
1540 | |
1541 | \sa begin(), QPaintDevice::paintingActive() |
1542 | */ |
1543 | |
1544 | bool QPainter::isActive() const |
1545 | { |
1546 | Q_D(const QPainter); |
1547 | return d->engine; |
1548 | } |
1549 | |
1550 | #if QT_DEPRECATED_SINCE(5, 13) |
1551 | /*! |
1552 | Initializes the painters pen, background and font to the same as |
1553 | the given \a device. |
1554 | |
1555 | \obsolete |
1556 | |
1557 | \sa begin(), {QPainter#Settings}{Settings} |
1558 | */ |
1559 | void QPainter::initFrom(const QPaintDevice *device) |
1560 | { |
1561 | Q_ASSERT_X(device, "QPainter::initFrom(const QPaintDevice *device)" , "QPaintDevice cannot be 0" ); |
1562 | Q_D(QPainter); |
1563 | d->initFrom(device); |
1564 | } |
1565 | #endif |
1566 | |
1567 | void QPainterPrivate::initFrom(const QPaintDevice *device) |
1568 | { |
1569 | if (!engine) { |
1570 | qWarning("QPainter::initFrom: Painter not active, aborted" ); |
1571 | return; |
1572 | } |
1573 | |
1574 | Q_Q(QPainter); |
1575 | device->initPainter(q); |
1576 | |
1577 | if (extended) { |
1578 | extended->penChanged(); |
1579 | } else if (engine) { |
1580 | engine->setDirty(QPaintEngine::DirtyPen); |
1581 | engine->setDirty(QPaintEngine::DirtyBrush); |
1582 | engine->setDirty(QPaintEngine::DirtyFont); |
1583 | } |
1584 | } |
1585 | |
1586 | /*! |
1587 | Saves the current painter state (pushes the state onto a stack). A |
1588 | save() must be followed by a corresponding restore(); the end() |
1589 | function unwinds the stack. |
1590 | |
1591 | \sa restore() |
1592 | */ |
1593 | |
1594 | void QPainter::save() |
1595 | { |
1596 | #ifdef QT_DEBUG_DRAW |
1597 | if (qt_show_painter_debug_output) |
1598 | printf("QPainter::save()\n" ); |
1599 | #endif |
1600 | Q_D(QPainter); |
1601 | if (!d->engine) { |
1602 | qWarning("QPainter::save: Painter not active" ); |
1603 | return; |
1604 | } |
1605 | |
1606 | if (d->extended) { |
1607 | d->state = d->extended->createState(d->states.back()); |
1608 | d->extended->setState(d->state); |
1609 | } else { |
1610 | d->updateState(d->state); |
1611 | d->state = new QPainterState(d->states.back()); |
1612 | d->engine->state = d->state; |
1613 | } |
1614 | d->states.push_back(d->state); |
1615 | } |
1616 | |
1617 | /*! |
1618 | Restores the current painter state (pops a saved state off the |
1619 | stack). |
1620 | |
1621 | \sa save() |
1622 | */ |
1623 | |
1624 | void QPainter::restore() |
1625 | { |
1626 | #ifdef QT_DEBUG_DRAW |
1627 | if (qt_show_painter_debug_output) |
1628 | printf("QPainter::restore()\n" ); |
1629 | #endif |
1630 | Q_D(QPainter); |
1631 | if (d->states.size()<=1) { |
1632 | qWarning("QPainter::restore: Unbalanced save/restore" ); |
1633 | return; |
1634 | } else if (!d->engine) { |
1635 | qWarning("QPainter::restore: Painter not active" ); |
1636 | return; |
1637 | } |
1638 | |
1639 | QPainterState *tmp = d->state; |
1640 | d->states.pop_back(); |
1641 | d->state = d->states.back(); |
1642 | d->txinv = false; |
1643 | |
1644 | if (d->extended) { |
1645 | d->checkEmulation(); |
1646 | d->extended->setState(d->state); |
1647 | delete tmp; |
1648 | return; |
1649 | } |
1650 | |
1651 | // trigger clip update if the clip path/region has changed since |
1652 | // last save |
1653 | if (!d->state->clipInfo.isEmpty() |
1654 | && (tmp->changeFlags & (QPaintEngine::DirtyClipRegion | QPaintEngine::DirtyClipPath))) { |
1655 | // reuse the tmp state to avoid any extra allocs... |
1656 | tmp->dirtyFlags = QPaintEngine::DirtyClipPath; |
1657 | tmp->clipOperation = Qt::NoClip; |
1658 | tmp->clipPath = QPainterPath(); |
1659 | d->engine->updateState(*tmp); |
1660 | // replay the list of clip states, |
1661 | for (const QPainterClipInfo &info : qAsConst(d->state->clipInfo)) { |
1662 | tmp->matrix = info.matrix; |
1663 | tmp->matrix *= d->state->redirectionMatrix; |
1664 | tmp->clipOperation = info.operation; |
1665 | if (info.clipType == QPainterClipInfo::RectClip) { |
1666 | tmp->dirtyFlags = QPaintEngine::DirtyClipRegion | QPaintEngine::DirtyTransform; |
1667 | tmp->clipRegion = info.rect; |
1668 | } else if (info.clipType == QPainterClipInfo::RegionClip) { |
1669 | tmp->dirtyFlags = QPaintEngine::DirtyClipRegion | QPaintEngine::DirtyTransform; |
1670 | tmp->clipRegion = info.region; |
1671 | } else { // clipType == QPainterClipInfo::PathClip |
1672 | tmp->dirtyFlags = QPaintEngine::DirtyClipPath | QPaintEngine::DirtyTransform; |
1673 | tmp->clipPath = info.path; |
1674 | } |
1675 | d->engine->updateState(*tmp); |
1676 | } |
1677 | |
1678 | |
1679 | //Since we've updated the clip region anyway, pretend that the clip path hasn't changed: |
1680 | d->state->dirtyFlags &= ~(QPaintEngine::DirtyClipPath | QPaintEngine::DirtyClipRegion); |
1681 | tmp->changeFlags &= ~uint(QPaintEngine::DirtyClipPath | QPaintEngine::DirtyClipRegion); |
1682 | tmp->changeFlags |= QPaintEngine::DirtyTransform; |
1683 | } |
1684 | |
1685 | d->updateState(d->state); |
1686 | delete tmp; |
1687 | } |
1688 | |
1689 | |
1690 | /*! |
1691 | |
1692 | \fn bool QPainter::begin(QPaintDevice *device) |
1693 | |
1694 | Begins painting the paint \a device and returns \c true if |
1695 | successful; otherwise returns \c false. |
1696 | |
1697 | Notice that all painter settings (setPen(), setBrush() etc.) are reset |
1698 | to default values when begin() is called. |
1699 | |
1700 | The errors that can occur are serious problems, such as these: |
1701 | |
1702 | \snippet code/src_gui_painting_qpainter.cpp 3 |
1703 | |
1704 | Note that most of the time, you can use one of the constructors |
1705 | instead of begin(), and that end() is automatically done at |
1706 | destruction. |
1707 | |
1708 | \warning A paint device can only be painted by one painter at a |
1709 | time. |
1710 | |
1711 | \warning Painting on a QImage with the format |
1712 | QImage::Format_Indexed8 is not supported. |
1713 | |
1714 | \sa end(), QPainter() |
1715 | */ |
1716 | |
1717 | static inline void qt_cleanup_painter_state(QPainterPrivate *d) |
1718 | { |
1719 | d->states.clear(); |
1720 | delete d->state; |
1721 | d->state = 0; |
1722 | d->engine = 0; |
1723 | d->device = 0; |
1724 | } |
1725 | |
1726 | bool QPainter::begin(QPaintDevice *pd) |
1727 | { |
1728 | Q_ASSERT(pd); |
1729 | |
1730 | if (pd->painters > 0) { |
1731 | qWarning("QPainter::begin: A paint device can only be painted by one painter at a time." ); |
1732 | return false; |
1733 | } |
1734 | |
1735 | if (d_ptr->engine) { |
1736 | qWarning("QPainter::begin: Painter already active" ); |
1737 | return false; |
1738 | } |
1739 | |
1740 | if (QPainterPrivate::attachPainterPrivate(this, pd)) |
1741 | return true; |
1742 | |
1743 | Q_D(QPainter); |
1744 | |
1745 | d->helper_device = pd; |
1746 | d->original_device = pd; |
1747 | |
1748 | QPoint redirectionOffset; |
1749 | QPaintDevice *rpd = pd->redirected(&redirectionOffset); |
1750 | if (rpd) |
1751 | pd = rpd; |
1752 | |
1753 | #ifdef QT_DEBUG_DRAW |
1754 | if (qt_show_painter_debug_output) |
1755 | printf("QPainter::begin(), device=%p, type=%d\n" , pd, pd->devType()); |
1756 | #endif |
1757 | |
1758 | if (pd->devType() == QInternal::Pixmap) |
1759 | static_cast<QPixmap *>(pd)->detach(); |
1760 | else if (pd->devType() == QInternal::Image) |
1761 | static_cast<QImage *>(pd)->detach(); |
1762 | |
1763 | d->engine = pd->paintEngine(); |
1764 | |
1765 | if (!d->engine) { |
1766 | qWarning("QPainter::begin: Paint device returned engine == 0, type: %d" , pd->devType()); |
1767 | return false; |
1768 | } |
1769 | |
1770 | d->device = pd; |
1771 | |
1772 | d->extended = d->engine->isExtended() ? static_cast<QPaintEngineEx *>(d->engine) : 0; |
1773 | if (d->emulationEngine) |
1774 | d->emulationEngine->real_engine = d->extended; |
1775 | |
1776 | // Setup new state... |
1777 | Q_ASSERT(!d->state); |
1778 | d->state = d->extended ? d->extended->createState(0) : new QPainterState; |
1779 | d->state->painter = this; |
1780 | d->states.push_back(d->state); |
1781 | |
1782 | d->state->redirectionMatrix.translate(-redirectionOffset.x(), -redirectionOffset.y()); |
1783 | d->state->brushOrigin = QPointF(); |
1784 | |
1785 | // Slip a painter state into the engine before we do any other operations |
1786 | if (d->extended) |
1787 | d->extended->setState(d->state); |
1788 | else |
1789 | d->engine->state = d->state; |
1790 | |
1791 | switch (pd->devType()) { |
1792 | case QInternal::Pixmap: |
1793 | { |
1794 | QPixmap *pm = static_cast<QPixmap *>(pd); |
1795 | Q_ASSERT(pm); |
1796 | if (pm->isNull()) { |
1797 | qWarning("QPainter::begin: Cannot paint on a null pixmap" ); |
1798 | qt_cleanup_painter_state(d); |
1799 | return false; |
1800 | } |
1801 | |
1802 | if (pm->depth() == 1) { |
1803 | d->state->pen = QPen(Qt::color1); |
1804 | d->state->brush = QBrush(Qt::color0); |
1805 | } |
1806 | break; |
1807 | } |
1808 | case QInternal::Image: |
1809 | { |
1810 | QImage *img = static_cast<QImage *>(pd); |
1811 | Q_ASSERT(img); |
1812 | if (img->isNull()) { |
1813 | qWarning("QPainter::begin: Cannot paint on a null image" ); |
1814 | qt_cleanup_painter_state(d); |
1815 | return false; |
1816 | } else if (img->format() == QImage::Format_Indexed8) { |
1817 | // Painting on indexed8 images is not supported. |
1818 | qWarning("QPainter::begin: Cannot paint on an image with the QImage::Format_Indexed8 format" ); |
1819 | qt_cleanup_painter_state(d); |
1820 | return false; |
1821 | } |
1822 | if (img->depth() == 1) { |
1823 | d->state->pen = QPen(Qt::color1); |
1824 | d->state->brush = QBrush(Qt::color0); |
1825 | } |
1826 | break; |
1827 | } |
1828 | default: |
1829 | break; |
1830 | } |
1831 | if (d->state->ww == 0) // For compat with 3.x painter defaults |
1832 | d->state->ww = d->state->wh = d->state->vw = d->state->vh = 1024; |
1833 | |
1834 | d->engine->setPaintDevice(pd); |
1835 | |
1836 | bool begun = d->engine->begin(pd); |
1837 | if (!begun) { |
1838 | qWarning("QPainter::begin(): Returned false" ); |
1839 | if (d->engine->isActive()) { |
1840 | end(); |
1841 | } else { |
1842 | qt_cleanup_painter_state(d); |
1843 | } |
1844 | return false; |
1845 | } else { |
1846 | d->engine->setActive(begun); |
1847 | } |
1848 | |
1849 | // Copy painter properties from original paint device, |
1850 | // required for QPixmap::grabWidget() |
1851 | if (d->original_device->devType() == QInternal::Widget) { |
1852 | d->initFrom(d->original_device); |
1853 | } else { |
1854 | d->state->layoutDirection = Qt::LayoutDirectionAuto; |
1855 | // make sure we have a font compatible with the paintdevice |
1856 | d->state->deviceFont = d->state->font = QFont(d->state->deviceFont, device()); |
1857 | } |
1858 | |
1859 | QRect systemRect = d->engine->systemRect(); |
1860 | if (!systemRect.isEmpty()) { |
1861 | d->state->ww = d->state->vw = systemRect.width(); |
1862 | d->state->wh = d->state->vh = systemRect.height(); |
1863 | } else { |
1864 | d->state->ww = d->state->vw = pd->metric(QPaintDevice::PdmWidth); |
1865 | d->state->wh = d->state->vh = pd->metric(QPaintDevice::PdmHeight); |
1866 | } |
1867 | |
1868 | const QPoint coordinateOffset = d->engine->coordinateOffset(); |
1869 | d->state->redirectionMatrix.translate(-coordinateOffset.x(), -coordinateOffset.y()); |
1870 | |
1871 | Q_ASSERT(d->engine->isActive()); |
1872 | |
1873 | if (!d->state->redirectionMatrix.isIdentity() || d->effectiveDevicePixelRatio() > 1) |
1874 | d->updateMatrix(); |
1875 | |
1876 | Q_ASSERT(d->engine->isActive()); |
1877 | d->state->renderHints = QPainter::TextAntialiasing; |
1878 | ++d->device->painters; |
1879 | |
1880 | d->state->emulationSpecifier = 0; |
1881 | |
1882 | return true; |
1883 | } |
1884 | |
1885 | /*! |
1886 | Ends painting. Any resources used while painting are released. You |
1887 | don't normally need to call this since it is called by the |
1888 | destructor. |
1889 | |
1890 | Returns \c true if the painter is no longer active; otherwise returns \c false. |
1891 | |
1892 | \sa begin(), isActive() |
1893 | */ |
1894 | |
1895 | bool QPainter::end() |
1896 | { |
1897 | #ifdef QT_DEBUG_DRAW |
1898 | if (qt_show_painter_debug_output) |
1899 | printf("QPainter::end()\n" ); |
1900 | #endif |
1901 | Q_D(QPainter); |
1902 | |
1903 | if (!d->engine) { |
1904 | qWarning("QPainter::end: Painter not active, aborted" ); |
1905 | qt_cleanup_painter_state(d); |
1906 | return false; |
1907 | } |
1908 | |
1909 | if (d->refcount > 1) { |
1910 | d->detachPainterPrivate(this); |
1911 | return true; |
1912 | } |
1913 | |
1914 | bool ended = true; |
1915 | |
1916 | if (d->engine->isActive()) { |
1917 | ended = d->engine->end(); |
1918 | d->updateState(0); |
1919 | |
1920 | --d->device->painters; |
1921 | if (d->device->painters == 0) { |
1922 | d->engine->setPaintDevice(0); |
1923 | d->engine->setActive(false); |
1924 | } |
1925 | } |
1926 | |
1927 | if (d->states.size() > 1) { |
1928 | qWarning("QPainter::end: Painter ended with %d saved states" , |
1929 | d->states.size()); |
1930 | } |
1931 | |
1932 | if (d->engine->autoDestruct()) { |
1933 | delete d->engine; |
1934 | } |
1935 | |
1936 | if (d->emulationEngine) { |
1937 | delete d->emulationEngine; |
1938 | d->emulationEngine = 0; |
1939 | } |
1940 | |
1941 | if (d->extended) { |
1942 | d->extended = 0; |
1943 | } |
1944 | |
1945 | qt_cleanup_painter_state(d); |
1946 | |
1947 | return ended; |
1948 | } |
1949 | |
1950 | |
1951 | /*! |
1952 | Returns the paint engine that the painter is currently operating |
1953 | on if the painter is active; otherwise 0. |
1954 | |
1955 | \sa isActive() |
1956 | */ |
1957 | QPaintEngine *QPainter::paintEngine() const |
1958 | { |
1959 | Q_D(const QPainter); |
1960 | return d->engine; |
1961 | } |
1962 | |
1963 | /*! |
1964 | \since 4.6 |
1965 | |
1966 | Flushes the painting pipeline and prepares for the user issuing commands |
1967 | directly to the underlying graphics context. Must be followed by a call to |
1968 | endNativePainting(). |
1969 | |
1970 | Note that only the states the underlying paint engine changes will be reset |
1971 | to their respective default states. The states we reset may change from |
1972 | release to release. The following states are currently reset in the OpenGL |
1973 | 2 engine: |
1974 | |
1975 | \list |
1976 | \li blending is disabled |
1977 | \li the depth, stencil and scissor tests are disabled |
1978 | \li the active texture unit is reset to 0 |
1979 | \li the depth mask, depth function and the clear depth are reset to their |
1980 | default values |
1981 | \li the stencil mask, stencil operation and stencil function are reset to |
1982 | their default values |
1983 | \li the current color is reset to solid white |
1984 | \endlist |
1985 | |
1986 | If, for example, the OpenGL polygon mode is changed by the user inside a |
1987 | beginNativePaint()/endNativePainting() block, it will not be reset to the |
1988 | default state by endNativePainting(). Here is an example that shows |
1989 | intermixing of painter commands and raw OpenGL commands: |
1990 | |
1991 | \snippet code/src_gui_painting_qpainter.cpp 21 |
1992 | |
1993 | \sa endNativePainting() |
1994 | */ |
1995 | void QPainter::beginNativePainting() |
1996 | { |
1997 | Q_D(QPainter); |
1998 | if (!d->engine) { |
1999 | qWarning("QPainter::beginNativePainting: Painter not active" ); |
2000 | return; |
2001 | } |
2002 | |
2003 | if (d->extended) |
2004 | d->extended->beginNativePainting(); |
2005 | } |
2006 | |
2007 | /*! |
2008 | \since 4.6 |
2009 | |
2010 | Restores the painter after manually issuing native painting commands. Lets |
2011 | the painter restore any native state that it relies on before calling any |
2012 | other painter commands. |
2013 | |
2014 | \sa beginNativePainting() |
2015 | */ |
2016 | void QPainter::endNativePainting() |
2017 | { |
2018 | Q_D(const QPainter); |
2019 | if (!d->engine) { |
2020 | qWarning("QPainter::beginNativePainting: Painter not active" ); |
2021 | return; |
2022 | } |
2023 | |
2024 | if (d->extended) |
2025 | d->extended->endNativePainting(); |
2026 | else |
2027 | d->engine->syncState(); |
2028 | } |
2029 | |
2030 | /*! |
2031 | Returns the font metrics for the painter if the painter is |
2032 | active. Otherwise, the return value is undefined. |
2033 | |
2034 | \sa font(), isActive(), {QPainter#Settings}{Settings} |
2035 | */ |
2036 | |
2037 | QFontMetrics QPainter::fontMetrics() const |
2038 | { |
2039 | Q_D(const QPainter); |
2040 | if (!d->engine) { |
2041 | qWarning("QPainter::fontMetrics: Painter not active" ); |
2042 | return QFontMetrics(QFont()); |
2043 | } |
2044 | return QFontMetrics(d->state->font); |
2045 | } |
2046 | |
2047 | |
2048 | /*! |
2049 | Returns the font info for the painter if the painter is |
2050 | active. Otherwise, the return value is undefined. |
2051 | |
2052 | \sa font(), isActive(), {QPainter#Settings}{Settings} |
2053 | */ |
2054 | |
2055 | QFontInfo QPainter::fontInfo() const |
2056 | { |
2057 | Q_D(const QPainter); |
2058 | if (!d->engine) { |
2059 | qWarning("QPainter::fontInfo: Painter not active" ); |
2060 | return QFontInfo(QFont()); |
2061 | } |
2062 | return QFontInfo(d->state->font); |
2063 | } |
2064 | |
2065 | /*! |
2066 | \since 4.2 |
2067 | |
2068 | Returns the opacity of the painter. The default value is |
2069 | 1. |
2070 | */ |
2071 | |
2072 | qreal QPainter::opacity() const |
2073 | { |
2074 | Q_D(const QPainter); |
2075 | if (!d->engine) { |
2076 | qWarning("QPainter::opacity: Painter not active" ); |
2077 | return 1.0; |
2078 | } |
2079 | return d->state->opacity; |
2080 | } |
2081 | |
2082 | /*! |
2083 | \since 4.2 |
2084 | |
2085 | Sets the opacity of the painter to \a opacity. The value should |
2086 | be in the range 0.0 to 1.0, where 0.0 is fully transparent and |
2087 | 1.0 is fully opaque. |
2088 | |
2089 | Opacity set on the painter will apply to all drawing operations |
2090 | individually. |
2091 | */ |
2092 | |
2093 | void QPainter::setOpacity(qreal opacity) |
2094 | { |
2095 | Q_D(QPainter); |
2096 | |
2097 | if (!d->engine) { |
2098 | qWarning("QPainter::setOpacity: Painter not active" ); |
2099 | return; |
2100 | } |
2101 | |
2102 | opacity = qMin(qreal(1), qMax(qreal(0), opacity)); |
2103 | |
2104 | if (opacity == d->state->opacity) |
2105 | return; |
2106 | |
2107 | d->state->opacity = opacity; |
2108 | |
2109 | if (d->extended) |
2110 | d->extended->opacityChanged(); |
2111 | else |
2112 | d->state->dirtyFlags |= QPaintEngine::DirtyOpacity; |
2113 | } |
2114 | |
2115 | |
2116 | /*! |
2117 | Returns the currently set brush origin. |
2118 | |
2119 | \sa setBrushOrigin(), {QPainter#Settings}{Settings} |
2120 | */ |
2121 | |
2122 | QPoint QPainter::brushOrigin() const |
2123 | { |
2124 | Q_D(const QPainter); |
2125 | if (!d->engine) { |
2126 | qWarning("QPainter::brushOrigin: Painter not active" ); |
2127 | return QPoint(); |
2128 | } |
2129 | return QPointF(d->state->brushOrigin).toPoint(); |
2130 | } |
2131 | |
2132 | /*! |
2133 | \fn void QPainter::setBrushOrigin(const QPointF &position) |
2134 | |
2135 | Sets the brush origin to \a position. |
2136 | |
2137 | The brush origin specifies the (0, 0) coordinate of the painter's |
2138 | brush. |
2139 | |
2140 | Note that while the brushOrigin() was necessary to adopt the |
2141 | parent's background for a widget in Qt 3, this is no longer the |
2142 | case since the Qt 4 painter doesn't paint the background unless |
2143 | you explicitly tell it to do so by setting the widget's \l |
2144 | {QWidget::autoFillBackground}{autoFillBackground} property to |
2145 | true. |
2146 | |
2147 | \sa brushOrigin(), {QPainter#Settings}{Settings} |
2148 | */ |
2149 | |
2150 | void QPainter::setBrushOrigin(const QPointF &p) |
2151 | { |
2152 | Q_D(QPainter); |
2153 | #ifdef QT_DEBUG_DRAW |
2154 | if (qt_show_painter_debug_output) |
2155 | printf("QPainter::setBrushOrigin(), (%.2f,%.2f)\n" , p.x(), p.y()); |
2156 | #endif |
2157 | |
2158 | if (!d->engine) { |
2159 | qWarning("QPainter::setBrushOrigin: Painter not active" ); |
2160 | return; |
2161 | } |
2162 | |
2163 | d->state->brushOrigin = p; |
2164 | |
2165 | if (d->extended) { |
2166 | d->extended->brushOriginChanged(); |
2167 | return; |
2168 | } |
2169 | |
2170 | d->state->dirtyFlags |= QPaintEngine::DirtyBrushOrigin; |
2171 | } |
2172 | |
2173 | /*! |
2174 | \fn void QPainter::setBrushOrigin(const QPoint &position) |
2175 | \overload |
2176 | |
2177 | Sets the brush's origin to the given \a position. |
2178 | */ |
2179 | |
2180 | /*! |
2181 | \fn void QPainter::setBrushOrigin(int x, int y) |
2182 | |
2183 | \overload |
2184 | |
2185 | Sets the brush's origin to point (\a x, \a y). |
2186 | */ |
2187 | |
2188 | /*! |
2189 | \enum QPainter::CompositionMode |
2190 | |
2191 | Defines the modes supported for digital image compositing. |
2192 | Composition modes are used to specify how the pixels in one image, |
2193 | the source, are merged with the pixel in another image, the |
2194 | destination. |
2195 | |
2196 | Please note that the bitwise raster operation modes, denoted with |
2197 | a RasterOp prefix, are only natively supported in the X11 and |
2198 | raster paint engines. This means that the only way to utilize |
2199 | these modes on the Mac is via a QImage. The RasterOp denoted blend |
2200 | modes are \e not supported for pens and brushes with alpha |
2201 | components. Also, turning on the QPainter::Antialiasing render |
2202 | hint will effectively disable the RasterOp modes. |
2203 | |
2204 | |
2205 | \image qpainter-compositionmode1.png |
2206 | \image qpainter-compositionmode2.png |
2207 | |
2208 | The most common type is SourceOver (often referred to as just |
2209 | alpha blending) where the source pixel is blended on top of the |
2210 | destination pixel in such a way that the alpha component of the |
2211 | source defines the translucency of the pixel. |
2212 | |
2213 | Several composition modes require an alpha channel in the source or |
2214 | target images to have an effect. For optimal performance the |
2215 | image format \l {QImage::Format}{Format_ARGB32_Premultiplied} is |
2216 | preferred. |
2217 | |
2218 | When a composition mode is set it applies to all painting |
2219 | operator, pens, brushes, gradients and pixmap/image drawing. |
2220 | |
2221 | \value CompositionMode_SourceOver This is the default mode. The |
2222 | alpha of the source is used to blend the pixel on top of the |
2223 | destination. |
2224 | |
2225 | \value CompositionMode_DestinationOver The alpha of the |
2226 | destination is used to blend it on top of the source pixels. This |
2227 | mode is the inverse of CompositionMode_SourceOver. |
2228 | |
2229 | \value CompositionMode_Clear The pixels in the destination are |
2230 | cleared (set to fully transparent) independent of the source. |
2231 | |
2232 | \value CompositionMode_Source The output is the source |
2233 | pixel. (This means a basic copy operation and is identical to |
2234 | SourceOver when the source pixel is opaque). |
2235 | |
2236 | \value CompositionMode_Destination The output is the destination |
2237 | pixel. This means that the blending has no effect. This mode is |
2238 | the inverse of CompositionMode_Source. |
2239 | |
2240 | \value CompositionMode_SourceIn The output is the source, where |
2241 | the alpha is reduced by that of the destination. |
2242 | |
2243 | \value CompositionMode_DestinationIn The output is the |
2244 | destination, where the alpha is reduced by that of the |
2245 | source. This mode is the inverse of CompositionMode_SourceIn. |
2246 | |
2247 | \value CompositionMode_SourceOut The output is the source, where |
2248 | the alpha is reduced by the inverse of destination. |
2249 | |
2250 | \value CompositionMode_DestinationOut The output is the |
2251 | destination, where the alpha is reduced by the inverse of the |
2252 | source. This mode is the inverse of CompositionMode_SourceOut. |
2253 | |
2254 | \value CompositionMode_SourceAtop The source pixel is blended on |
2255 | top of the destination, with the alpha of the source pixel reduced |
2256 | by the alpha of the destination pixel. |
2257 | |
2258 | \value CompositionMode_DestinationAtop The destination pixel is |
2259 | blended on top of the source, with the alpha of the destination |
2260 | pixel is reduced by the alpha of the destination pixel. This mode |
2261 | is the inverse of CompositionMode_SourceAtop. |
2262 | |
2263 | \value CompositionMode_Xor The source, whose alpha is reduced with |
2264 | the inverse of the destination alpha, is merged with the |
2265 | destination, whose alpha is reduced by the inverse of the source |
2266 | alpha. CompositionMode_Xor is not the same as the bitwise Xor. |
2267 | |
2268 | \value CompositionMode_Plus Both the alpha and color of the source |
2269 | and destination pixels are added together. |
2270 | |
2271 | \value CompositionMode_Multiply The output is the source color |
2272 | multiplied by the destination. Multiplying a color with white |
2273 | leaves the color unchanged, while multiplying a color |
2274 | with black produces black. |
2275 | |
2276 | \value CompositionMode_Screen The source and destination colors |
2277 | are inverted and then multiplied. Screening a color with white |
2278 | produces white, whereas screening a color with black leaves the |
2279 | color unchanged. |
2280 | |
2281 | \value CompositionMode_Overlay Multiplies or screens the colors |
2282 | depending on the destination color. The destination color is mixed |
2283 | with the source color to reflect the lightness or darkness of the |
2284 | destination. |
2285 | |
2286 | \value CompositionMode_Darken The darker of the source and |
2287 | destination colors is selected. |
2288 | |
2289 | \value CompositionMode_Lighten The lighter of the source and |
2290 | destination colors is selected. |
2291 | |
2292 | \value CompositionMode_ColorDodge The destination color is |
2293 | brightened to reflect the source color. A black source color |
2294 | leaves the destination color unchanged. |
2295 | |
2296 | \value CompositionMode_ColorBurn The destination color is darkened |
2297 | to reflect the source color. A white source color leaves the |
2298 | destination color unchanged. |
2299 | |
2300 | \value CompositionMode_HardLight Multiplies or screens the colors |
2301 | depending on the source color. A light source color will lighten |
2302 | the destination color, whereas a dark source color will darken the |
2303 | destination color. |
2304 | |
2305 | \value CompositionMode_SoftLight Darkens or lightens the colors |
2306 | depending on the source color. Similar to |
2307 | CompositionMode_HardLight. |
2308 | |
2309 | \value CompositionMode_Difference Subtracts the darker of the |
2310 | colors from the lighter. Painting with white inverts the |
2311 | destination color, whereas painting with black leaves the |
2312 | destination color unchanged. |
2313 | |
2314 | \value CompositionMode_Exclusion Similar to |
2315 | CompositionMode_Difference, but with a lower contrast. Painting |
2316 | with white inverts the destination color, whereas painting with |
2317 | black leaves the destination color unchanged. |
2318 | |
2319 | \value RasterOp_SourceOrDestination Does a bitwise OR operation on |
2320 | the source and destination pixels (src OR dst). |
2321 | |
2322 | \value RasterOp_SourceAndDestination Does a bitwise AND operation |
2323 | on the source and destination pixels (src AND dst). |
2324 | |
2325 | \value RasterOp_SourceXorDestination Does a bitwise XOR operation |
2326 | on the source and destination pixels (src XOR dst). |
2327 | |
2328 | \value RasterOp_NotSourceAndNotDestination Does a bitwise NOR |
2329 | operation on the source and destination pixels ((NOT src) AND (NOT |
2330 | dst)). |
2331 | |
2332 | \value RasterOp_NotSourceOrNotDestination Does a bitwise NAND |
2333 | operation on the source and destination pixels ((NOT src) OR (NOT |
2334 | dst)). |
2335 | |
2336 | \value RasterOp_NotSourceXorDestination Does a bitwise operation |
2337 | where the source pixels are inverted and then XOR'ed with the |
2338 | destination ((NOT src) XOR dst). |
2339 | |
2340 | \value RasterOp_NotSource Does a bitwise operation where the |
2341 | source pixels are inverted (NOT src). |
2342 | |
2343 | \value RasterOp_NotSourceAndDestination Does a bitwise operation |
2344 | where the source is inverted and then AND'ed with the destination |
2345 | ((NOT src) AND dst). |
2346 | |
2347 | \value RasterOp_SourceAndNotDestination Does a bitwise operation |
2348 | where the source is AND'ed with the inverted destination pixels |
2349 | (src AND (NOT dst)). |
2350 | |
2351 | \value RasterOp_NotSourceOrDestination Does a bitwise operation |
2352 | where the source is inverted and then OR'ed with the destination |
2353 | ((NOT src) OR dst). |
2354 | |
2355 | \value RasterOp_ClearDestination The pixels in the destination are |
2356 | cleared (set to 0) independent of the source. |
2357 | |
2358 | \value RasterOp_SetDestination The pixels in the destination are |
2359 | set (set to 1) independent of the source. |
2360 | |
2361 | \value RasterOp_NotDestination Does a bitwise operation |
2362 | where the destination pixels are inverted (NOT dst). |
2363 | |
2364 | \value RasterOp_SourceOrNotDestination Does a bitwise operation |
2365 | where the source is OR'ed with the inverted destination pixels |
2366 | (src OR (NOT dst)). |
2367 | |
2368 | \sa compositionMode(), setCompositionMode(), {QPainter#Composition |
2369 | Modes}{Composition Modes}, {Image Composition Example} |
2370 | */ |
2371 | |
2372 | /*! |
2373 | Sets the composition mode to the given \a mode. |
2374 | |
2375 | \warning Only a QPainter operating on a QImage fully supports all |
2376 | composition modes. The RasterOp modes are supported for X11 as |
2377 | described in compositionMode(). |
2378 | |
2379 | \sa compositionMode() |
2380 | */ |
2381 | void QPainter::setCompositionMode(CompositionMode mode) |
2382 | { |
2383 | Q_D(QPainter); |
2384 | if (!d->engine) { |
2385 | qWarning("QPainter::setCompositionMode: Painter not active" ); |
2386 | return; |
2387 | } |
2388 | if (d->state->composition_mode == mode) |
2389 | return; |
2390 | if (d->extended) { |
2391 | d->state->composition_mode = mode; |
2392 | d->extended->compositionModeChanged(); |
2393 | return; |
2394 | } |
2395 | |
2396 | if (mode >= QPainter::RasterOp_SourceOrDestination) { |
2397 | if (!d->engine->hasFeature(QPaintEngine::RasterOpModes)) { |
2398 | qWarning("QPainter::setCompositionMode: " |
2399 | "Raster operation modes not supported on device" ); |
2400 | return; |
2401 | } |
2402 | } else if (mode >= QPainter::CompositionMode_Plus) { |
2403 | if (!d->engine->hasFeature(QPaintEngine::BlendModes)) { |
2404 | qWarning("QPainter::setCompositionMode: " |
2405 | "Blend modes not supported on device" ); |
2406 | return; |
2407 | } |
2408 | } else if (!d->engine->hasFeature(QPaintEngine::PorterDuff)) { |
2409 | if (mode != CompositionMode_Source && mode != CompositionMode_SourceOver) { |
2410 | qWarning("QPainter::setCompositionMode: " |
2411 | "PorterDuff modes not supported on device" ); |
2412 | return; |
2413 | } |
2414 | } |
2415 | |
2416 | d->state->composition_mode = mode; |
2417 | d->state->dirtyFlags |= QPaintEngine::DirtyCompositionMode; |
2418 | } |
2419 | |
2420 | /*! |
2421 | Returns the current composition mode. |
2422 | |
2423 | \sa CompositionMode, setCompositionMode() |
2424 | */ |
2425 | QPainter::CompositionMode QPainter::compositionMode() const |
2426 | { |
2427 | Q_D(const QPainter); |
2428 | if (!d->engine) { |
2429 | qWarning("QPainter::compositionMode: Painter not active" ); |
2430 | return QPainter::CompositionMode_SourceOver; |
2431 | } |
2432 | return d->state->composition_mode; |
2433 | } |
2434 | |
2435 | /*! |
2436 | Returns the current background brush. |
2437 | |
2438 | \sa setBackground(), {QPainter#Settings}{Settings} |
2439 | */ |
2440 | |
2441 | const QBrush &QPainter::background() const |
2442 | { |
2443 | Q_D(const QPainter); |
2444 | if (!d->engine) { |
2445 | qWarning("QPainter::background: Painter not active" ); |
2446 | return d->fakeState()->brush; |
2447 | } |
2448 | return d->state->bgBrush; |
2449 | } |
2450 | |
2451 | |
2452 | /*! |
2453 | Returns \c true if clipping has been set; otherwise returns \c false. |
2454 | |
2455 | \sa setClipping(), {QPainter#Clipping}{Clipping} |
2456 | */ |
2457 | |
2458 | bool QPainter::hasClipping() const |
2459 | { |
2460 | Q_D(const QPainter); |
2461 | if (!d->engine) { |
2462 | qWarning("QPainter::hasClipping: Painter not active" ); |
2463 | return false; |
2464 | } |
2465 | return d->state->clipEnabled && d->state->clipOperation != Qt::NoClip; |
2466 | } |
2467 | |
2468 | |
2469 | /*! |
2470 | Enables clipping if \a enable is true, or disables clipping if \a |
2471 | enable is false. |
2472 | |
2473 | \sa hasClipping(), {QPainter#Clipping}{Clipping} |
2474 | */ |
2475 | |
2476 | void QPainter::setClipping(bool enable) |
2477 | { |
2478 | Q_D(QPainter); |
2479 | #ifdef QT_DEBUG_DRAW |
2480 | if (qt_show_painter_debug_output) |
2481 | printf("QPainter::setClipping(), enable=%s, was=%s\n" , |
2482 | enable ? "on" : "off" , |
2483 | hasClipping() ? "on" : "off" ); |
2484 | #endif |
2485 | if (!d->engine) { |
2486 | qWarning("QPainter::setClipping: Painter not active, state will be reset by begin" ); |
2487 | return; |
2488 | } |
2489 | |
2490 | if (hasClipping() == enable) |
2491 | return; |
2492 | |
2493 | // we can't enable clipping if we don't have a clip |
2494 | if (enable |
2495 | && (d->state->clipInfo.isEmpty() || d->state->clipInfo.constLast().operation == Qt::NoClip)) |
2496 | return; |
2497 | d->state->clipEnabled = enable; |
2498 | |
2499 | if (d->extended) { |
2500 | d->extended->clipEnabledChanged(); |
2501 | return; |
2502 | } |
2503 | |
2504 | d->state->dirtyFlags |= QPaintEngine::DirtyClipEnabled; |
2505 | d->updateState(d->state); |
2506 | } |
2507 | |
2508 | |
2509 | /*! |
2510 | Returns the currently set clip region. Note that the clip region |
2511 | is given in logical coordinates. |
2512 | |
2513 | \warning QPainter does not store the combined clip explicitly as |
2514 | this is handled by the underlying QPaintEngine, so the path is |
2515 | recreated on demand and transformed to the current logical |
2516 | coordinate system. This is potentially an expensive operation. |
2517 | |
2518 | \sa setClipRegion(), clipPath(), setClipping() |
2519 | */ |
2520 | |
2521 | QRegion QPainter::clipRegion() const |
2522 | { |
2523 | Q_D(const QPainter); |
2524 | if (!d->engine) { |
2525 | qWarning("QPainter::clipRegion: Painter not active" ); |
2526 | return QRegion(); |
2527 | } |
2528 | |
2529 | QRegion region; |
2530 | bool lastWasNothing = true; |
2531 | |
2532 | if (!d->txinv) |
2533 | const_cast<QPainter *>(this)->d_ptr->updateInvMatrix(); |
2534 | |
2535 | // ### Falcon: Use QPainterPath |
2536 | for (const QPainterClipInfo &info : qAsConst(d->state->clipInfo)) { |
2537 | switch (info.clipType) { |
2538 | |
2539 | case QPainterClipInfo::RegionClip: { |
2540 | QTransform matrix = (info.matrix * d->invMatrix); |
2541 | if (lastWasNothing) { |
2542 | region = info.region * matrix; |
2543 | lastWasNothing = false; |
2544 | continue; |
2545 | } |
2546 | if (info.operation == Qt::IntersectClip) |
2547 | region &= info.region * matrix; |
2548 | else if (info.operation == Qt::NoClip) { |
2549 | lastWasNothing = true; |
2550 | region = QRegion(); |
2551 | } else |
2552 | region = info.region * matrix; |
2553 | break; |
2554 | } |
2555 | |
2556 | case QPainterClipInfo::PathClip: { |
2557 | QTransform matrix = (info.matrix * d->invMatrix); |
2558 | if (lastWasNothing) { |
2559 | region = QRegion((info.path * matrix).toFillPolygon().toPolygon(), |
2560 | info.path.fillRule()); |
2561 | lastWasNothing = false; |
2562 | continue; |
2563 | } |
2564 | if (info.operation == Qt::IntersectClip) { |
2565 | region &= QRegion((info.path * matrix).toFillPolygon().toPolygon(), |
2566 | info.path.fillRule()); |
2567 | } else if (info.operation == Qt::NoClip) { |
2568 | lastWasNothing = true; |
2569 | region = QRegion(); |
2570 | } else { |
2571 | region = QRegion((info.path * matrix).toFillPolygon().toPolygon(), |
2572 | info.path.fillRule()); |
2573 | } |
2574 | break; |
2575 | } |
2576 | |
2577 | case QPainterClipInfo::RectClip: { |
2578 | QTransform matrix = (info.matrix * d->invMatrix); |
2579 | if (lastWasNothing) { |
2580 | region = QRegion(info.rect) * matrix; |
2581 | lastWasNothing = false; |
2582 | continue; |
2583 | } |
2584 | if (info.operation == Qt::IntersectClip) { |
2585 | // Use rect intersection if possible. |
2586 | if (matrix.type() <= QTransform::TxScale) |
2587 | region &= matrix.mapRect(info.rect); |
2588 | else |
2589 | region &= matrix.map(QRegion(info.rect)); |
2590 | } else if (info.operation == Qt::NoClip) { |
2591 | lastWasNothing = true; |
2592 | region = QRegion(); |
2593 | } else { |
2594 | region = QRegion(info.rect) * matrix; |
2595 | } |
2596 | break; |
2597 | } |
2598 | |
2599 | case QPainterClipInfo::RectFClip: { |
2600 | QTransform matrix = (info.matrix * d->invMatrix); |
2601 | if (lastWasNothing) { |
2602 | region = QRegion(info.rectf.toRect()) * matrix; |
2603 | lastWasNothing = false; |
2604 | continue; |
2605 | } |
2606 | if (info.operation == Qt::IntersectClip) { |
2607 | // Use rect intersection if possible. |
2608 | if (matrix.type() <= QTransform::TxScale) |
2609 | region &= matrix.mapRect(info.rectf.toRect()); |
2610 | else |
2611 | region &= matrix.map(QRegion(info.rectf.toRect())); |
2612 | } else if (info.operation == Qt::NoClip) { |
2613 | lastWasNothing = true; |
2614 | region = QRegion(); |
2615 | } else { |
2616 | region = QRegion(info.rectf.toRect()) * matrix; |
2617 | } |
2618 | break; |
2619 | } |
2620 | } |
2621 | } |
2622 | |
2623 | return region; |
2624 | } |
2625 | |
2626 | extern QPainterPath qt_regionToPath(const QRegion ®ion); |
2627 | |
2628 | /*! |
2629 | Returns the current clip path in logical coordinates. |
2630 | |
2631 | \warning QPainter does not store the combined clip explicitly as |
2632 | this is handled by the underlying QPaintEngine, so the path is |
2633 | recreated on demand and transformed to the current logical |
2634 | coordinate system. This is potentially an expensive operation. |
2635 | |
2636 | \sa setClipPath(), clipRegion(), setClipping() |
2637 | */ |
2638 | QPainterPath QPainter::clipPath() const |
2639 | { |
2640 | Q_D(const QPainter); |
2641 | |
2642 | // ### Since we do not support path intersections and path unions yet, |
2643 | // we just use clipRegion() here... |
2644 | if (!d->engine) { |
2645 | qWarning("QPainter::clipPath: Painter not active" ); |
2646 | return QPainterPath(); |
2647 | } |
2648 | |
2649 | // No clip, return empty |
2650 | if (d->state->clipInfo.isEmpty()) { |
2651 | return QPainterPath(); |
2652 | } else { |
2653 | |
2654 | // Update inverse matrix, used below. |
2655 | if (!d->txinv) |
2656 | const_cast<QPainter *>(this)->d_ptr->updateInvMatrix(); |
2657 | |
2658 | // For the simple case avoid conversion. |
2659 | if (d->state->clipInfo.size() == 1 |
2660 | && d->state->clipInfo.at(0).clipType == QPainterClipInfo::PathClip) { |
2661 | QTransform matrix = (d->state->clipInfo.at(0).matrix * d->invMatrix); |
2662 | return d->state->clipInfo.at(0).path * matrix; |
2663 | |
2664 | } else if (d->state->clipInfo.size() == 1 |
2665 | && d->state->clipInfo.at(0).clipType == QPainterClipInfo::RectClip) { |
2666 | QTransform matrix = (d->state->clipInfo.at(0).matrix * d->invMatrix); |
2667 | QPainterPath path; |
2668 | path.addRect(d->state->clipInfo.at(0).rect); |
2669 | return path * matrix; |
2670 | } else { |
2671 | // Fallback to clipRegion() for now, since we don't have isect/unite for paths |
2672 | return qt_regionToPath(clipRegion()); |
2673 | } |
2674 | } |
2675 | } |
2676 | |
2677 | /*! |
2678 | Returns the bounding rectangle of the current clip if there is a clip; |
2679 | otherwise returns an empty rectangle. Note that the clip region is |
2680 | given in logical coordinates. |
2681 | |
2682 | The bounding rectangle is not guaranteed to be tight. |
2683 | |
2684 | \sa setClipRect(), setClipPath(), setClipRegion() |
2685 | |
2686 | \since 4.8 |
2687 | */ |
2688 | |
2689 | QRectF QPainter::clipBoundingRect() const |
2690 | { |
2691 | Q_D(const QPainter); |
2692 | |
2693 | if (!d->engine) { |
2694 | qWarning("QPainter::clipBoundingRect: Painter not active" ); |
2695 | return QRectF(); |
2696 | } |
2697 | |
2698 | // Accumulate the bounding box in device space. This is not 100% |
2699 | // precise, but it fits within the guarantee and it is reasonably |
2700 | // fast. |
2701 | QRectF bounds; |
2702 | bool first = true; |
2703 | for (const QPainterClipInfo &info : qAsConst(d->state->clipInfo)) { |
2704 | QRectF r; |
2705 | |
2706 | if (info.clipType == QPainterClipInfo::RectClip) |
2707 | r = info.rect; |
2708 | else if (info.clipType == QPainterClipInfo::RectFClip) |
2709 | r = info.rectf; |
2710 | else if (info.clipType == QPainterClipInfo::RegionClip) |
2711 | r = info.region.boundingRect(); |
2712 | else |
2713 | r = info.path.boundingRect(); |
2714 | |
2715 | r = info.matrix.mapRect(r); |
2716 | |
2717 | if (first) |
2718 | bounds = r; |
2719 | else if (info.operation == Qt::IntersectClip) |
2720 | bounds &= r; |
2721 | first = false; |
2722 | } |
2723 | |
2724 | |
2725 | // Map the rectangle back into logical space using the inverse |
2726 | // matrix. |
2727 | if (!d->txinv) |
2728 | const_cast<QPainter *>(this)->d_ptr->updateInvMatrix(); |
2729 | |
2730 | return d->invMatrix.mapRect(bounds); |
2731 | } |
2732 | |
2733 | /*! |
2734 | \fn void QPainter::setClipRect(const QRectF &rectangle, Qt::ClipOperation operation) |
2735 | |
2736 | Enables clipping, and sets the clip region to the given \a |
2737 | rectangle using the given clip \a operation. The default operation |
2738 | is to replace the current clip rectangle. |
2739 | |
2740 | Note that the clip rectangle is specified in logical (painter) |
2741 | coordinates. |
2742 | |
2743 | \sa clipRegion(), setClipping(), {QPainter#Clipping}{Clipping} |
2744 | */ |
2745 | void QPainter::setClipRect(const QRectF &rect, Qt::ClipOperation op) |
2746 | { |
2747 | Q_D(QPainter); |
2748 | |
2749 | if (d->extended) { |
2750 | if (!d->engine) { |
2751 | qWarning("QPainter::setClipRect: Painter not active" ); |
2752 | return; |
2753 | } |
2754 | bool simplifyClipOp = (paintEngine()->type() != QPaintEngine::Picture); |
2755 | if (simplifyClipOp && (!d->state->clipEnabled && op != Qt::NoClip)) |
2756 | op = Qt::ReplaceClip; |
2757 | |
2758 | qreal right = rect.x() + rect.width(); |
2759 | qreal bottom = rect.y() + rect.height(); |
2760 | qreal pts[] = { rect.x(), rect.y(), |
2761 | right, rect.y(), |
2762 | right, bottom, |
2763 | rect.x(), bottom }; |
2764 | QVectorPath vp(pts, 4, 0, QVectorPath::RectangleHint); |
2765 | d->state->clipEnabled = true; |
2766 | d->extended->clip(vp, op); |
2767 | if (op == Qt::ReplaceClip || op == Qt::NoClip) |
2768 | d->state->clipInfo.clear(); |
2769 | d->state->clipInfo.append(QPainterClipInfo(rect, op, d->state->matrix)); |
2770 | d->state->clipOperation = op; |
2771 | return; |
2772 | } |
2773 | |
2774 | if (qreal(int(rect.top())) == rect.top() |
2775 | && qreal(int(rect.bottom())) == rect.bottom() |
2776 | && qreal(int(rect.left())) == rect.left() |
2777 | && qreal(int(rect.right())) == rect.right()) |
2778 | { |
2779 | setClipRect(rect.toRect(), op); |
2780 | return; |
2781 | } |
2782 | |
2783 | if (rect.isEmpty()) { |
2784 | setClipRegion(QRegion(), op); |
2785 | return; |
2786 | } |
2787 | |
2788 | QPainterPath path; |
2789 | path.addRect(rect); |
2790 | setClipPath(path, op); |
2791 | } |
2792 | |
2793 | /*! |
2794 | \fn void QPainter::setClipRect(const QRect &rectangle, Qt::ClipOperation operation) |
2795 | \overload |
2796 | |
2797 | Enables clipping, and sets the clip region to the given \a rectangle using the given |
2798 | clip \a operation. |
2799 | */ |
2800 | void QPainter::setClipRect(const QRect &rect, Qt::ClipOperation op) |
2801 | { |
2802 | Q_D(QPainter); |
2803 | |
2804 | if (!d->engine) { |
2805 | qWarning("QPainter::setClipRect: Painter not active" ); |
2806 | return; |
2807 | } |
2808 | bool simplifyClipOp = (paintEngine()->type() != QPaintEngine::Picture); |
2809 | |
2810 | if (simplifyClipOp && (!d->state->clipEnabled && op != Qt::NoClip)) |
2811 | op = Qt::ReplaceClip; |
2812 | |
2813 | if (d->extended) { |
2814 | d->state->clipEnabled = true; |
2815 | d->extended->clip(rect, op); |
2816 | if (op == Qt::ReplaceClip || op == Qt::NoClip) |
2817 | d->state->clipInfo.clear(); |
2818 | d->state->clipInfo.append(QPainterClipInfo(rect, op, d->state->matrix)); |
2819 | d->state->clipOperation = op; |
2820 | return; |
2821 | } |
2822 | |
2823 | if (simplifyClipOp && d->state->clipOperation == Qt::NoClip && op == Qt::IntersectClip) |
2824 | op = Qt::ReplaceClip; |
2825 | |
2826 | d->state->clipRegion = rect; |
2827 | d->state->clipOperation = op; |
2828 | if (op == Qt::NoClip || op == Qt::ReplaceClip) |
2829 | d->state->clipInfo.clear(); |
2830 | d->state->clipInfo.append(QPainterClipInfo(rect, op, d->state->matrix)); |
2831 | d->state->clipEnabled = true; |
2832 | d->state->dirtyFlags |= QPaintEngine::DirtyClipRegion | QPaintEngine::DirtyClipEnabled; |
2833 | d->updateState(d->state); |
2834 | } |
2835 | |
2836 | /*! |
2837 | \fn void QPainter::setClipRect(int x, int y, int width, int height, Qt::ClipOperation operation) |
2838 | |
2839 | Enables clipping, and sets the clip region to the rectangle beginning at (\a x, \a y) |
2840 | with the given \a width and \a height. |
2841 | */ |
2842 | |
2843 | /*! |
2844 | \fn void QPainter::setClipRegion(const QRegion ®ion, Qt::ClipOperation operation) |
2845 | |
2846 | Sets the clip region to the given \a region using the specified clip |
2847 | \a operation. The default clip operation is to replace the current |
2848 | clip region. |
2849 | |
2850 | Note that the clip region is given in logical coordinates. |
2851 | |
2852 | \sa clipRegion(), setClipRect(), {QPainter#Clipping}{Clipping} |
2853 | */ |
2854 | void QPainter::setClipRegion(const QRegion &r, Qt::ClipOperation op) |
2855 | { |
2856 | Q_D(QPainter); |
2857 | #ifdef QT_DEBUG_DRAW |
2858 | QRect rect = r.boundingRect(); |
2859 | if (qt_show_painter_debug_output) |
2860 | printf("QPainter::setClipRegion(), size=%d, [%d,%d,%d,%d]\n" , |
2861 | r.rectCount(), rect.x(), rect.y(), rect.width(), rect.height()); |
2862 | #endif |
2863 | if (!d->engine) { |
2864 | qWarning("QPainter::setClipRegion: Painter not active" ); |
2865 | return; |
2866 | } |
2867 | bool simplifyClipOp = (paintEngine()->type() != QPaintEngine::Picture); |
2868 | |
2869 | if (simplifyClipOp && (!d->state->clipEnabled && op != Qt::NoClip)) |
2870 | op = Qt::ReplaceClip; |
2871 | |
2872 | if (d->extended) { |
2873 | d->state->clipEnabled = true; |
2874 | d->extended->clip(r, op); |
2875 | if (op == Qt::NoClip || op == Qt::ReplaceClip) |
2876 | d->state->clipInfo.clear(); |
2877 | d->state->clipInfo.append(QPainterClipInfo(r, op, d->state->matrix)); |
2878 | d->state->clipOperation = op; |
2879 | return; |
2880 | } |
2881 | |
2882 | if (simplifyClipOp && d->state->clipOperation == Qt::NoClip && op == Qt::IntersectClip) |
2883 | op = Qt::ReplaceClip; |
2884 | |
2885 | d->state->clipRegion = r; |
2886 | d->state->clipOperation = op; |
2887 | if (op == Qt::NoClip || op == Qt::ReplaceClip) |
2888 | d->state->clipInfo.clear(); |
2889 | d->state->clipInfo.append(QPainterClipInfo(r, op, d->state->matrix)); |
2890 | d->state->clipEnabled = true; |
2891 | d->state->dirtyFlags |= QPaintEngine::DirtyClipRegion | QPaintEngine::DirtyClipEnabled; |
2892 | d->updateState(d->state); |
2893 | } |
2894 | |
2895 | #if QT_DEPRECATED_SINCE(5, 13) |
2896 | /*! |
2897 | \since 4.2 |
2898 | \obsolete |
2899 | |
2900 | Sets the transformation matrix to \a matrix and enables transformations. |
2901 | |
2902 | \note It is advisable to use setWorldTransform() instead of this function to |
2903 | preserve the properties of perspective transformations. |
2904 | |
2905 | If \a combine is true, then \a matrix is combined with the current |
2906 | transformation matrix; otherwise \a matrix replaces the current |
2907 | transformation matrix. |
2908 | |
2909 | If \a matrix is the identity matrix and \a combine is false, this |
2910 | function calls setWorldMatrixEnabled(false). (The identity matrix is the |
2911 | matrix where QMatrix::m11() and QMatrix::m22() are 1.0 and the |
2912 | rest are 0.0.) |
2913 | |
2914 | The following functions can transform the coordinate system without using |
2915 | a QMatrix: |
2916 | \list |
2917 | \li translate() |
2918 | \li scale() |
2919 | \li shear() |
2920 | \li rotate() |
2921 | \endlist |
2922 | |
2923 | They operate on the painter's worldMatrix() and are implemented like this: |
2924 | |
2925 | \snippet code/src_gui_painting_qpainter.cpp 4 |
2926 | |
2927 | Note that when using setWorldMatrix() function you should always have |
2928 | \a combine be true when you are drawing into a QPicture. Otherwise |
2929 | it may not be possible to replay the picture with additional |
2930 | transformations; using the translate(), scale(), etc. convenience |
2931 | functions is safe. |
2932 | |
2933 | For more information about the coordinate system, transformations |
2934 | and window-viewport conversion, see \l {Coordinate System}. |
2935 | |
2936 | \sa setWorldTransform(), QTransform |
2937 | */ |
2938 | |
2939 | void QPainter::setWorldMatrix(const QMatrix &matrix, bool combine) |
2940 | { |
2941 | setWorldTransform(QTransform(matrix), combine); |
2942 | } |
2943 | |
2944 | /*! |
2945 | \since 4.2 |
2946 | \obsolete |
2947 | |
2948 | Returns the world transformation matrix. |
2949 | |
2950 | It is advisable to use worldTransform() because worldMatrix() does not |
2951 | preserve the properties of perspective transformations. |
2952 | |
2953 | \sa {QPainter#Coordinate Transformations}{Coordinate Transformations}, |
2954 | {Coordinate System} |
2955 | */ |
2956 | |
2957 | const QMatrix &QPainter::worldMatrix() const |
2958 | { |
2959 | Q_D(const QPainter); |
2960 | if (!d->engine) { |
2961 | qWarning("QPainter::worldMatrix: Painter not active" ); |
2962 | return d->fakeState()->transform.toAffine(); |
2963 | } |
2964 | return d->state->worldMatrix.toAffine(); |
2965 | } |
2966 | |
2967 | /*! |
2968 | \obsolete |
2969 | |
2970 | Use setWorldTransform() instead. |
2971 | |
2972 | \sa setWorldTransform() |
2973 | */ |
2974 | |
2975 | void QPainter::setMatrix(const QMatrix &matrix, bool combine) |
2976 | { |
2977 | setWorldTransform(QTransform(matrix), combine); |
2978 | } |
2979 | |
2980 | /*! |
2981 | \obsolete |
2982 | |
2983 | Use worldTransform() instead. |
2984 | |
2985 | \sa worldTransform() |
2986 | */ |
2987 | |
2988 | const QMatrix &QPainter::matrix() const |
2989 | { |
2990 | QT_WARNING_PUSH |
2991 | QT_WARNING_DISABLE_DEPRECATED |
2992 | return worldMatrix(); |
2993 | QT_WARNING_POP |
2994 | } |
2995 | |
2996 | |
2997 | /*! |
2998 | \since 4.2 |
2999 | \obsolete |
3000 | |
3001 | Returns the transformation matrix combining the current |
3002 | window/viewport and world transformation. |
3003 | |
3004 | It is advisable to use combinedTransform() instead of this |
3005 | function to preserve the properties of perspective transformations. |
3006 | |
3007 | \sa setWorldTransform(), setWindow(), setViewport() |
3008 | */ |
3009 | QMatrix QPainter::combinedMatrix() const |
3010 | { |
3011 | return combinedTransform().toAffine(); |
3012 | } |
3013 | |
3014 | |
3015 | /*! |
3016 | \obsolete |
3017 | |
3018 | Returns the matrix that transforms from logical coordinates to |
3019 | device coordinates of the platform dependent paint device. |
3020 | |
3021 | \note It is advisable to use deviceTransform() instead of this |
3022 | function to preserve the properties of perspective transformations. |
3023 | |
3024 | This function is \e only needed when using platform painting |
3025 | commands on the platform dependent handle (Qt::HANDLE), and the |
3026 | platform does not do transformations nativly. |
3027 | |
3028 | The QPaintEngine::PaintEngineFeature enum can be queried to |
3029 | determine whether the platform performs the transformations or |
3030 | not. |
3031 | |
3032 | \sa worldMatrix(), QPaintEngine::hasFeature(), |
3033 | */ |
3034 | const QMatrix &QPainter::deviceMatrix() const |
3035 | { |
3036 | Q_D(const QPainter); |
3037 | if (!d->engine) { |
3038 | qWarning("QPainter::deviceMatrix: Painter not active" ); |
3039 | return d->fakeState()->transform.toAffine(); |
3040 | } |
3041 | return d->state->matrix.toAffine(); |
3042 | } |
3043 | |
3044 | /*! |
3045 | \obsolete |
3046 | |
3047 | Resets any transformations that were made using translate(), scale(), |
3048 | shear(), rotate(), setWorldMatrix(), setViewport() and |
3049 | setWindow(). |
3050 | |
3051 | It is advisable to use resetTransform() instead of this function |
3052 | to preserve the properties of perspective transformations. |
3053 | |
3054 | \sa {QPainter#Coordinate Transformations}{Coordinate |
3055 | Transformations} |
3056 | */ |
3057 | |
3058 | void QPainter::resetMatrix() |
3059 | { |
3060 | resetTransform(); |
3061 | } |
3062 | #endif |
3063 | |
3064 | /*! |
3065 | \since 4.2 |
3066 | |
3067 | Enables transformations if \a enable is true, or disables |
3068 | transformations if \a enable is false. The world transformation |
3069 | matrix is not changed. |
3070 | |
3071 | \sa worldMatrixEnabled(), worldTransform(), {QPainter#Coordinate |
3072 | Transformations}{Coordinate Transformations} |
3073 | */ |
3074 | |
3075 | void QPainter::setWorldMatrixEnabled(bool enable) |
3076 | { |
3077 | Q_D(QPainter); |
3078 | #ifdef QT_DEBUG_DRAW |
3079 | if (qt_show_painter_debug_output) |
3080 | printf("QPainter::setMatrixEnabled(), enable=%d\n" , enable); |
3081 | #endif |
3082 | |
3083 | if (!d->engine) { |
3084 | qWarning("QPainter::setMatrixEnabled: Painter not active" ); |
3085 | return; |
3086 | } |
3087 | if (enable == d->state->WxF) |
3088 | return; |
3089 | |
3090 | d->state->WxF = enable; |
3091 | d->updateMatrix(); |
3092 | } |
3093 | |
3094 | /*! |
3095 | \since 4.2 |
3096 | |
3097 | Returns \c true if world transformation is enabled; otherwise returns |
3098 | false. |
3099 | |
3100 | \sa setWorldMatrixEnabled(), worldTransform(), {Coordinate System} |
3101 | */ |
3102 | |
3103 | bool QPainter::worldMatrixEnabled() const |
3104 | { |
3105 | Q_D(const QPainter); |
3106 | if (!d->engine) { |
3107 | qWarning("QPainter::worldMatrixEnabled: Painter not active" ); |
3108 | return false; |
3109 | } |
3110 | return d->state->WxF; |
3111 | } |
3112 | |
3113 | #if QT_DEPRECATED_SINCE(5, 13) |
3114 | /*! |
3115 | \obsolete |
3116 | |
3117 | Use setWorldMatrixEnabled() instead. |
3118 | |
3119 | \sa setWorldMatrixEnabled() |
3120 | */ |
3121 | |
3122 | void QPainter::setMatrixEnabled(bool enable) |
3123 | { |
3124 | setWorldMatrixEnabled(enable); |
3125 | } |
3126 | |
3127 | /*! |
3128 | \obsolete |
3129 | |
3130 | Use worldMatrixEnabled() instead |
3131 | |
3132 | \sa worldMatrixEnabled() |
3133 | */ |
3134 | |
3135 | bool QPainter::matrixEnabled() const |
3136 | { |
3137 | return worldMatrixEnabled(); |
3138 | } |
3139 | #endif |
3140 | |
3141 | /*! |
3142 | Scales the coordinate system by (\a{sx}, \a{sy}). |
3143 | |
3144 | \sa setWorldTransform(), {QPainter#Coordinate Transformations}{Coordinate Transformations} |
3145 | */ |
3146 | |
3147 | void QPainter::scale(qreal sx, qreal sy) |
3148 | { |
3149 | #ifdef QT_DEBUG_DRAW |
3150 | if (qt_show_painter_debug_output) |
3151 | printf("QPainter::scale(), sx=%f, sy=%f\n" , sx, sy); |
3152 | #endif |
3153 | Q_D(QPainter); |
3154 | if (!d->engine) { |
3155 | qWarning("QPainter::scale: Painter not active" ); |
3156 | return; |
3157 | } |
3158 | |
3159 | d->state->worldMatrix.scale(sx,sy); |
3160 | d->state->WxF = true; |
3161 | d->updateMatrix(); |
3162 | } |
3163 | |
3164 | /*! |
3165 | Shears the coordinate system by (\a{sh}, \a{sv}). |
3166 | |
3167 | \sa setWorldTransform(), {QPainter#Coordinate Transformations}{Coordinate Transformations} |
3168 | */ |
3169 | |
3170 | void QPainter::shear(qreal sh, qreal sv) |
3171 | { |
3172 | #ifdef QT_DEBUG_DRAW |
3173 | if (qt_show_painter_debug_output) |
3174 | printf("QPainter::shear(), sh=%f, sv=%f\n" , sh, sv); |
3175 | #endif |
3176 | Q_D(QPainter); |
3177 | if (!d->engine) { |
3178 | qWarning("QPainter::shear: Painter not active" ); |
3179 | return; |
3180 | } |
3181 | |
3182 | d->state->worldMatrix.shear(sh, sv); |
3183 | d->state->WxF = true; |
3184 | d->updateMatrix(); |
3185 | } |
3186 | |
3187 | /*! |
3188 | \fn void QPainter::rotate(qreal angle) |
3189 | |
3190 | Rotates the coordinate system clockwise. The given \a angle parameter is in degrees. |
3191 | |
3192 | \sa setWorldTransform(), {QPainter#Coordinate Transformations}{Coordinate Transformations} |
3193 | */ |
3194 | |
3195 | void QPainter::rotate(qreal a) |
3196 | { |
3197 | #ifdef QT_DEBUG_DRAW |
3198 | if (qt_show_painter_debug_output) |
3199 | printf("QPainter::rotate(), angle=%f\n" , a); |
3200 | #endif |
3201 | Q_D(QPainter); |
3202 | if (!d->engine) { |
3203 | qWarning("QPainter::rotate: Painter not active" ); |
3204 | return; |
3205 | } |
3206 | |
3207 | d->state->worldMatrix.rotate(a); |
3208 | d->state->WxF = true; |
3209 | d->updateMatrix(); |
3210 | } |
3211 | |
3212 | /*! |
3213 | Translates the coordinate system by the given \a offset; i.e. the |
3214 | given \a offset is added to points. |
3215 | |
3216 | \sa setWorldTransform(), {QPainter#Coordinate Transformations}{Coordinate Transformations} |
3217 | */ |
3218 | void QPainter::translate(const QPointF &offset) |
3219 | { |
3220 | qreal dx = offset.x(); |
3221 | qreal dy = offset.y(); |
3222 | #ifdef QT_DEBUG_DRAW |
3223 | if (qt_show_painter_debug_output) |
3224 | printf("QPainter::translate(), dx=%f, dy=%f\n" , dx, dy); |
3225 | #endif |
3226 | Q_D(QPainter); |
3227 | if (!d->engine) { |
3228 | qWarning("QPainter::translate: Painter not active" ); |
3229 | return; |
3230 | } |
3231 | |
3232 | d->state->worldMatrix.translate(dx, dy); |
3233 | d->state->WxF = true; |
3234 | d->updateMatrix(); |
3235 | } |
3236 | |
3237 | /*! |
3238 | \fn void QPainter::translate(const QPoint &offset) |
3239 | \overload |
3240 | |
3241 | Translates the coordinate system by the given \a offset. |
3242 | */ |
3243 | |
3244 | /*! |
3245 | \fn void QPainter::translate(qreal dx, qreal dy) |
3246 | \overload |
3247 | |
3248 | Translates the coordinate system by the vector (\a dx, \a dy). |
3249 | */ |
3250 | |
3251 | /*! |
3252 | \fn void QPainter::setClipPath(const QPainterPath &path, Qt::ClipOperation operation) |
3253 | |
3254 | Enables clipping, and sets the clip path for the painter to the |
3255 | given \a path, with the clip \a operation. |
3256 | |
3257 | Note that the clip path is specified in logical (painter) |
3258 | coordinates. |
3259 | |
3260 | \sa clipPath(), clipRegion(), {QPainter#Clipping}{Clipping} |
3261 | |
3262 | */ |
3263 | void QPainter::setClipPath(const QPainterPath &path, Qt::ClipOperation op) |
3264 | { |
3265 | #ifdef QT_DEBUG_DRAW |
3266 | if (qt_show_painter_debug_output) { |
3267 | QRectF b = path.boundingRect(); |
3268 | printf("QPainter::setClipPath(), size=%d, op=%d, bounds=[%.2f,%.2f,%.2f,%.2f]\n" , |
3269 | path.elementCount(), op, b.x(), b.y(), b.width(), b.height()); |
3270 | } |
3271 | #endif |
3272 | Q_D(QPainter); |
3273 | |
3274 | if (!d->engine) { |
3275 | qWarning("QPainter::setClipPath: Painter not active" ); |
3276 | return; |
3277 | } |
3278 | |
3279 | if ((!d->state->clipEnabled && op != Qt::NoClip)) |
3280 | op = Qt::ReplaceClip; |
3281 | |
3282 | if (d->extended) { |
3283 | d->state->clipEnabled = true; |
3284 | d->extended->clip(path, op); |
3285 | if (op == Qt::NoClip || op == Qt::ReplaceClip) |
3286 | d->state->clipInfo.clear(); |
3287 | d->state->clipInfo.append(QPainterClipInfo(path, op, d->state->matrix)); |
3288 | d->state->clipOperation = op; |
3289 | return; |
3290 | } |
3291 | |
3292 | if (d->state->clipOperation == Qt::NoClip && op == Qt::IntersectClip) |
3293 | op = Qt::ReplaceClip; |
3294 | |
3295 | d->state->clipPath = path; |
3296 | d->state->clipOperation = op; |
3297 | if (op == Qt::NoClip || op == Qt::ReplaceClip) |
3298 | d->state->clipInfo.clear(); |
3299 | d->state->clipInfo.append(QPainterClipInfo(path, op, d->state->matrix)); |
3300 | d->state->clipEnabled = true; |
3301 | d->state->dirtyFlags |= QPaintEngine::DirtyClipPath | QPaintEngine::DirtyClipEnabled; |
3302 | d->updateState(d->state); |
3303 | } |
3304 | |
3305 | /*! |
3306 | Draws the outline (strokes) the path \a path with the pen specified |
3307 | by \a pen |
3308 | |
3309 | \sa fillPath(), {QPainter#Drawing}{Drawing} |
3310 | */ |
3311 | void QPainter::strokePath(const QPainterPath &path, const QPen &pen) |
3312 | { |
3313 | Q_D(QPainter); |
3314 | |
3315 | if (!d->engine) { |
3316 | qWarning("QPainter::strokePath: Painter not active" ); |
3317 | return; |
3318 | } |
3319 | |
3320 | if (path.isEmpty()) |
3321 | return; |
3322 | |
3323 | if (d->extended) { |
3324 | const QGradient *g = qpen_brush(pen).gradient(); |
3325 | if (!g || g->coordinateMode() == QGradient::LogicalMode) { |
3326 | d->extended->stroke(qtVectorPathForPath(path), pen); |
3327 | return; |
3328 | } |
3329 | } |
3330 | |
3331 | QBrush oldBrush = d->state->brush; |
3332 | QPen oldPen = d->state->pen; |
3333 | |
3334 | setPen(pen); |
3335 | setBrush(Qt::NoBrush); |
3336 | |
3337 | drawPath(path); |
3338 | |
3339 | // Reset old state |
3340 | setPen(oldPen); |
3341 | setBrush(oldBrush); |
3342 | } |
3343 | |
3344 | /*! |
3345 | Fills the given \a path using the given \a brush. The outline is |
3346 | not drawn. |
3347 | |
3348 | Alternatively, you can specify a QColor instead of a QBrush; the |
3349 | QBrush constructor (taking a QColor argument) will automatically |
3350 | create a solid pattern brush. |
3351 | |
3352 | \sa drawPath() |
3353 | */ |
3354 | void QPainter::fillPath(const QPainterPath &path, const QBrush &brush) |
3355 | { |
3356 | Q_D(QPainter); |
3357 | |
3358 | if (!d->engine) { |
3359 | qWarning("QPainter::fillPath: Painter not active" ); |
3360 | return; |
3361 | } |
3362 | |
3363 | if (path.isEmpty()) |
3364 | return; |
3365 | |
3366 | if (d->extended) { |
3367 | const QGradient *g = brush.gradient(); |
3368 | if (!g || g->coordinateMode() == QGradient::LogicalMode) { |
3369 | d->extended->fill(qtVectorPathForPath(path), brush); |
3370 | return; |
3371 | } |
3372 | } |
3373 | |
3374 | QBrush oldBrush = d->state->brush; |
3375 | QPen oldPen = d->state->pen; |
3376 | |
3377 | setPen(Qt::NoPen); |
3378 | setBrush(brush); |
3379 | |
3380 | drawPath(path); |
3381 | |
3382 | // Reset old state |
3383 | setPen(oldPen); |
3384 | setBrush(oldBrush); |
3385 | } |
3386 | |
3387 | /*! |
3388 | Draws the given painter \a path using the current pen for outline |
3389 | and the current brush for filling. |
3390 | |
3391 | \table 100% |
3392 | \row |
3393 | \li \inlineimage qpainter-path.png |
3394 | \li |
3395 | \snippet code/src_gui_painting_qpainter.cpp 5 |
3396 | \endtable |
3397 | |
3398 | \sa {painting/painterpaths}{the Painter Paths |
3399 | example},{painting/deform}{the Vector Deformation example} |
3400 | */ |
3401 | void QPainter::drawPath(const QPainterPath &path) |
3402 | { |
3403 | #ifdef QT_DEBUG_DRAW |
3404 | QRectF pathBounds = path.boundingRect(); |
3405 | if (qt_show_painter_debug_output) |
3406 | printf("QPainter::drawPath(), size=%d, [%.2f,%.2f,%.2f,%.2f]\n" , |
3407 | path.elementCount(), |
3408 | pathBounds.x(), pathBounds.y(), pathBounds.width(), pathBounds.height()); |
3409 | #endif |
3410 | |
3411 | Q_D(QPainter); |
3412 | |
3413 | if (!d->engine) { |
3414 | qWarning("QPainter::drawPath: Painter not active" ); |
3415 | return; |
3416 | } |
3417 | |
3418 | if (d->extended) { |
3419 | d->extended->drawPath(path); |
3420 | return; |
3421 | } |
3422 | d->updateState(d->state); |
3423 | |
3424 | if (d->engine->hasFeature(QPaintEngine::PainterPaths) && d->state->emulationSpecifier == 0) { |
3425 | d->engine->drawPath(path); |
3426 | } else { |
3427 | d->draw_helper(path); |
3428 | } |
3429 | } |
3430 | |
3431 | /*! |
3432 | \fn void QPainter::drawLine(const QLineF &line) |
3433 | |
3434 | Draws a line defined by \a line. |
3435 | |
3436 | \table 100% |
3437 | \row |
3438 | \li \inlineimage qpainter-line.png |
3439 | \li |
3440 | \snippet code/src_gui_painting_qpainter.cpp 6 |
3441 | \endtable |
3442 | |
3443 | \sa drawLines(), drawPolyline(), {Coordinate System} |
3444 | */ |
3445 | |
3446 | /*! |
3447 | \fn void QPainter::drawLine(const QLine &line) |
3448 | \overload |
3449 | |
3450 | Draws a line defined by \a line. |
3451 | */ |
3452 | |
3453 | /*! |
3454 | \fn void QPainter::drawLine(const QPoint &p1, const QPoint &p2) |
3455 | \overload |
3456 | |
3457 | Draws a line from \a p1 to \a p2. |
3458 | */ |
3459 | |
3460 | /*! |
3461 | \fn void QPainter::drawLine(const QPointF &p1, const QPointF &p2) |
3462 | \overload |
3463 | |
3464 | Draws a line from \a p1 to \a p2. |
3465 | */ |
3466 | |
3467 | /*! |
3468 | \fn void QPainter::drawLine(int x1, int y1, int x2, int y2) |
3469 | \overload |
3470 | |
3471 | Draws a line from (\a x1, \a y1) to (\a x2, \a y2). |
3472 | */ |
3473 | |
3474 | /*! |
3475 | \fn void QPainter::drawRect(const QRectF &rectangle) |
3476 | |
3477 | Draws the current \a rectangle with the current pen and brush. |
3478 | |
3479 | A filled rectangle has a size of \a{rectangle}.size(). A stroked |
3480 | rectangle has a size of \a{rectangle}.size() plus the pen width. |
3481 | |
3482 | \table 100% |
3483 | \row |
3484 | \li \inlineimage qpainter-rectangle.png |
3485 | \li |
3486 | \snippet code/src_gui_painting_qpainter.cpp 7 |
3487 | \endtable |
3488 | |
3489 | \sa drawRects(), drawPolygon(), {Coordinate System} |
3490 | */ |
3491 | |
3492 | /*! |
3493 | \fn void QPainter::drawRect(const QRect &rectangle) |
3494 | |
3495 | \overload |
3496 | |
3497 | Draws the current \a rectangle with the current pen and brush. |
3498 | */ |
3499 | |
3500 | /*! |
3501 | \fn void QPainter::drawRect(int x, int y, int width, int height) |
3502 | |
3503 | \overload |
3504 | |
3505 | Draws a rectangle with upper left corner at (\a{x}, \a{y}) and |
3506 | with the given \a width and \a height. |
3507 | */ |
3508 | |
3509 | /*! |
3510 | \fn void QPainter::drawRects(const QRectF *rectangles, int rectCount) |
3511 | |
3512 | Draws the first \a rectCount of the given \a rectangles using the |
3513 | current pen and brush. |
3514 | |
3515 | \sa drawRect() |
3516 | */ |
3517 | void QPainter::drawRects(const QRectF *rects, int rectCount) |
3518 | { |
3519 | #ifdef QT_DEBUG_DRAW |
3520 | if (qt_show_painter_debug_output) |
3521 | printf("QPainter::drawRects(), count=%d\n" , rectCount); |
3522 | #endif |
3523 | Q_D(QPainter); |
3524 | |
3525 | if (!d->engine) { |
3526 | qWarning("QPainter::drawRects: Painter not active" ); |
3527 | return; |
3528 | } |
3529 | |
3530 | if (rectCount <= 0) |
3531 | return; |
3532 | |
3533 | if (d->extended) { |
3534 | d->extended->drawRects(rects, rectCount); |
3535 | return; |
3536 | } |
3537 | |
3538 | d->updateState(d->state); |
3539 | |
3540 | if (!d->state->emulationSpecifier) { |
3541 | d->engine->drawRects(rects, rectCount); |
3542 | return; |
3543 | } |
3544 | |
3545 | if (d->state->emulationSpecifier == QPaintEngine::PrimitiveTransform |
3546 | && d->state->matrix.type() == QTransform::TxTranslate) { |
3547 | for (int i=0; i<rectCount; ++i) { |
3548 | QRectF r(rects[i].x() + d->state->matrix.dx(), |
3549 | rects[i].y() + d->state->matrix.dy(), |
3550 | rects[i].width(), |
3551 | rects[i].height()); |
3552 | d->engine->drawRects(&r, 1); |
3553 | } |
3554 | } else { |
3555 | if (d->state->brushNeedsResolving() || d->state->penNeedsResolving()) { |
3556 | for (int i=0; i<rectCount; ++i) { |
3557 | QPainterPath rectPath; |
3558 | rectPath.addRect(rects[i]); |
3559 | d->draw_helper(rectPath, QPainterPrivate::StrokeAndFillDraw); |
3560 | } |
3561 | } else { |
3562 | QPainterPath rectPath; |
3563 | for (int i=0; i<rectCount; ++i) |
3564 | rectPath.addRect(rects[i]); |
3565 | d->draw_helper(rectPath, QPainterPrivate::StrokeAndFillDraw); |
3566 | } |
3567 | } |
3568 | } |
3569 | |
3570 | /*! |
3571 | \fn void QPainter::drawRects(const QRect *rectangles, int rectCount) |
3572 | \overload |
3573 | |
3574 | Draws the first \a rectCount of the given \a rectangles using the |
3575 | current pen and brush. |
3576 | */ |
3577 | void QPainter::drawRects(const QRect *rects, int rectCount) |
3578 | { |
3579 | #ifdef QT_DEBUG_DRAW |
3580 | if (qt_show_painter_debug_output) |
3581 | printf("QPainter::drawRects(), count=%d\n" , rectCount); |
3582 | #endif |
3583 | Q_D(QPainter); |
3584 | |
3585 | if (!d->engine) { |
3586 | qWarning("QPainter::drawRects: Painter not active" ); |
3587 | return; |
3588 | } |
3589 | |
3590 | if (rectCount <= 0) |
3591 | return; |
3592 | |
3593 | if (d->extended) { |
3594 | d->extended->drawRects(rects, rectCount); |
3595 | return; |
3596 | } |
3597 | |
3598 | d->updateState(d->state); |
3599 | |
3600 | if (!d->state->emulationSpecifier) { |
3601 | d->engine->drawRects(rects, rectCount); |
3602 | return; |
3603 | } |
3604 | |
3605 | if (d->state->emulationSpecifier == QPaintEngine::PrimitiveTransform |
3606 | && d->state->matrix.type() == QTransform::TxTranslate) { |
3607 | for (int i=0; i<rectCount; ++i) { |
3608 | QRectF r(rects[i].x() + d->state->matrix.dx(), |
3609 | rects[i].y() + d->state->matrix.dy(), |
3610 | rects[i].width(), |
3611 | rects[i].height()); |
3612 | |
3613 | d->engine->drawRects(&r, 1); |
3614 | } |
3615 | } else { |
3616 | if (d->state->brushNeedsResolving() || d->state->penNeedsResolving()) { |
3617 | for (int i=0; i<rectCount; ++i) { |
3618 | QPainterPath rectPath; |
3619 | rectPath.addRect(rects[i]); |
3620 | d->draw_helper(rectPath, QPainterPrivate::StrokeAndFillDraw); |
3621 | } |
3622 | } else { |
3623 | QPainterPath rectPath; |
3624 | for (int i=0; i<rectCount; ++i) |
3625 | rectPath.addRect(rects[i]); |
3626 | |
3627 | d->draw_helper(rectPath, QPainterPrivate::StrokeAndFillDraw); |
3628 | } |
3629 | } |
3630 | } |
3631 | |
3632 | /*! |
3633 | \fn void QPainter::drawRects(const QVector<QRectF> &rectangles) |
3634 | \overload |
3635 | |
3636 | Draws the given \a rectangles using the current pen and brush. |
3637 | */ |
3638 | |
3639 | /*! |
3640 | \fn void QPainter::drawRects(const QVector<QRect> &rectangles) |
3641 | |
3642 | \overload |
3643 | |
3644 | Draws the given \a rectangles using the current pen and brush. |
3645 | */ |
3646 | |
3647 | /*! |
3648 | \fn void QPainter::drawPoint(const QPointF &position) |
3649 | |
3650 | Draws a single point at the given \a position using the current |
3651 | pen's color. |
3652 | |
3653 | \sa {Coordinate System} |
3654 | */ |
3655 | |
3656 | /*! |
3657 | \fn void QPainter::drawPoint(const QPoint &position) |
3658 | \overload |
3659 | |
3660 | Draws a single point at the given \a position using the current |
3661 | pen's color. |
3662 | */ |
3663 | |
3664 | /*! \fn void QPainter::drawPoint(int x, int y) |
3665 | |
3666 | \overload |
3667 | |
3668 | Draws a single point at position (\a x, \a y). |
3669 | */ |
3670 | |
3671 | /*! |
3672 | Draws the first \a pointCount points in the array \a points using |
3673 | the current pen's color. |
3674 | |
3675 | \sa {Coordinate System} |
3676 | */ |
3677 | void QPainter::drawPoints(const QPointF *points, int pointCount) |
3678 | { |
3679 | #ifdef QT_DEBUG_DRAW |
3680 | if (qt_show_painter_debug_output) |
3681 | printf("QPainter::drawPoints(), count=%d\n" , pointCount); |
3682 | #endif |
3683 | Q_D(QPainter); |
3684 | |
3685 | if (!d->engine) { |
3686 | qWarning("QPainter::drawPoints: Painter not active" ); |
3687 | return; |
3688 | } |
3689 | |
3690 | if (pointCount <= 0) |
3691 | return; |
3692 | |
3693 | if (d->extended) { |
3694 | d->extended->drawPoints(points, pointCount); |
3695 | return; |
3696 | } |
3697 | |
3698 | d->updateState(d->state); |
3699 | |
3700 | if (!d->state->emulationSpecifier) { |
3701 | d->engine->drawPoints(points, pointCount); |
3702 | return; |
3703 | } |
3704 | |
3705 | if (d->state->emulationSpecifier == QPaintEngine::PrimitiveTransform |
3706 | && d->state->matrix.type() == QTransform::TxTranslate) { |
3707 | // ### use drawPoints function |
3708 | for (int i=0; i<pointCount; ++i) { |
3709 | QPointF pt(points[i].x() + d->state->matrix.dx(), |
3710 | points[i].y() + d->state->matrix.dy()); |
3711 | d->engine->drawPoints(&pt, 1); |
3712 | } |
3713 | } else { |
3714 | QPen pen = d->state->pen; |
3715 | bool flat_pen = pen.capStyle() == Qt::FlatCap; |
3716 | if ( |
---|