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