1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: http://www.qt-project.org/
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
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.
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.
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.
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.
40 ****************************************************************************/
42 #include "QtQuick1/private/qdeclarativepainteditem_p.h"
43 #include "QtQuick1/private/qdeclarativepainteditem_p_p.h"
48 #include <QApplication>
49 #include <QGraphicsSceneMouseEvent>
51 #include <QPaintEngine>
59 \class QDeclarative1PaintedItem
60 \brief The QDeclarative1PaintedItem class is an abstract base class for QDeclarative1View items that want cached painting.
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.
68 To subclass QDeclarative1PaintedItem, you must reimplement drawContents() to draw
69 the contents of the item.
73 \fn void QDeclarative1PaintedItem::drawContents(QPainter *painter, const QRect &rect)
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.
82 \property QDeclarative1PaintedItem::contentsSize
83 \brief The size of the contents
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().
90 // XXX bug in WebKit - can call repaintRequested and other cache-changing functions from within render!
92 static int inpaint_clearcache=0;
94 extern Q_GUI_EXPORT bool qt_applefontsmoothing_enabled;
97 Marks areas of the cache that intersect with the given \a rect as dirty and
98 in need of being refreshed.
102 void QDeclarative1PaintedItem::dirtyCache(const QRect& rect)
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);
122 Marks the entirety of the contents cache as dirty.
126 void QDeclarative1PaintedItem::clearCache()
129 inpaint_clearcache=1;
132 Q_D(QDeclarative1PaintedItem);
133 qDeleteAll(d->imagecache);
134 d->imagecache.clear();
138 Returns the size of the contents.
140 \sa setContentsSize()
142 QSize QDeclarative1PaintedItem::contentsSize() const
144 Q_D(const QDeclarative1PaintedItem);
145 return d->contentsSize;
149 Sets the size of the contents to the given \a size.
153 void QDeclarative1PaintedItem::setContentsSize(const QSize &size)
155 Q_D(QDeclarative1PaintedItem);
156 if (d->contentsSize == size) return;
157 prepareGeometryChange();
158 d->contentsSize = size;
161 emit contentsSizeChanged();
164 qreal QDeclarative1PaintedItem::contentsScale() const
166 Q_D(const QDeclarative1PaintedItem);
167 return d->contentsScale;
170 void QDeclarative1PaintedItem::setContentsScale(qreal scale)
172 Q_D(QDeclarative1PaintedItem);
173 if (d->contentsScale == scale) return;
174 d->contentsScale = scale;
177 emit contentsScaleChanged();
182 Constructs a new QDeclarative1PaintedItem with the given \a parent.
184 QDeclarative1PaintedItem::QDeclarative1PaintedItem(QDeclarativeItem *parent)
185 : QDeclarativeItem(*(new QDeclarative1PaintedItemPrivate), parent)
191 Constructs a new QDeclarative1PaintedItem with the given \a parent and
192 initialized private data member \a dd.
194 QDeclarative1PaintedItem::QDeclarative1PaintedItem(QDeclarative1PaintedItemPrivate &dd, QDeclarativeItem *parent)
195 : QDeclarativeItem(dd, parent)
200 Destroys the image item.
202 QDeclarative1PaintedItem::~QDeclarative1PaintedItem()
207 void QDeclarative1PaintedItem::geometryChanged(const QRectF &newGeometry,
208 const QRectF &oldGeometry)
210 if (newGeometry.width() != oldGeometry.width() ||
211 newGeometry.height() != oldGeometry.height())
214 QDeclarativeItem::geometryChanged(newGeometry, oldGeometry);
217 QVariant QDeclarative1PaintedItem::itemChange(GraphicsItemChange change,
218 const QVariant &value)
220 if (change == ItemVisibleHasChanged)
223 return QDeclarativeItem::itemChange(change, value);
226 void QDeclarative1PaintedItem::setCacheFrozen(bool frozen)
228 Q_D(QDeclarative1PaintedItem);
229 if (d->cachefrozen == frozen)
231 d->cachefrozen = frozen;
235 QRectF QDeclarative1PaintedItem::boundingRect() const
237 Q_D(const QDeclarative1PaintedItem);
239 QSizeF sz = d->contentsSize * d->contentsScale;
242 qreal h = d->mHeight;
245 return QRectF(0.0,0.0,w,h);
251 void QDeclarative1PaintedItem::paint(QPainter *p, const QStyleOptionGraphicsItem *, QWidget *)
253 Q_D(QDeclarative1PaintedItem);
254 const QRect content = boundingRect().toRect();
255 if (content.width() <= 0 || content.height() <= 0)
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);
268 // do not make complicated regions...
269 effectiveClip = xinv.mapRect(sysClip.boundingRect());
272 QRegion topaint = p->clipRegion();
273 if (topaint.isEmpty()) {
274 if (effectiveClip.isEmpty())
275 topaint = QRect(0,0,p->device()->width(),p->device()->height());
277 topaint = effectiveClip;
278 } else if (!effectiveClip.isEmpty()) {
279 topaint &= effectiveClip;
283 QRegion uncached(content);
284 p->setRenderHints(QPainter::SmoothPixmapTransform, d->smooth);
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)) {
294 bool oldSmooth = qt_applefontsmoothing_enabled;
295 qt_applefontsmoothing_enabled = false;
297 QPainter qp(&d->imagecache[i]->image);
299 qt_applefontsmoothing_enabled = oldSmooth;
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);
318 qp.fillRect(sclip,d->fillColor);
321 drawContents(&qp, sclip);
322 d->imagecache[i]->dirty = QRect();
325 p->drawPixmap(target.toRect(), d->imagecache[i]->image);
327 d->imagecache[i]->age=0;
329 d->imagecache[i]->age++;
331 cachesize += area.width()*area.height();
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) {
343 for (int i=0; i<d->imagecache.count(); ++i) {
344 int a = d->imagecache[i]->age;
350 cachesize -= d->imagecache[oldest]->area.width()*d->imagecache[oldest]->area.height();
351 uncached += d->imagecache[oldest]->area;
352 delete d->imagecache.takeAt(oldest);
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);
363 bool oldSmooth = qt_applefontsmoothing_enabled;
364 qt_applefontsmoothing_enabled = false;
368 qt_applefontsmoothing_enabled = oldSmooth;
370 qp.setRenderHints(QPainter::HighQualityAntialiasing | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform, d->smoothCache);
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);
380 QDeclarative1PaintedItemPrivate::ImageCacheItem *newitem = new QDeclarative1PaintedItemPrivate::ImageCacheItem;
382 newitem->image = img;
383 d->imagecache.append(newitem);
384 p->drawPixmap(r, newitem->image);
387 const QVector<QRect> rects = uncached.rects();
388 for (int i = 0; i < rects.count(); ++i)
389 p->fillRect(rects.at(i), Qt::lightGray);
393 if (inpaint_clearcache) {
395 inpaint_clearcache = 0;
402 \qmlproperty int QtQuick1::PaintedItem::pixelCacheSize
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.
409 \property QDeclarative1PaintedItem::pixelCacheSize
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.
415 int QDeclarative1PaintedItem::pixelCacheSize() const
417 Q_D(const QDeclarative1PaintedItem);
418 return d->max_imagecache_size;
421 void QDeclarative1PaintedItem::setPixelCacheSize(int pixels)
423 Q_D(QDeclarative1PaintedItem);
424 if (pixels < d->max_imagecache_size) {
426 for (int i=0; i<d->imagecache.count(); ++i) {
427 QRect area = d->imagecache[i]->area;
428 cachesize += area.width()*area.height();
430 while (d->imagecache.count() && cachesize > pixels) {
433 for (int i=0; i<d->imagecache.count(); ++i) {
434 int a = d->imagecache[i]->age;
440 cachesize -= d->imagecache[oldest]->area.width()*d->imagecache[oldest]->area.height();
441 delete d->imagecache.takeAt(oldest);
444 d->max_imagecache_size = pixels;
450 \property QDeclarative1PaintedItem::fillColor
452 The color to be used to fill the item prior to calling drawContents().
453 By default, this is Qt::transparent.
455 Performance improvements can be achieved if subclasses call this with either an
456 invalid color (QColor()), or an appropriate solid color.
458 void QDeclarative1PaintedItem::setFillColor(const QColor& c)
460 Q_D(QDeclarative1PaintedItem);
461 if (d->fillColor == c)
464 emit fillColorChanged();
468 QColor QDeclarative1PaintedItem::fillColor() const
470 Q_D(const QDeclarative1PaintedItem);
475 \qmlproperty bool QtQuick1::PaintedItem::smoothCache
477 Controls whether the cached tiles of which the item is composed are
478 rendered smoothly when they are generated.
480 This is in addition toe Item::smooth, which controls the smooth painting of
481 the already-painted cached tiles under transformation.
483 bool QDeclarative1PaintedItem::smoothCache() const
485 Q_D(const QDeclarative1PaintedItem);
486 return d->smoothCache;
489 void QDeclarative1PaintedItem::setSmoothCache(bool on)
491 Q_D(QDeclarative1PaintedItem);
492 if (d->smoothCache != on) {