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