Remove "All rights reserved" line from license headers.
[profile/ivi/qtdeclarative.git] / src / qtquick1 / graphicsitems / qdeclarativeborderimage.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/qdeclarativeborderimage_p.h"
43 #include "QtQuick1/private/qdeclarativeborderimage_p_p.h"
44
45 #include <QtDeclarative/qdeclarativeinfo.h>
46 #include <QtDeclarative/private/qdeclarativeengine_p.h>
47
48 #include <QNetworkRequest>
49 #include <QNetworkReply>
50 #include <QFile>
51
52 #include <QtWidgets/qdrawutil.h>
53
54 QT_BEGIN_NAMESPACE
55
56
57
58 /*!
59     \qmlclass BorderImage QDeclarative1BorderImage
60     \inqmlmodule QtQuick 1
61     \brief The BorderImage element provides an image that can be used as a border.
62     \inherits Item
63     \since QtQuick 1.0
64     \ingroup qml-basic-visual-elements
65
66     The BorderImage element is used to create borders out of images by scaling or tiling
67     parts of each image.
68
69     A BorderImage element breaks a source image, specified using the \l url property,
70     into 9 regions, as shown below:
71
72     \image declarative-scalegrid.png
73
74     When the image is scaled, regions of the source image are scaled or tiled to
75     create the displayed border image in the following way:
76
77     \list
78     \i The corners (regions 1, 3, 7, and 9) are not scaled at all.
79     \i Regions 2 and 8 are scaled according to
80        \l{BorderImage::horizontalTileMode}{horizontalTileMode}.
81     \i Regions 4 and 6 are scaled according to
82        \l{BorderImage::verticalTileMode}{verticalTileMode}.
83     \i The middle (region 5) is scaled according to both
84        \l{BorderImage::horizontalTileMode}{horizontalTileMode} and
85        \l{BorderImage::verticalTileMode}{verticalTileMode}.
86     \endlist
87
88     The regions of the image are defined using the \l border property group, which
89     describes the distance from each edge of the source image to use as a border.
90
91     \section1 Example Usage
92
93     The following examples show the effects of the different modes on an image.
94     Guide lines are overlaid onto the image to show the different regions of the
95     image as described above.
96
97     \beginfloatleft
98     \image qml-borderimage-normal-image.png
99     \endfloat
100
101     An unscaled image is displayed using an Image element. The \l border property is
102     used to determine the parts of the image that will lie inside the unscaled corner
103     areas and the parts that will be stretched horizontally and vertically.
104
105     \snippet doc/src/snippets/qtquick1/borderimage/normal-image.qml normal image
106
107     \clearfloat
108     \beginfloatleft
109     \image qml-borderimage-scaled.png
110     \endfloat
111
112     A BorderImage element is used to display the image, and it is given a size that is
113     larger than the original image. Since the \l horizontalTileMode property is set to
114     \l{BorderImage::horizontalTileMode}{BorderImage.Stretch}, the parts of image in
115     regions 2 and 8 are stretched horizontally. Since the \l verticalTileMode property
116     is set to \l{BorderImage::verticalTileMode}{BorderImage.Stretch}, the parts of image
117     in regions 4 and 6 are stretched vertically.
118
119     \snippet doc/src/snippets/qtquick1/borderimage/borderimage-scaled.qml scaled border image
120
121     \clearfloat
122     \beginfloatleft
123     \image qml-borderimage-tiled.png
124     \endfloat
125
126     Again, a large BorderImage element is used to display the image. With the
127     \l horizontalTileMode property set to \l{BorderImage::horizontalTileMode}{BorderImage.Repeat},
128     the parts of image in regions 2 and 8 are tiled so that they fill the space at the
129     top and bottom of the element. Similarly, the \l verticalTileMode property is set to
130     \l{BorderImage::verticalTileMode}{BorderImage.Repeat}, the parts of image in regions
131     4 and 6 are tiled so that they fill the space at the left and right of the element.
132
133     \snippet doc/src/snippets/qtquick1/borderimage/borderimage-tiled.qml tiled border image
134
135     \clearfloat
136     In some situations, the width of regions 2 and 8 may not be an exact multiple of the width
137     of the corresponding regions in the source image. Similarly, the height of regions 4 and 6
138     may not be an exact multiple of the height of the corresponding regions. It can be useful
139     to use \l{BorderImage::horizontalTileMode}{BorderImage.Round} instead of
140     \l{BorderImage::horizontalTileMode}{BorderImage.Repeat} in cases like these.
141
142     The \l{declarative/imageelements/borderimage}{BorderImage example} shows how a BorderImage
143     can be used to simulate a shadow effect on a rectangular item.
144
145     \section1 Quality and Performance
146
147     By default, any scaled regions of the image are rendered without smoothing to improve
148     rendering speed. Setting the \l smooth property improves rendering quality of scaled
149     regions, but may slow down rendering.
150
151     The source image may not be loaded instantaneously, depending on its original location.
152     Loading progress can be monitored with the \l progress property.
153
154     \sa Image, AnimatedImage
155  */
156
157 /*!
158     \qmlproperty bool QtQuick1::BorderImage::asynchronous
159
160     Specifies that images on the local filesystem should be loaded
161     asynchronously in a separate thread.  The default value is
162     false, causing the user interface thread to block while the
163     image is loaded.  Setting \a asynchronous to true is useful where
164     maintaining a responsive user interface is more desirable
165     than having images immediately visible.
166
167     Note that this property is only valid for images read from the
168     local filesystem.  Images loaded via a network resource (e.g. HTTP)
169     are always loaded asynchonously.
170 */
171 QDeclarative1BorderImage::QDeclarative1BorderImage(QDeclarativeItem *parent)
172   : QDeclarative1ImageBase(*(new QDeclarative1BorderImagePrivate), parent)
173 {
174 }
175
176 QDeclarative1BorderImage::~QDeclarative1BorderImage()
177 {
178     Q_D(QDeclarative1BorderImage);
179     if (d->sciReply)
180         d->sciReply->deleteLater();
181 }
182 /*!
183     \qmlproperty enumeration QtQuick1::BorderImage::status
184
185     This property describes the status of image loading.  It can be one of:
186
187     \list
188     \o BorderImage.Null - no image has been set
189     \o BorderImage.Ready - the image has been loaded
190     \o BorderImage.Loading - the image is currently being loaded
191     \o BorderImage.Error - an error occurred while loading the image
192     \endlist
193
194     \sa progress
195 */
196
197 /*!
198     \qmlproperty real QtQuick1::BorderImage::progress
199
200     This property holds the progress of image loading, from 0.0 (nothing loaded)
201     to 1.0 (finished).
202
203     \sa status
204 */
205
206 /*!
207     \qmlproperty bool QtQuick1::BorderImage::smooth
208
209     Set this property if you want the image to be smoothly filtered when scaled or
210     transformed.  Smooth filtering gives better visual quality, but is slower.  If
211     the image is displayed at its natural size, this property has no visual or
212     performance effect.
213
214     By default, this property is set to false.
215
216     \note Generally scaling artifacts are only visible if the image is stationary on
217     the screen.  A common pattern when animating an image is to disable smooth
218     filtering at the beginning of the animation and enable it at the conclusion.
219 */
220
221 /*!
222     \qmlproperty bool QtQuick1::BorderImage::cache
223     \since Quick 1.1
224
225     Specifies whether the image should be cached. The default value is
226     true. Setting \a cache to false is useful when dealing with large images,
227     to make sure that they aren't cached at the expense of small 'ui element' images.
228 */
229
230 /*!
231     \qmlproperty bool QtQuick1::BorderImage::mirror
232     \since Quick 1.1
233
234     This property holds whether the image should be horizontally inverted
235     (effectively displaying a mirrored image).
236
237     The default value is false.
238 */
239
240 /*!
241     \qmlproperty url QtQuick1::BorderImage::source
242
243     This property holds the URL that refers to the source image.
244
245     BorderImage can handle any image format supported by Qt, loaded from any
246     URL scheme supported by Qt.
247
248     This property can also be used to refer to .sci files, which are
249     written in a QML-specific, text-based format that specifies the
250     borders, the image file and the tile rules for a given border image.
251
252     The following .sci file sets the borders to 10 on each side for the
253     image \c picture.png:
254
255     \code
256     border.left: 10
257     border.top: 10
258     border.bottom: 10
259     border.right: 10
260     source: "picture.png"
261     \endcode
262
263     The URL may be absolute, or relative to the URL of the component.
264
265     \sa QDeclarativeImageProvider
266 */
267
268 /*!
269     \qmlproperty QSize QtQuick1::BorderImage::sourceSize
270
271     This property holds the actual width and height of the loaded image.
272
273     In BorderImage, this property is read-only.
274
275     \sa Image::sourceSize
276 */
277 void QDeclarative1BorderImage::setSource(const QUrl &url)
278 {
279     Q_D(QDeclarative1BorderImage);
280     //equality is fairly expensive, so we bypass for simple, common case
281     if ((d->url.isEmpty() == url.isEmpty()) && url == d->url)
282         return;
283
284     if (d->sciReply) {
285         d->sciReply->deleteLater();
286         d->sciReply = 0;
287     }
288
289     d->url = url;
290     d->sciurl = QUrl();
291     emit sourceChanged(d->url);
292
293     if (isComponentComplete())
294         load();
295 }
296
297 void QDeclarative1BorderImage::load()
298 {
299     Q_D(QDeclarative1BorderImage);
300     if (d->progress != 0.0) {
301         d->progress = 0.0;
302         emit progressChanged(d->progress);
303     }
304
305     if (d->url.isEmpty()) {
306         d->pix.clear(this);
307         d->status = Null;
308         setImplicitWidth(0);
309         setImplicitHeight(0);
310         emit statusChanged(d->status);
311         update();
312     } else {
313         d->status = Loading;
314         if (d->url.path().endsWith(QLatin1String("sci"))) {
315 #ifndef QT_NO_LOCALFILE_OPTIMIZED_QML
316             QString lf = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(d->url);
317             if (!lf.isEmpty()) {
318                 QFile file(lf);
319                 file.open(QIODevice::ReadOnly);
320                 setGridScaledImage(QDeclarative1GridScaledImage(&file));
321             } else
322 #endif
323             {
324                 QNetworkRequest req(d->url);
325                 d->sciReply = qmlEngine(this)->networkAccessManager()->get(req);
326
327                 static int sciReplyFinished = -1;
328                 static int thisSciRequestFinished = -1;
329                 if (sciReplyFinished == -1) {
330                     sciReplyFinished =
331                         QNetworkReply::staticMetaObject.indexOfSignal("finished()");
332                     thisSciRequestFinished =
333                         QDeclarative1BorderImage::staticMetaObject.indexOfSlot("sciRequestFinished()");
334                 }
335
336                 QMetaObject::connect(d->sciReply, sciReplyFinished, this,
337                                      thisSciRequestFinished, Qt::DirectConnection);
338             }
339         } else {
340
341             QDeclarative1Pixmap::Options options;
342             if (d->async)
343                 options |= QDeclarative1Pixmap::Asynchronous;
344             if (d->cache)
345                 options |= QDeclarative1Pixmap::Cache;
346             d->pix.clear(this);
347             d->pix.load(qmlEngine(this), d->url, options);
348
349             if (d->pix.isLoading()) {
350                 d->pix.connectFinished(this, SLOT(requestFinished()));
351                 d->pix.connectDownloadProgress(this, SLOT(requestProgress(qint64,qint64)));
352             } else {
353                 QSize impsize = d->pix.implicitSize();
354                 setImplicitWidth(impsize.width());
355                 setImplicitHeight(impsize.height());
356
357                 if (d->pix.isReady()) {
358                     d->status = Ready;
359                 } else {
360                     d->status = Error;
361                     qmlInfo(this) << d->pix.error();
362                 }
363
364                 d->progress = 1.0;
365                 emit statusChanged(d->status);
366                 emit progressChanged(d->progress);
367                 requestFinished();
368                 update();
369             }
370         }
371     }
372
373     emit statusChanged(d->status);
374 }
375
376 /*!
377     \qmlproperty int QtQuick1::BorderImage::border.left
378     \qmlproperty int QtQuick1::BorderImage::border.right
379     \qmlproperty int QtQuick1::BorderImage::border.top
380     \qmlproperty int QtQuick1::BorderImage::border.bottom
381
382     The 4 border lines (2 horizontal and 2 vertical) break the image into 9 sections,
383     as shown below:
384
385     \image declarative-scalegrid.png
386
387     Each border line (left, right, top, and bottom) specifies an offset in pixels
388     from the respective edge of the source image. By default, each border line has
389     a value of 0.
390
391     For example, the following definition sets the bottom line 10 pixels up from
392     the bottom of the image:
393
394     \qml
395     BorderImage {
396         border.bottom: 10
397         // ...
398     }
399     \endqml
400
401     The border lines can also be specified using a
402     \l {BorderImage::source}{.sci file}.
403 */
404
405 QDeclarative1ScaleGrid *QDeclarative1BorderImage::border()
406 {
407     Q_D(QDeclarative1BorderImage);
408     return d->getScaleGrid();
409 }
410
411 /*!
412     \qmlproperty enumeration QtQuick1::BorderImage::horizontalTileMode
413     \qmlproperty enumeration QtQuick1::BorderImage::verticalTileMode
414
415     This property describes how to repeat or stretch the middle parts of the border image.
416
417     \list
418     \o BorderImage.Stretch - Scales the image to fit to the available area.
419     \o BorderImage.Repeat - Tile the image until there is no more space. May crop the last image.
420     \o BorderImage.Round - Like Repeat, but scales the images down to ensure that the last image is not cropped.
421     \endlist
422
423     The default tile mode for each property is BorderImage.Stretch.
424 */
425 QDeclarative1BorderImage::TileMode QDeclarative1BorderImage::horizontalTileMode() const
426 {
427     Q_D(const QDeclarative1BorderImage);
428     return d->horizontalTileMode;
429 }
430
431 void QDeclarative1BorderImage::setHorizontalTileMode(TileMode t)
432 {
433     Q_D(QDeclarative1BorderImage);
434     if (t != d->horizontalTileMode) {
435         d->horizontalTileMode = t;
436         emit horizontalTileModeChanged();
437         update();
438     }
439 }
440
441 QDeclarative1BorderImage::TileMode QDeclarative1BorderImage::verticalTileMode() const
442 {
443     Q_D(const QDeclarative1BorderImage);
444     return d->verticalTileMode;
445 }
446
447 void QDeclarative1BorderImage::setVerticalTileMode(TileMode t)
448 {
449     Q_D(QDeclarative1BorderImage);
450     if (t != d->verticalTileMode) {
451         d->verticalTileMode = t;
452         emit verticalTileModeChanged();
453         update();
454     }
455 }
456
457 void QDeclarative1BorderImage::setGridScaledImage(const QDeclarative1GridScaledImage& sci)
458 {
459     Q_D(QDeclarative1BorderImage);
460     if (!sci.isValid()) {
461         d->status = Error;
462         emit statusChanged(d->status);
463     } else {
464         QDeclarative1ScaleGrid *sg = border();
465         sg->setTop(sci.gridTop());
466         sg->setBottom(sci.gridBottom());
467         sg->setLeft(sci.gridLeft());
468         sg->setRight(sci.gridRight());
469         d->horizontalTileMode = sci.horizontalTileRule();
470         d->verticalTileMode = sci.verticalTileRule();
471
472         d->sciurl = d->url.resolved(QUrl(sci.pixmapUrl()));
473
474         QDeclarative1Pixmap::Options options;
475         if (d->async)
476             options |= QDeclarative1Pixmap::Asynchronous;
477         if (d->cache)
478             options |= QDeclarative1Pixmap::Cache;
479         d->pix.clear(this);
480         d->pix.load(qmlEngine(this), d->sciurl, options);
481
482         if (d->pix.isLoading()) {
483             static int thisRequestProgress = -1;
484             static int thisRequestFinished = -1;
485             if (thisRequestProgress == -1) {
486                 thisRequestProgress =
487                     QDeclarative1BorderImage::staticMetaObject.indexOfSlot("requestProgress(qint64,qint64)");
488                 thisRequestFinished =
489                     QDeclarative1BorderImage::staticMetaObject.indexOfSlot("requestFinished()");
490             }
491
492             d->pix.connectFinished(this, thisRequestFinished);
493             d->pix.connectDownloadProgress(this, thisRequestProgress);
494
495         } else {
496
497             QSize impsize = d->pix.implicitSize();
498             setImplicitWidth(impsize.width());
499             setImplicitHeight(impsize.height());
500
501             if (d->pix.isReady()) {
502                 d->status = Ready;
503             } else {
504                 d->status = Error;
505                 qmlInfo(this) << d->pix.error();
506             }
507
508             d->progress = 1.0;
509             emit statusChanged(d->status);
510             emit progressChanged(1.0);
511             update();
512
513         }
514     }
515 }
516
517 void QDeclarative1BorderImage::requestFinished()
518 {
519     Q_D(QDeclarative1BorderImage);
520
521     QSize impsize = d->pix.implicitSize();
522     if (d->pix.isError()) {
523         d->status = Error;
524         qmlInfo(this) << d->pix.error();
525     } else {
526         d->status = Ready;
527     }
528
529     setImplicitWidth(impsize.width());
530     setImplicitHeight(impsize.height());
531
532     if (d->sourcesize.width() != d->pix.width() || d->sourcesize.height() != d->pix.height())
533         emit sourceSizeChanged();
534
535     d->progress = 1.0;
536     emit statusChanged(d->status);
537     emit progressChanged(1.0);
538     update();
539 }
540
541 #define BORDERIMAGE_MAX_REDIRECT 16
542
543 void QDeclarative1BorderImage::sciRequestFinished()
544 {
545     Q_D(QDeclarative1BorderImage);
546
547     d->redirectCount++;
548     if (d->redirectCount < BORDERIMAGE_MAX_REDIRECT) {
549         QVariant redirect = d->sciReply->attribute(QNetworkRequest::RedirectionTargetAttribute);
550         if (redirect.isValid()) {
551             QUrl url = d->sciReply->url().resolved(redirect.toUrl());
552             setSource(url);
553             return;
554         }
555     }
556     d->redirectCount=0;
557
558     if (d->sciReply->error() != QNetworkReply::NoError) {
559         d->status = Error;
560         d->sciReply->deleteLater();
561         d->sciReply = 0;
562         emit statusChanged(d->status);
563     } else {
564         QDeclarative1GridScaledImage sci(d->sciReply);
565         d->sciReply->deleteLater();
566         d->sciReply = 0;
567         setGridScaledImage(sci);
568     }
569 }
570
571 void QDeclarative1BorderImage::doUpdate()
572 {
573     update();
574 }
575
576 void QDeclarative1BorderImage::paint(QPainter *p, const QStyleOptionGraphicsItem *, QWidget *)
577 {
578     Q_D(QDeclarative1BorderImage);
579     if (d->pix.isNull() || d->width() <= 0.0 || d->height() <= 0.0)
580         return;
581
582     bool oldAA = p->testRenderHint(QPainter::Antialiasing);
583     bool oldSmooth = p->testRenderHint(QPainter::SmoothPixmapTransform);
584     QTransform oldTransform;
585     if (d->smooth)
586         p->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform, d->smooth);
587     if (d->mirror) {
588         oldTransform = p->transform();
589         QTransform mirror;
590         mirror.translate(d->width(), 0).scale(-1, 1.0);
591         p->setWorldTransform(mirror * oldTransform);
592     }
593
594     const QDeclarative1ScaleGrid *border = d->getScaleGrid();
595     int left = border->left();
596     int right = border->right();
597     qreal borderWidth = left + right;
598     if (borderWidth > 0.0 && d->width() < borderWidth) {
599         qreal diff = borderWidth - d->width() - 1;
600         left -= qRound(diff * qreal(left) / borderWidth);
601         right -= qRound(diff * qreal(right) / borderWidth);
602     }
603     int top = border->top();
604     int bottom = border->bottom();
605     qreal borderHeight = top + bottom;
606     if (borderHeight > 0.0 && d->height() < borderHeight) {
607         qreal diff = borderHeight - d->height() - 1;
608         top -= qRound(diff * qreal(top) / borderHeight);
609         bottom -= qRound(diff * qreal(bottom) / borderHeight);
610     }
611     QMargins margins(left, top, right, bottom);
612     QTileRules rules((Qt::TileRule)d->horizontalTileMode, (Qt::TileRule)d->verticalTileMode);
613     qDrawBorderPixmap(p, QRect(0, 0, (int)d->width(), (int)d->height()), margins, d->pix, d->pix.rect(), margins, rules);
614     if (d->smooth) {
615         p->setRenderHint(QPainter::Antialiasing, oldAA);
616         p->setRenderHint(QPainter::SmoothPixmapTransform, oldSmooth);
617     }
618     if (d->mirror)
619         p->setWorldTransform(oldTransform);
620 }
621
622
623
624 QT_END_NAMESPACE