Remove "All rights reserved" line from license headers.
[profile/ivi/qtdeclarative.git] / src / qtquick1 / graphicsitems / qdeclarativepainteditem.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtDeclarative module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "QtQuick1/private/qdeclarativepainteditem_p.h"
43 #include "QtQuick1/private/qdeclarativepainteditem_p_p.h"
44
45 #include <QDebug>
46 #include <QPen>
47 #include <QEvent>
48 #include <QApplication>
49 #include <QGraphicsSceneMouseEvent>
50 #include <QPainter>
51 #include <QPaintEngine>
52 #include <qmath.h>
53
54 QT_BEGIN_NAMESPACE
55
56
57
58 /*!
59     \class QDeclarative1PaintedItem
60     \brief The QDeclarative1PaintedItem class is an abstract base class for QDeclarative1View items that want cached painting.
61     \internal
62
63     This is a convenience class for implementing items that cache their painting.
64     The contents of the item are cached behind the scenes.
65     The dirtyCache() function should be called if the contents change to
66     ensure the cache is refreshed the next time painting occurs.
67
68     To subclass QDeclarative1PaintedItem, you must reimplement drawContents() to draw
69     the contents of the item.
70 */
71
72 /*!
73     \fn void QDeclarative1PaintedItem::drawContents(QPainter *painter, const QRect &rect)
74
75     This function is called when the cache needs to be refreshed. When
76     sub-classing QDeclarative1PaintedItem this function should be implemented so as to
77     paint the contents of the item using the given \a painter for the
78     area of the contents specified by \a rect.
79 */
80
81 /*!
82     \property QDeclarative1PaintedItem::contentsSize
83     \brief The size of the contents
84
85     The contents size is the size of the item in regards to how it is painted
86     using the drawContents() function.  This is distinct from the size of the
87     item in regards to height() and width().
88 */
89
90 // XXX bug in WebKit - can call repaintRequested and other cache-changing functions from within render!
91 static int inpaint=0;
92 static int inpaint_clearcache=0;
93
94 extern Q_GUI_EXPORT bool qt_applefontsmoothing_enabled;
95
96 /*!
97     Marks areas of the cache that intersect with the given \a rect as dirty and
98     in need of being refreshed.
99
100     \sa clearCache()
101 */
102 void QDeclarative1PaintedItem::dirtyCache(const QRect& rect)
103 {
104     Q_D(QDeclarative1PaintedItem);
105     QRect srect(qCeil(rect.x()*d->contentsScale),
106             qCeil(rect.y()*d->contentsScale),
107             qCeil(rect.width()*d->contentsScale),
108             qCeil(rect.height()*d->contentsScale));
109     for (int i=0; i < d->imagecache.count(); ) {
110         QDeclarative1PaintedItemPrivate::ImageCacheItem *c = d->imagecache[i];
111         QRect isect = (c->area & srect) | c->dirty;
112         if (isect == c->area && !inpaint) {
113             delete d->imagecache.takeAt(i);
114         } else {
115             c->dirty = isect;
116             ++i;
117         }
118     }
119 }
120
121 /*!
122     Marks the entirety of the contents cache as dirty.
123
124     \sa dirtyCache()
125 */
126 void QDeclarative1PaintedItem::clearCache()
127 {
128     if (inpaint) {
129         inpaint_clearcache=1;
130         return;
131     }
132     Q_D(QDeclarative1PaintedItem);
133     qDeleteAll(d->imagecache);
134     d->imagecache.clear();
135 }
136
137 /*!
138     Returns the size of the contents.
139
140     \sa setContentsSize()
141 */
142 QSize QDeclarative1PaintedItem::contentsSize() const
143 {
144     Q_D(const QDeclarative1PaintedItem);
145     return d->contentsSize;
146 }
147
148 /*!
149     Sets the size of the contents to the given \a size.
150
151     \sa contentsSize()
152 */
153 void QDeclarative1PaintedItem::setContentsSize(const QSize &size)
154 {
155     Q_D(QDeclarative1PaintedItem);
156     if (d->contentsSize == size) return;
157     prepareGeometryChange();
158     d->contentsSize = size;
159     clearCache();
160     update();
161     emit contentsSizeChanged();
162 }
163
164 qreal QDeclarative1PaintedItem::contentsScale() const
165 {
166     Q_D(const QDeclarative1PaintedItem);
167     return d->contentsScale;
168 }
169
170 void QDeclarative1PaintedItem::setContentsScale(qreal scale)
171 {
172     Q_D(QDeclarative1PaintedItem);
173     if (d->contentsScale == scale) return;
174     d->contentsScale = scale;
175     clearCache();
176     update();
177     emit contentsScaleChanged();
178 }
179
180
181 /*!
182     Constructs a new QDeclarative1PaintedItem with the given \a parent.
183 */
184 QDeclarative1PaintedItem::QDeclarative1PaintedItem(QDeclarativeItem *parent)
185   : QDeclarativeItem(*(new QDeclarative1PaintedItemPrivate), parent)
186 {
187 }
188
189 /*!
190     \internal
191     Constructs a new QDeclarative1PaintedItem with the given \a parent and
192     initialized private data member \a dd.
193 */
194 QDeclarative1PaintedItem::QDeclarative1PaintedItem(QDeclarative1PaintedItemPrivate &dd, QDeclarativeItem *parent)
195   : QDeclarativeItem(dd, parent)
196 {
197 }
198
199 /*!
200     Destroys the image item.
201 */
202 QDeclarative1PaintedItem::~QDeclarative1PaintedItem()
203 {
204     clearCache();
205 }
206
207 void QDeclarative1PaintedItem::geometryChanged(const QRectF &newGeometry,
208                                               const QRectF &oldGeometry)
209 {
210     if (newGeometry.width() != oldGeometry.width() ||
211         newGeometry.height() != oldGeometry.height())
212         clearCache();
213
214     QDeclarativeItem::geometryChanged(newGeometry, oldGeometry);
215 }
216
217 QVariant QDeclarative1PaintedItem::itemChange(GraphicsItemChange change,
218                                              const QVariant &value)
219 {
220     if (change == ItemVisibleHasChanged)
221         clearCache();
222
223     return QDeclarativeItem::itemChange(change, value);
224 }
225
226 void QDeclarative1PaintedItem::setCacheFrozen(bool frozen)
227 {
228     Q_D(QDeclarative1PaintedItem);
229     if (d->cachefrozen == frozen)
230         return;
231     d->cachefrozen = frozen;
232     // XXX clear cache?
233 }
234
235 QRectF QDeclarative1PaintedItem::boundingRect() const
236 {
237     Q_D(const QDeclarative1PaintedItem);
238     qreal w = d->mWidth;
239     QSizeF sz = d->contentsSize * d->contentsScale;
240     if (w < sz.width())
241         w = sz.width();
242     qreal h = d->mHeight;
243     if (h < sz.height())
244         h = sz.height();
245     return QRectF(0.0,0.0,w,h);
246 }
247
248 /*!
249     \internal
250 */
251 void QDeclarative1PaintedItem::paint(QPainter *p, const QStyleOptionGraphicsItem *, QWidget *)
252 {
253     Q_D(QDeclarative1PaintedItem);
254     const QRect content = boundingRect().toRect();
255     if (content.width() <= 0 || content.height() <= 0)
256         return;
257
258     ++inpaint;
259
260     const QTransform &x = p->deviceTransform();
261     QTransform xinv = x.inverted();
262     QRegion effectiveClip;
263     QRegion sysClip = p->paintEngine()->systemClip();
264     if (xinv.type() <= QTransform::TxScale && sysClip.numRects() < 5) {
265         // simple transform, region gets no more complicated...
266         effectiveClip = xinv.map(sysClip);
267     } else {
268         // do not make complicated regions...
269         effectiveClip = xinv.mapRect(sysClip.boundingRect());
270     }
271
272     QRegion topaint = p->clipRegion();
273     if (topaint.isEmpty()) {
274         if (effectiveClip.isEmpty())
275             topaint = QRect(0,0,p->device()->width(),p->device()->height());
276         else
277             topaint = effectiveClip;
278     } else if (!effectiveClip.isEmpty()) {
279         topaint &= effectiveClip;
280     }
281
282     topaint &= content;
283     QRegion uncached(content);
284     p->setRenderHints(QPainter::SmoothPixmapTransform, d->smooth);
285
286     int cachesize=0;
287     for (int i=0; i<d->imagecache.count(); ++i) {
288         QRect area = d->imagecache[i]->area;
289         if (topaint.contains(area)) {
290             QRectF target(area.x(), area.y(), area.width(), area.height());
291             if (!d->cachefrozen) {
292                 if (!d->imagecache[i]->dirty.isNull() && topaint.contains(d->imagecache[i]->dirty)) {
293 #ifdef Q_WS_MAC
294                     bool oldSmooth = qt_applefontsmoothing_enabled;
295                     qt_applefontsmoothing_enabled = false;
296 #endif
297                     QPainter qp(&d->imagecache[i]->image);
298 #ifdef Q_WS_MAC
299                     qt_applefontsmoothing_enabled = oldSmooth;
300 #endif
301                     qp.setRenderHints(QPainter::HighQualityAntialiasing | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform, d->smoothCache);
302                     qp.translate(-area.x(), -area.y());
303                     qp.scale(d->contentsScale,d->contentsScale);
304                     QRect clip = d->imagecache[i]->dirty;
305                     QRect sclip(qFloor(clip.x()/d->contentsScale),
306                             qFloor(clip.y()/d->contentsScale),
307                             qCeil(clip.width()/d->contentsScale+clip.x()/d->contentsScale-qFloor(clip.x()/d->contentsScale)),
308                             qCeil(clip.height()/d->contentsScale+clip.y()/d->contentsScale-qFloor(clip.y()/d->contentsScale)));
309                     qp.setClipRect(sclip);
310                     if (d->fillColor.isValid()){
311                         if(d->fillColor.alpha() < 255){
312                             // ### Might not work outside of raster paintengine
313                             QPainter::CompositionMode prev = qp.compositionMode();
314                             qp.setCompositionMode(QPainter::CompositionMode_Source);
315                             qp.fillRect(sclip,d->fillColor);
316                             qp.setCompositionMode(prev);
317                         }else{
318                             qp.fillRect(sclip,d->fillColor);
319                         }
320                     }
321                     drawContents(&qp, sclip);
322                     d->imagecache[i]->dirty = QRect();
323                 }
324             }
325             p->drawPixmap(target.toRect(), d->imagecache[i]->image);
326             topaint -= area;
327             d->imagecache[i]->age=0;
328         } else {
329             d->imagecache[i]->age++;
330         }
331         cachesize += area.width()*area.height();
332         uncached -= area;
333     }
334
335     if (!topaint.isEmpty()) {
336         if (!d->cachefrozen) {
337             // Find a sensible larger area, otherwise will paint lots of tiny images.
338             QRect biggerrect = topaint.boundingRect().adjusted(-64,-64,128,128);
339             cachesize += biggerrect.width() * biggerrect.height();
340             while (d->imagecache.count() && cachesize > d->max_imagecache_size) {
341                 int oldest=-1;
342                 int age=-1;
343                 for (int i=0; i<d->imagecache.count(); ++i) {
344                     int a = d->imagecache[i]->age;
345                     if (a > age) {
346                         oldest = i;
347                         age = a;
348                     }
349                 }
350                 cachesize -= d->imagecache[oldest]->area.width()*d->imagecache[oldest]->area.height();
351                 uncached += d->imagecache[oldest]->area;
352                 delete d->imagecache.takeAt(oldest);
353             }
354             const QRegion bigger = QRegion(biggerrect) & uncached;
355             const QVector<QRect> rects = bigger.rects();
356             for (int i = 0; i < rects.count(); ++i) {
357                 const QRect &r = rects.at(i);
358                 QPixmap img(r.size());
359                 if (d->fillColor.isValid())
360                     img.fill(d->fillColor);
361                 {
362 #ifdef Q_WS_MAC
363                     bool oldSmooth = qt_applefontsmoothing_enabled;
364                     qt_applefontsmoothing_enabled = false;
365 #endif
366                     QPainter qp(&img);
367 #ifdef Q_WS_MAC
368                     qt_applefontsmoothing_enabled = oldSmooth;
369 #endif
370                     qp.setRenderHints(QPainter::HighQualityAntialiasing | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform, d->smoothCache);
371
372                     qp.translate(-r.x(),-r.y());
373                     qp.scale(d->contentsScale,d->contentsScale);
374                     QRect sclip(qFloor(r.x()/d->contentsScale),
375                             qFloor(r.y()/d->contentsScale),
376                             qCeil(r.width()/d->contentsScale+r.x()/d->contentsScale-qFloor(r.x()/d->contentsScale)),
377                             qCeil(r.height()/d->contentsScale+r.y()/d->contentsScale-qFloor(r.y()/d->contentsScale)));
378                     drawContents(&qp, sclip);
379                 }
380                 QDeclarative1PaintedItemPrivate::ImageCacheItem *newitem = new QDeclarative1PaintedItemPrivate::ImageCacheItem;
381                 newitem->area = r;
382                 newitem->image = img;
383                 d->imagecache.append(newitem);
384                 p->drawPixmap(r, newitem->image);
385             }
386         } else {
387             const QVector<QRect> rects = uncached.rects();
388             for (int i = 0; i < rects.count(); ++i)
389                 p->fillRect(rects.at(i), Qt::lightGray);
390         }
391     }
392
393     if (inpaint_clearcache) {
394         clearCache();
395         inpaint_clearcache = 0;
396     }
397
398     --inpaint;
399 }
400
401 /*!
402   \qmlproperty int QtQuick1::PaintedItem::pixelCacheSize
403
404   This property holds the maximum number of pixels of image cache to
405   allow. The default is 0.1 megapixels. The cache will not be larger
406   than the (unscaled) size of the WebView.
407 */
408 /*!
409   \property QDeclarative1PaintedItem::pixelCacheSize
410
411   The maximum number of pixels of image cache to allow. The default
412   is 0.1 megapixels. The cache will not be larger than the (unscaled)
413   size of the QDeclarative1PaintedItem.
414 */
415 int QDeclarative1PaintedItem::pixelCacheSize() const
416 {
417     Q_D(const QDeclarative1PaintedItem);
418     return d->max_imagecache_size;
419 }
420
421 void QDeclarative1PaintedItem::setPixelCacheSize(int pixels)
422 {
423     Q_D(QDeclarative1PaintedItem);
424     if (pixels < d->max_imagecache_size) {
425         int cachesize=0;
426         for (int i=0; i<d->imagecache.count(); ++i) {
427             QRect area = d->imagecache[i]->area;
428             cachesize += area.width()*area.height();
429         }
430         while (d->imagecache.count() && cachesize > pixels) {
431             int oldest=-1;
432             int age=-1;
433             for (int i=0; i<d->imagecache.count(); ++i) {
434                 int a = d->imagecache[i]->age;
435                 if (a > age) {
436                     oldest = i;
437                     age = a;
438                 }
439             }
440             cachesize -= d->imagecache[oldest]->area.width()*d->imagecache[oldest]->area.height();
441             delete d->imagecache.takeAt(oldest);
442         }
443     }
444     d->max_imagecache_size = pixels;
445 }
446
447
448
449 /*!
450     \property QDeclarative1PaintedItem::fillColor
451
452     The color to be used to fill the item prior to calling drawContents().
453     By default, this is Qt::transparent.
454
455     Performance improvements can be achieved if subclasses call this with either an
456     invalid color (QColor()), or an appropriate solid color.
457 */
458 void QDeclarative1PaintedItem::setFillColor(const QColor& c)
459 {
460     Q_D(QDeclarative1PaintedItem);
461     if (d->fillColor == c)
462         return;
463     d->fillColor = c;
464     emit fillColorChanged();
465     update();
466 }
467
468 QColor QDeclarative1PaintedItem::fillColor() const
469 {
470     Q_D(const QDeclarative1PaintedItem);
471     return d->fillColor;
472 }
473
474 /*!
475     \qmlproperty bool QtQuick1::PaintedItem::smoothCache
476
477     Controls whether the cached tiles of which the item is composed are
478     rendered smoothly when they are generated.
479
480     This is in addition toe Item::smooth, which controls the smooth painting of
481     the already-painted cached tiles under transformation.
482 */
483 bool QDeclarative1PaintedItem::smoothCache() const
484 {
485     Q_D(const QDeclarative1PaintedItem);
486     return d->smoothCache;
487 }
488
489 void QDeclarative1PaintedItem::setSmoothCache(bool on)
490 {
491     Q_D(QDeclarative1PaintedItem);
492     if (d->smoothCache != on) {
493         d->smoothCache = on;
494         clearCache();
495     }
496 }
497
498
499
500
501 QT_END_NAMESPACE