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/qdeclarativeborderimage_p.h"
43 #include "QtQuick1/private/qdeclarativeborderimage_p_p.h"
45 #include <QtDeclarative/qdeclarativeinfo.h>
46 #include <QtDeclarative/private/qdeclarativeengine_p.h>
48 #include <QNetworkRequest>
49 #include <QNetworkReply>
52 #include <QtWidgets/qdrawutil.h>
59 \qmlclass BorderImage QDeclarative1BorderImage
60 \inqmlmodule QtQuick 1
61 \brief The BorderImage element provides an image that can be used as a border.
64 \ingroup qml-basic-visual-elements
66 The BorderImage element is used to create borders out of images by scaling or tiling
69 A BorderImage element breaks a source image, specified using the \l url property,
70 into 9 regions, as shown below:
72 \image declarative-scalegrid.png
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:
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}.
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.
91 \section1 Example Usage
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.
98 \image qml-borderimage-normal-image.png
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.
105 \snippet doc/src/snippets/qtquick1/borderimage/normal-image.qml normal image
109 \image qml-borderimage-scaled.png
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.
119 \snippet doc/src/snippets/qtquick1/borderimage/borderimage-scaled.qml scaled border image
123 \image qml-borderimage-tiled.png
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.
133 \snippet doc/src/snippets/qtquick1/borderimage/borderimage-tiled.qml tiled border image
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.
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.
145 \section1 Quality and Performance
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.
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.
154 \sa Image, AnimatedImage
158 \qmlproperty bool QtQuick1::BorderImage::asynchronous
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.
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.
171 QDeclarative1BorderImage::QDeclarative1BorderImage(QDeclarativeItem *parent)
172 : QDeclarative1ImageBase(*(new QDeclarative1BorderImagePrivate), parent)
176 QDeclarative1BorderImage::~QDeclarative1BorderImage()
178 Q_D(QDeclarative1BorderImage);
180 d->sciReply->deleteLater();
183 \qmlproperty enumeration QtQuick1::BorderImage::status
185 This property describes the status of image loading. It can be one of:
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
198 \qmlproperty real QtQuick1::BorderImage::progress
200 This property holds the progress of image loading, from 0.0 (nothing loaded)
207 \qmlproperty bool QtQuick1::BorderImage::smooth
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
214 By default, this property is set to false.
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.
222 \qmlproperty bool QtQuick1::BorderImage::cache
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.
231 \qmlproperty bool QtQuick1::BorderImage::mirror
234 This property holds whether the image should be horizontally inverted
235 (effectively displaying a mirrored image).
237 The default value is false.
241 \qmlproperty url QtQuick1::BorderImage::source
243 This property holds the URL that refers to the source image.
245 BorderImage can handle any image format supported by Qt, loaded from any
246 URL scheme supported by Qt.
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.
252 The following .sci file sets the borders to 10 on each side for the
253 image \c picture.png:
260 source: "picture.png"
263 The URL may be absolute, or relative to the URL of the component.
265 \sa QDeclarativeImageProvider
269 \qmlproperty QSize QtQuick1::BorderImage::sourceSize
271 This property holds the actual width and height of the loaded image.
273 In BorderImage, this property is read-only.
275 \sa Image::sourceSize
277 void QDeclarative1BorderImage::setSource(const QUrl &url)
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)
285 d->sciReply->deleteLater();
291 emit sourceChanged(d->url);
293 if (isComponentComplete())
297 void QDeclarative1BorderImage::load()
299 Q_D(QDeclarative1BorderImage);
300 if (d->progress != 0.0) {
302 emit progressChanged(d->progress);
305 if (d->url.isEmpty()) {
309 setImplicitHeight(0);
310 emit statusChanged(d->status);
314 if (d->url.path().endsWith(QLatin1String("sci"))) {
315 #ifndef QT_NO_LOCALFILE_OPTIMIZED_QML
316 QString lf = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(d->url);
319 file.open(QIODevice::ReadOnly);
320 setGridScaledImage(QDeclarative1GridScaledImage(&file));
324 QNetworkRequest req(d->url);
325 d->sciReply = qmlEngine(this)->networkAccessManager()->get(req);
327 static int sciReplyFinished = -1;
328 static int thisSciRequestFinished = -1;
329 if (sciReplyFinished == -1) {
331 QNetworkReply::staticMetaObject.indexOfSignal("finished()");
332 thisSciRequestFinished =
333 QDeclarative1BorderImage::staticMetaObject.indexOfSlot("sciRequestFinished()");
336 QMetaObject::connect(d->sciReply, sciReplyFinished, this,
337 thisSciRequestFinished, Qt::DirectConnection);
341 QDeclarative1Pixmap::Options options;
343 options |= QDeclarative1Pixmap::Asynchronous;
345 options |= QDeclarative1Pixmap::Cache;
347 d->pix.load(qmlEngine(this), d->url, options);
349 if (d->pix.isLoading()) {
350 d->pix.connectFinished(this, SLOT(requestFinished()));
351 d->pix.connectDownloadProgress(this, SLOT(requestProgress(qint64,qint64)));
353 QSize impsize = d->pix.implicitSize();
354 setImplicitWidth(impsize.width());
355 setImplicitHeight(impsize.height());
357 if (d->pix.isReady()) {
361 qmlInfo(this) << d->pix.error();
365 emit statusChanged(d->status);
366 emit progressChanged(d->progress);
373 emit statusChanged(d->status);
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
382 The 4 border lines (2 horizontal and 2 vertical) break the image into 9 sections,
385 \image declarative-scalegrid.png
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
391 For example, the following definition sets the bottom line 10 pixels up from
392 the bottom of the image:
401 The border lines can also be specified using a
402 \l {BorderImage::source}{.sci file}.
405 QDeclarative1ScaleGrid *QDeclarative1BorderImage::border()
407 Q_D(QDeclarative1BorderImage);
408 return d->getScaleGrid();
412 \qmlproperty enumeration QtQuick1::BorderImage::horizontalTileMode
413 \qmlproperty enumeration QtQuick1::BorderImage::verticalTileMode
415 This property describes how to repeat or stretch the middle parts of the border image.
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.
423 The default tile mode for each property is BorderImage.Stretch.
425 QDeclarative1BorderImage::TileMode QDeclarative1BorderImage::horizontalTileMode() const
427 Q_D(const QDeclarative1BorderImage);
428 return d->horizontalTileMode;
431 void QDeclarative1BorderImage::setHorizontalTileMode(TileMode t)
433 Q_D(QDeclarative1BorderImage);
434 if (t != d->horizontalTileMode) {
435 d->horizontalTileMode = t;
436 emit horizontalTileModeChanged();
441 QDeclarative1BorderImage::TileMode QDeclarative1BorderImage::verticalTileMode() const
443 Q_D(const QDeclarative1BorderImage);
444 return d->verticalTileMode;
447 void QDeclarative1BorderImage::setVerticalTileMode(TileMode t)
449 Q_D(QDeclarative1BorderImage);
450 if (t != d->verticalTileMode) {
451 d->verticalTileMode = t;
452 emit verticalTileModeChanged();
457 void QDeclarative1BorderImage::setGridScaledImage(const QDeclarative1GridScaledImage& sci)
459 Q_D(QDeclarative1BorderImage);
460 if (!sci.isValid()) {
462 emit statusChanged(d->status);
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();
472 d->sciurl = d->url.resolved(QUrl(sci.pixmapUrl()));
474 QDeclarative1Pixmap::Options options;
476 options |= QDeclarative1Pixmap::Asynchronous;
478 options |= QDeclarative1Pixmap::Cache;
480 d->pix.load(qmlEngine(this), d->sciurl, options);
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()");
492 d->pix.connectFinished(this, thisRequestFinished);
493 d->pix.connectDownloadProgress(this, thisRequestProgress);
497 QSize impsize = d->pix.implicitSize();
498 setImplicitWidth(impsize.width());
499 setImplicitHeight(impsize.height());
501 if (d->pix.isReady()) {
505 qmlInfo(this) << d->pix.error();
509 emit statusChanged(d->status);
510 emit progressChanged(1.0);
517 void QDeclarative1BorderImage::requestFinished()
519 Q_D(QDeclarative1BorderImage);
521 QSize impsize = d->pix.implicitSize();
522 if (d->pix.isError()) {
524 qmlInfo(this) << d->pix.error();
529 setImplicitWidth(impsize.width());
530 setImplicitHeight(impsize.height());
532 if (d->sourcesize.width() != d->pix.width() || d->sourcesize.height() != d->pix.height())
533 emit sourceSizeChanged();
536 emit statusChanged(d->status);
537 emit progressChanged(1.0);
541 #define BORDERIMAGE_MAX_REDIRECT 16
543 void QDeclarative1BorderImage::sciRequestFinished()
545 Q_D(QDeclarative1BorderImage);
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());
558 if (d->sciReply->error() != QNetworkReply::NoError) {
560 d->sciReply->deleteLater();
562 emit statusChanged(d->status);
564 QDeclarative1GridScaledImage sci(d->sciReply);
565 d->sciReply->deleteLater();
567 setGridScaledImage(sci);
571 void QDeclarative1BorderImage::doUpdate()
576 void QDeclarative1BorderImage::paint(QPainter *p, const QStyleOptionGraphicsItem *, QWidget *)
578 Q_D(QDeclarative1BorderImage);
579 if (d->pix.isNull() || d->width() <= 0.0 || d->height() <= 0.0)
582 bool oldAA = p->testRenderHint(QPainter::Antialiasing);
583 bool oldSmooth = p->testRenderHint(QPainter::SmoothPixmapTransform);
584 QTransform oldTransform;
586 p->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform, d->smooth);
588 oldTransform = p->transform();
590 mirror.translate(d->width(), 0).scale(-1, 1.0);
591 p->setWorldTransform(mirror * oldTransform);
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);
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);
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);
615 p->setRenderHint(QPainter::Antialiasing, oldAA);
616 p->setRenderHint(QPainter::SmoothPixmapTransform, oldSmooth);
619 p->setWorldTransform(oldTransform);