Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qpaintengine_alpha.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
5#include <qtprintsupportglobal.h>
6
7#ifndef QT_NO_PRINTER
8#include <qdebug.h>
9#include "private/qpaintengine_alpha_p.h"
10
11#include "private/qpainter_p.h"
12#include "private/qpicture_p.h"
13#include "private/qfont_p.h"
14#include "QtGui/qpicture.h"
15
17
18QAlphaPaintEngine::QAlphaPaintEngine(QAlphaPaintEnginePrivate &data, PaintEngineFeatures devcaps)
19 : QPaintEngine(data, devcaps)
20{
21
22}
23
24QAlphaPaintEngine::~QAlphaPaintEngine()
25{
26
27}
28
29bool QAlphaPaintEngine::begin(QPaintDevice *pdev)
30{
31 Q_D(QAlphaPaintEngine);
32
33 d->m_continueCall = true;
34 if (d->m_pass != 0) {
35 return true;
36 }
37
38 d->m_savedcaps = gccaps;
39 d->m_pdev = pdev;
40
41 d->m_alphaPen = false;
42 d->m_alphaBrush = false;
43 d->m_alphaOpacity = false;
44 d->m_hasalpha = false;
45 d->m_advancedPen = false;
46 d->m_advancedBrush = false;
47 d->m_complexTransform = false;
48 d->m_emulateProjectiveTransforms = false;
49
50 // clear alpha region
51 d->m_alphargn = QRegion();
52 d->m_cliprgn = QRegion();
53 d->m_pen = QPen();
54 d->m_transform = QTransform();
55
56 flushAndInit();
57
58 return true;
59}
60
61bool QAlphaPaintEngine::end()
62{
63 Q_D(QAlphaPaintEngine);
64
65 d->m_continueCall = true;
66 if (d->m_pass != 0) {
67 return true;
68 }
69
70 flushAndInit(false);
71 return true;
72}
73
74void QAlphaPaintEngine::updateState(const QPaintEngineState &state)
75{
76 Q_D(QAlphaPaintEngine);
77
78 DirtyFlags flags = state.state();
79 if (flags & QPaintEngine::DirtyTransform) {
80 d->m_transform = state.transform();
81 d->m_complexTransform = (d->m_transform.type() > QTransform::TxScale);
82 d->m_emulateProjectiveTransforms = !(d->m_savedcaps & QPaintEngine::PerspectiveTransform)
83 && !(d->m_savedcaps & QPaintEngine::AlphaBlend)
84 && (d->m_transform.type() >= QTransform::TxProject);
85 }
86 if (flags & QPaintEngine::DirtyPen) {
87 d->m_pen = state.pen();
88 if (d->m_pen.style() == Qt::NoPen) {
89 d->m_advancedPen = false;
90 d->m_alphaPen = false;
91 } else {
92 d->m_advancedPen = (d->m_pen.brush().style() != Qt::SolidPattern);
93 d->m_alphaPen = !d->m_pen.brush().isOpaque();
94 }
95 }
96
97 if (d->m_pass != 0) {
98 d->m_continueCall = true;
99 return;
100 }
101 d->m_continueCall = false;
102
103 if (flags & QPaintEngine::DirtyOpacity) {
104 d->m_alphaOpacity = (state.opacity() != 1.0f);
105 }
106
107 if (flags & QPaintEngine::DirtyBrush) {
108 if (state.brush().style() == Qt::NoBrush) {
109 d->m_advancedBrush = false;
110 d->m_alphaBrush = false;
111 } else {
112 d->m_advancedBrush = (state.brush().style() != Qt::SolidPattern);
113 d->m_alphaBrush = !state.brush().isOpaque();
114 }
115 }
116
117
118 d->m_hasalpha = d->m_alphaOpacity || d->m_alphaBrush || d->m_alphaPen;
119
120 if (d->m_picengine) {
121 const QPainter *p = painter();
122 d->m_picpainter->setPen(p->pen());
123 d->m_picpainter->setBrush(p->brush());
124 d->m_picpainter->setBrushOrigin(p->brushOriginF());
125 d->m_picpainter->setFont(p->font());
126 d->m_picpainter->setOpacity(p->opacity());
127 d->m_picpainter->setTransform(p->combinedTransform());
128 d->m_picengine->updateState(state);
129 }
130}
131
132void QAlphaPaintEngine::drawPath(const QPainterPath &path)
133{
134 Q_D(QAlphaPaintEngine);
135
136 QRectF tr = d->addPenWidth(path);
137
138 if (d->m_pass == 0) {
139 d->m_continueCall = false;
140 if (d->canSeeTroughBackground(d->m_hasalpha, tr) || d->m_advancedPen || d->m_advancedBrush
141 || d->m_emulateProjectiveTransforms)
142 {
143 d->addAlphaRect(tr);
144 }
145
146 d->addDirtyRect(tr);
147
148 if (d->m_picengine)
149 d->m_picengine->drawPath(path);
150 } else {
151 d->m_continueCall = !d->fullyContained(tr);
152 }
153}
154
155void QAlphaPaintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
156{
157 Q_D(QAlphaPaintEngine);
158
159 QPolygonF poly;
160 poly.reserve(pointCount);
161 for (int i = 0; i < pointCount; ++i)
162 poly.append(points[i]);
163
164 QPainterPath path;
165 path.addPolygon(poly);
166 QRectF tr = d->addPenWidth(path);
167
168 if (d->m_pass == 0) {
169 d->m_continueCall = false;
170 if (d->canSeeTroughBackground(d->m_hasalpha, tr) || d->m_advancedPen || d->m_advancedBrush
171 || d->m_emulateProjectiveTransforms)
172 {
173 d->addAlphaRect(tr);
174 }
175
176 d->addDirtyRect(tr);
177
178 if (d->m_picengine)
179 d->m_picengine->drawPolygon(points, pointCount, mode);
180 } else {
181 d->m_continueCall = !d->fullyContained(tr);
182 }
183}
184
185void QAlphaPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr)
186{
187 Q_D(QAlphaPaintEngine);
188
189 QRectF tr = d->m_transform.mapRect(r);
190 if (d->m_pass == 0) {
191 d->m_continueCall = false;
192 if (d->canSeeTroughBackground(pm.hasAlpha() || d->m_alphaOpacity, tr) || d->m_complexTransform || pm.isQBitmap()) {
193 d->addAlphaRect(tr);
194 }
195
196 d->addDirtyRect(tr);
197
198 if (d->m_picengine)
199 d->m_picengine->drawPixmap(r, pm, sr);
200
201 } else {
202 d->m_continueCall = !d->fullyContained(tr);
203 }
204}
205
206void QAlphaPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
207{
208 Q_D(QAlphaPaintEngine);
209
210 QRectF tr(p.x(), p.y() - textItem.ascent(), textItem.width() + 5, textItem.ascent() + textItem.descent() + 5);
211 tr = d->m_transform.mapRect(tr);
212
213 if (d->m_pass == 0) {
214 d->m_continueCall = false;
215 if (d->canSeeTroughBackground(d->m_alphaPen || d->m_alphaOpacity, tr) || d->m_advancedPen) {
216 d->addAlphaRect(tr);
217 }
218
219 d->addDirtyRect(tr);
220
221 if (d->m_picengine) {
222 d->m_picengine->drawTextItem(p, textItem);
223 }
224 } else {
225 d->m_continueCall = !d->fullyContained(tr);
226 }
227}
228
229void QAlphaPaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &s)
230{
231 Q_D(QAlphaPaintEngine);
232
233 QRectF brect = d->m_transform.mapRect(r);
234
235 if (d->m_pass == 0) {
236 d->m_continueCall = false;
237 if (d->canSeeTroughBackground(pixmap.hasAlpha() || d->m_alphaOpacity, brect) || d->m_complexTransform || pixmap.isQBitmap()) {
238 d->addAlphaRect(brect);
239 }
240
241 d->addDirtyRect(brect);
242
243 if (d->m_picengine)
244 d->m_picengine->drawTiledPixmap(r, pixmap, s);
245 } else {
246 d->m_continueCall = !d->fullyContained(brect);
247 }
248}
249
250QRegion QAlphaPaintEngine::alphaClipping() const
251{
252 Q_D(const QAlphaPaintEngine);
253 return d->m_cliprgn;
254}
255
256bool QAlphaPaintEngine::continueCall() const
257{
258 Q_D(const QAlphaPaintEngine);
259 return d->m_continueCall;
260}
261
262void QAlphaPaintEngine::flushAndInit(bool init)
263{
264 Q_D(QAlphaPaintEngine);
265 Q_ASSERT(d->m_pass == 0);
266
267 if (d->m_pic) {
268 d->m_picpainter->end();
269
270 // set clip region
271 d->m_alphargn = d->m_alphargn.intersected(QRect(0, 0, d->m_pdev->width(), d->m_pdev->height()));
272
273 // just use the bounding rect if it's a complex region..
274 if (d->m_alphargn.rectCount() > 10) {
275 QRect br = d->m_alphargn.boundingRect();
276 d->m_alphargn = QRegion(br);
277 }
278
279 const auto oldAlphaRegion = d->m_cliprgn = d->m_alphargn;
280
281 // now replay the QPicture
282 ++d->m_pass; // we are now doing pass #2
283
284 // reset states
285 gccaps = d->m_savedcaps;
286
287 painter()->save();
288 d->resetState(painter());
289
290 // make sure the output from QPicture is unscaled
291 QTransform mtx;
292 mtx.scale(1.0f / (qreal(d->m_pdev->logicalDpiX()) / qreal(qt_defaultDpiX())),
293 1.0f / (qreal(d->m_pdev->logicalDpiY()) / qreal(qt_defaultDpiY())));
294 painter()->setTransform(mtx);
295 painter()->drawPicture(0, 0, *d->m_pic);
296
297 d->m_cliprgn = QRegion();
298 d->resetState(painter());
299
300 // fill in the alpha images
301 for (const auto &rect : oldAlphaRegion)
302 d->drawAlphaImage(rect);
303
304 d->m_alphargn = QRegion();
305
306 painter()->restore();
307
308 --d->m_pass; // pass #2 finished
309
310 cleanUp();
311 }
312
313 if (init) {
314 gccaps = PaintEngineFeatures(AllFeatures & ~QPaintEngine::ObjectBoundingModeGradients);
315
316 d->m_pic = new QPicture();
317 d->m_pic->d_ptr->in_memory_only = true;
318 d->m_picpainter = new QPainter(d->m_pic);
319 d->m_picengine = d->m_picpainter->paintEngine();
320
321 // When newPage() is called and the m_picpainter is recreated
322 // we have to copy the current state of the original printer
323 // painter back to the m_picpainter
324 d->m_picpainter->setPen(painter()->pen());
325 d->m_picpainter->setBrush(painter()->brush());
326 d->m_picpainter->setBrushOrigin(painter()->brushOriginF());
327 d->m_picpainter->setFont(painter()->font());
328 d->m_picpainter->setOpacity(painter()->opacity());
329 d->m_picpainter->setTransform(painter()->combinedTransform());
330 d->m_picengine->syncState();
331 QPainterState &state = *d->m_picpainter->d_func()->state;
332 QPainter *oldPainter = state.painter;
333 state = *painter()->d_func()->state;
334 state.painter = oldPainter;
335 }
336}
337
338void QAlphaPaintEngine::cleanUp()
339{
340 Q_D(QAlphaPaintEngine);
341
342 delete d->m_picpainter;
343 delete d->m_pic;
344
345 d->m_picpainter = nullptr;
346 d->m_pic = nullptr;
347 d->m_picengine = nullptr;
348}
349
350QAlphaPaintEnginePrivate::QAlphaPaintEnginePrivate()
351 : m_pass(0),
352 m_pic(nullptr),
353 m_picengine(nullptr),
354 m_picpainter(nullptr),
355 m_numberOfCachedRects(0),
356 m_hasalpha(false),
357 m_alphaPen(false),
358 m_alphaBrush(false),
359 m_alphaOpacity(false),
360 m_advancedPen(false),
361 m_advancedBrush(false),
362 m_complexTransform(false)
363{
364
365}
366
367QAlphaPaintEnginePrivate::~QAlphaPaintEnginePrivate()
368{
369 delete m_picpainter;
370 delete m_pic;
371}
372
373QRectF QAlphaPaintEnginePrivate::addPenWidth(const QPainterPath &path)
374{
375 QPainterPath tmp = path;
376
377 if (m_pen.style() == Qt::NoPen)
378 return (path.controlPointRect() * m_transform).boundingRect();
379 bool cosmetic = m_pen.isCosmetic();
380 if (cosmetic)
381 tmp = path * m_transform;
382
383 QPainterPathStroker stroker;
384 if (m_pen.widthF() == 0.0f)
385 stroker.setWidth(1.0);
386 else
387 stroker.setWidth(m_pen.widthF());
388 stroker.setJoinStyle(m_pen.joinStyle());
389 stroker.setCapStyle(m_pen.capStyle());
390 tmp = stroker.createStroke(tmp);
391 if (cosmetic)
392 return tmp.controlPointRect();
393
394 return (tmp.controlPointRect() * m_transform).boundingRect();
395}
396
397void QAlphaPaintEnginePrivate::addAlphaRect(const QRectF &rect)
398{
399 m_alphargn |= rect.toAlignedRect();
400}
401
402bool QAlphaPaintEnginePrivate::canSeeTroughBackground(bool somethingInRectHasAlpha, const QRectF &rect) const
403{
404 if (somethingInRectHasAlpha) {
405 if (m_dirtyRects.size() != m_numberOfCachedRects) {
406 m_cachedDirtyRgn.setRects(m_dirtyRects.constData(), m_dirtyRects.size());
407 m_numberOfCachedRects = m_dirtyRects.size();
408 }
409 return m_cachedDirtyRgn.intersects(rect.toAlignedRect());
410 }
411 return false;
412}
413
414void QAlphaPaintEnginePrivate::drawAlphaImage(const QRectF &rect)
415{
416 Q_Q(QAlphaPaintEngine);
417
418 qreal dpiX = qMax(m_pdev->logicalDpiX(), 300);
419 qreal dpiY = qMax(m_pdev->logicalDpiY(), 300);
420 qreal xscale = (dpiX / m_pdev->logicalDpiX());
421 qreal yscale = (dpiY / m_pdev->logicalDpiY());
422
423 QTransform picscale;
424 picscale.scale(xscale, yscale);
425
426 const int tileSize = 2048;
427 QSize size((int(rect.width() * xscale)), int(rect.height() * yscale));
428 int divw = (size.width() / tileSize);
429 int divh = (size.height() / tileSize);
430 divw += 1;
431 divh += 1;
432
433 int incx = int(rect.width() / divw);
434 int incy = int(rect.height() / divh);
435
436 for (int y=0; y<divh; ++y) {
437 int ypos = int((incy * y) + rect.y());
438 int height = int((y == (divh - 1)) ? (rect.height() - (incy * y)) : incy) + 1;
439
440 for (int x=0; x<divw; ++x) {
441 int xpos = int((incx * x) + rect.x());
442 int width = int((x == (divw - 1)) ? (rect.width() - (incx * x)) : incx) + 1;
443
444 QSize imgsize((int)(width * xscale), (int)(height * yscale));
445 QImage img(imgsize, QImage::Format_RGB32);
446 img.fill(0xffffffff);
447
448 QPainter imgpainter(&img);
449 imgpainter.setTransform(picscale);
450 QPointF picpos(qreal(-xpos), qreal(-ypos));
451 imgpainter.drawPicture(picpos, *m_pic);
452 imgpainter.end();
453
454 q->painter()->setTransform(QTransform());
455 QRect r(xpos, ypos, width, height);
456 q->painter()->drawImage(r, img);
457 }
458 }
459}
460
461bool QAlphaPaintEnginePrivate::fullyContained(const QRectF &rect) const
462{
463 QRegion r(rect.toAlignedRect());
464 return (m_cliprgn.intersected(r) == r);
465}
466
467void QAlphaPaintEnginePrivate::resetState(QPainter *p)
468{
469 p->setPen(QPen());
470 p->setBrush(QBrush());
471 p->setBrushOrigin(0,0);
472 p->setBackground(QBrush());
473 p->setFont(QFont());
474 p->setTransform(QTransform());
475 // The view transform is already recorded and included in the
476 // picture we're about to replay. If we don't turn if off,
477 // the view matrix will be applied twice.
478 p->setViewTransformEnabled(false);
479 p->setClipRegion(QRegion(), Qt::NoClip);
480 p->setClipPath(QPainterPath(), Qt::NoClip);
481 p->setClipping(false);
482 p->setOpacity(1.0f);
483}
484
485
486QT_END_NAMESPACE
487
488#endif // QT_NO_PRINTER