source: trunk/src/declarative/graphicsitems/qdeclarativeborderimage.cpp

Last change on this file was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 18.4 KB
Line 
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 QtDeclarative module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at qt-info@nokia.com.
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "private/qdeclarativeborderimage_p.h"
43#include "private/qdeclarativeborderimage_p_p.h"
44
45#include <qdeclarativeinfo.h>
46#include <private/qdeclarativeengine_p.h>
47
48#include <QNetworkRequest>
49#include <QNetworkReply>
50#include <QFile>
51
52QT_BEGIN_NAMESPACE
53
54/*!
55 \qmlclass BorderImage QDeclarativeBorderImage
56 \brief The BorderImage element provides an image that can be used as a border.
57 \inherits Item
58 \since 4.7
59 \ingroup qml-basic-visual-elements
60
61 The BorderImage element is used to create borders out of images by scaling or tiling
62 parts of each image.
63
64 A BorderImage element breaks a source image, specified using the \l url property,
65 into 9 regions, as shown below:
66
67 \image declarative-scalegrid.png
68
69 When the image is scaled, regions of the source image are scaled or tiled to
70 create the displayed border image in the following way:
71
72 \list
73 \i The corners (regions 1, 3, 7, and 9) are not scaled at all.
74 \i Regions 2 and 8 are scaled according to
75 \l{BorderImage::horizontalTileMode}{horizontalTileMode}.
76 \i Regions 4 and 6 are scaled according to
77 \l{BorderImage::verticalTileMode}{verticalTileMode}.
78 \i The middle (region 5) is scaled according to both
79 \l{BorderImage::horizontalTileMode}{horizontalTileMode} and
80 \l{BorderImage::verticalTileMode}{verticalTileMode}.
81 \endlist
82
83 The regions of the image are defined using the \l border property group, which
84 describes the distance from each edge of the source image to use as a border.
85
86 \section1 Example Usage
87
88 The following examples show the effects of the different modes on an image.
89 Guide lines are overlaid onto the image to show the different regions of the
90 image as described above.
91
92 \beginfloatleft
93 \image qml-borderimage-normal-image.png
94 \endfloat
95
96 An unscaled image is displayed using an Image element. The \l border property is
97 used to determine the parts of the image that will lie inside the unscaled corner
98 areas and the parts that will be stretched horizontally and vertically.
99
100 \snippet doc/src/snippets/declarative/borderimage/normal-image.qml normal image
101
102 \clearfloat
103 \beginfloatleft
104 \image qml-borderimage-scaled.png
105 \endfloat
106
107 A BorderImage element is used to display the image, and it is given a size that is
108 larger than the original image. Since the \l horizontalTileMode property is set to
109 \l{BorderImage::horizontalTileMode}{BorderImage.Stretch}, the parts of image in
110 regions 2 and 8 are stretched horizontally. Since the \l verticalTileMode property
111 is set to \l{BorderImage::verticalTileMode}{BorderImage.Stretch}, the parts of image
112 in regions 4 and 6 are stretched vertically.
113
114 \snippet doc/src/snippets/declarative/borderimage/borderimage-scaled.qml scaled border image
115
116 \clearfloat
117 \beginfloatleft
118 \image qml-borderimage-tiled.png
119 \endfloat
120
121 Again, a large BorderImage element is used to display the image. With the
122 \l horizontalTileMode property set to \l{BorderImage::horizontalTileMode}{BorderImage.Repeat},
123 the parts of image in regions 2 and 8 are tiled so that they fill the space at the
124 top and bottom of the element. Similarly, the \l verticalTileMode property is set to
125 \l{BorderImage::verticalTileMode}{BorderImage.Repeat}, the parts of image in regions
126 4 and 6 are tiled so that they fill the space at the left and right of the element.
127
128 \snippet doc/src/snippets/declarative/borderimage/borderimage-tiled.qml tiled border image
129
130 \clearfloat
131 In some situations, the width of regions 2 and 8 may not be an exact multiple of the width
132 of the corresponding regions in the source image. Similarly, the height of regions 4 and 6
133 may not be an exact multiple of the height of the corresponding regions. It can be useful
134 to use \l{BorderImage::horizontalTileMode}{BorderImage.Round} instead of
135 \l{BorderImage::horizontalTileMode}{BorderImage.Repeat} in cases like these.
136
137 The \l{declarative/imageelements/borderimage}{BorderImage example} shows how a BorderImage
138 can be used to simulate a shadow effect on a rectangular item.
139
140 \section1 Quality and Performance
141
142 By default, any scaled regions of the image are rendered without smoothing to improve
143 rendering speed. Setting the \l smooth property improves rendering quality of scaled
144 regions, but may slow down rendering.
145
146 The source image may not be loaded instantaneously, depending on its original location.
147 Loading progress can be monitored with the \l progress property.
148
149 \sa Image, AnimatedImage
150 */
151
152QDeclarativeBorderImage::QDeclarativeBorderImage(QDeclarativeItem *parent)
153 : QDeclarativeImageBase(*(new QDeclarativeBorderImagePrivate), parent)
154{
155}
156
157QDeclarativeBorderImage::~QDeclarativeBorderImage()
158{
159 Q_D(QDeclarativeBorderImage);
160 if (d->sciReply)
161 d->sciReply->deleteLater();
162}
163/*!
164 \qmlproperty enumeration BorderImage::status
165
166 This property describes the status of image loading. It can be one of:
167
168 \list
169 \o BorderImage.Null - no image has been set
170 \o BorderImage.Ready - the image has been loaded
171 \o BorderImage.Loading - the image is currently being loaded
172 \o BorderImage.Error - an error occurred while loading the image
173 \endlist
174
175 \sa progress
176*/
177
178/*!
179 \qmlproperty real BorderImage::progress
180
181 This property holds the progress of image loading, from 0.0 (nothing loaded)
182 to 1.0 (finished).
183
184 \sa status
185*/
186
187/*!
188 \qmlproperty bool BorderImage::smooth
189
190 Set this property if you want the image to be smoothly filtered when scaled or
191 transformed. Smooth filtering gives better visual quality, but is slower. If
192 the image is displayed at its natural size, this property has no visual or
193 performance effect.
194
195 By default, this property is set to false.
196
197 \note Generally scaling artifacts are only visible if the image is stationary on
198 the screen. A common pattern when animating an image is to disable smooth
199 filtering at the beginning of the animation and enable it at the conclusion.
200*/
201
202/*!
203 \qmlproperty url BorderImage::source
204
205 This property holds the URL that refers to the source image.
206
207 BorderImage can handle any image format supported by Qt, loaded from any
208 URL scheme supported by Qt.
209
210 It can also handle .sci files, which are a QML-specific format. A .sci
211 file uses a simple text-based format that specifies the borders, the
212 image file and the tile rules.
213
214 The following .sci file sets the borders to 10 on each side for the
215 image \c picture.png:
216
217 \qml
218 border.left: 10
219 border.top: 10
220 border.bottom: 10
221 border.right: 10
222 source: picture.png
223 \endqml
224
225 The URL may be absolute, or relative to the URL of the component.
226
227 \sa QDeclarativeImageProvider
228*/
229void QDeclarativeBorderImage::setSource(const QUrl &url)
230{
231 Q_D(QDeclarativeBorderImage);
232 //equality is fairly expensive, so we bypass for simple, common case
233 if ((d->url.isEmpty() == url.isEmpty()) && url == d->url)
234 return;
235
236 if (d->sciReply) {
237 d->sciReply->deleteLater();
238 d->sciReply = 0;
239 }
240
241 d->url = url;
242 d->sciurl = QUrl();
243 emit sourceChanged(d->url);
244
245 if (isComponentComplete())
246 load();
247}
248
249void QDeclarativeBorderImage::load()
250{
251 Q_D(QDeclarativeBorderImage);
252 if (d->progress != 0.0) {
253 d->progress = 0.0;
254 emit progressChanged(d->progress);
255 }
256
257 if (d->url.isEmpty()) {
258 d->pix.clear();
259 d->status = Null;
260 setImplicitWidth(0);
261 setImplicitHeight(0);
262 emit statusChanged(d->status);
263 update();
264 } else {
265 d->status = Loading;
266 if (d->url.path().endsWith(QLatin1String("sci"))) {
267#ifndef QT_NO_LOCALFILE_OPTIMIZED_QML
268 QString lf = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(d->url);
269 if (!lf.isEmpty()) {
270 QFile file(lf);
271 file.open(QIODevice::ReadOnly);
272 setGridScaledImage(QDeclarativeGridScaledImage(&file));
273 } else
274#endif
275 {
276 QNetworkRequest req(d->url);
277 d->sciReply = qmlEngine(this)->networkAccessManager()->get(req);
278
279 static int sciReplyFinished = -1;
280 static int thisSciRequestFinished = -1;
281 if (sciReplyFinished == -1) {
282 sciReplyFinished =
283 QNetworkReply::staticMetaObject.indexOfSignal("finished()");
284 thisSciRequestFinished =
285 QDeclarativeBorderImage::staticMetaObject.indexOfSlot("sciRequestFinished()");
286 }
287
288 QMetaObject::connect(d->sciReply, sciReplyFinished, this,
289 thisSciRequestFinished, Qt::DirectConnection);
290 }
291 } else {
292
293 d->pix.load(qmlEngine(this), d->url, d->async);
294
295 if (d->pix.isLoading()) {
296 d->pix.connectFinished(this, SLOT(requestFinished()));
297 d->pix.connectDownloadProgress(this, SLOT(requestProgress(qint64,qint64)));
298 } else {
299 QSize impsize = d->pix.implicitSize();
300 setImplicitWidth(impsize.width());
301 setImplicitHeight(impsize.height());
302
303 if (d->pix.isReady()) {
304 d->status = Ready;
305 } else {
306 d->status = Error;
307 qmlInfo(this) << d->pix.error();
308 }
309
310 d->progress = 1.0;
311 emit statusChanged(d->status);
312 emit progressChanged(d->progress);
313 update();
314 }
315 }
316 }
317
318 emit statusChanged(d->status);
319}
320
321/*!
322 \qmlproperty int BorderImage::border.left
323 \qmlproperty int BorderImage::border.right
324 \qmlproperty int BorderImage::border.top
325 \qmlproperty int BorderImage::border.bottom
326
327 The 4 border lines (2 horizontal and 2 vertical) break the image into 9 sections,
328 as shown below:
329
330 \image declarative-scalegrid.png
331
332 Each border line (left, right, top, and bottom) specifies an offset in pixels
333 from the respective edge of the source image. By default, each border line has
334 a value of 0.
335
336 For example, the following definition sets the bottom line 10 pixels up from
337 the bottom of the image:
338
339 \qml
340 border.bottom: 10
341 \endqml
342
343 The border lines can also be specified using a
344 \l {BorderImage::source}{.sci file}.
345*/
346
347QDeclarativeScaleGrid *QDeclarativeBorderImage::border()
348{
349 Q_D(QDeclarativeBorderImage);
350 return d->getScaleGrid();
351}
352
353/*!
354 \qmlproperty enumeration BorderImage::horizontalTileMode
355 \qmlproperty enumeration BorderImage::verticalTileMode
356
357 This property describes how to repeat or stretch the middle parts of the border image.
358
359 \list
360 \o BorderImage.Stretch - Scales the image to fit to the available area.
361 \o BorderImage.Repeat - Tile the image until there is no more space. May crop the last image.
362 \o BorderImage.Round - Like Repeat, but scales the images down to ensure that the last image is not cropped.
363 \endlist
364
365 The default tile mode for each property is BorderImage.Stretch.
366*/
367QDeclarativeBorderImage::TileMode QDeclarativeBorderImage::horizontalTileMode() const
368{
369 Q_D(const QDeclarativeBorderImage);
370 return d->horizontalTileMode;
371}
372
373void QDeclarativeBorderImage::setHorizontalTileMode(TileMode t)
374{
375 Q_D(QDeclarativeBorderImage);
376 if (t != d->horizontalTileMode) {
377 d->horizontalTileMode = t;
378 emit horizontalTileModeChanged();
379 update();
380 }
381}
382
383QDeclarativeBorderImage::TileMode QDeclarativeBorderImage::verticalTileMode() const
384{
385 Q_D(const QDeclarativeBorderImage);
386 return d->verticalTileMode;
387}
388
389void QDeclarativeBorderImage::setVerticalTileMode(TileMode t)
390{
391 Q_D(QDeclarativeBorderImage);
392 if (t != d->verticalTileMode) {
393 d->verticalTileMode = t;
394 emit verticalTileModeChanged();
395 update();
396 }
397}
398
399void QDeclarativeBorderImage::setGridScaledImage(const QDeclarativeGridScaledImage& sci)
400{
401 Q_D(QDeclarativeBorderImage);
402 if (!sci.isValid()) {
403 d->status = Error;
404 emit statusChanged(d->status);
405 } else {
406 QDeclarativeScaleGrid *sg = border();
407 sg->setTop(sci.gridTop());
408 sg->setBottom(sci.gridBottom());
409 sg->setLeft(sci.gridLeft());
410 sg->setRight(sci.gridRight());
411 d->horizontalTileMode = sci.horizontalTileRule();
412 d->verticalTileMode = sci.verticalTileRule();
413
414 d->sciurl = d->url.resolved(QUrl(sci.pixmapUrl()));
415
416 d->pix.load(qmlEngine(this), d->sciurl, d->async);
417
418 if (d->pix.isLoading()) {
419 static int thisRequestProgress = -1;
420 static int thisRequestFinished = -1;
421 if (thisRequestProgress == -1) {
422 thisRequestProgress =
423 QDeclarativeBorderImage::staticMetaObject.indexOfSlot("requestProgress(qint64,qint64)");
424 thisRequestFinished =
425 QDeclarativeBorderImage::staticMetaObject.indexOfSlot("requestFinished()");
426 }
427
428 d->pix.connectFinished(this, thisRequestFinished);
429 d->pix.connectDownloadProgress(this, thisRequestProgress);
430
431 } else {
432
433 QSize impsize = d->pix.implicitSize();
434 setImplicitWidth(impsize.width());
435 setImplicitHeight(impsize.height());
436
437 if (d->pix.isReady()) {
438 d->status = Ready;
439 } else {
440 d->status = Error;
441 qmlInfo(this) << d->pix.error();
442 }
443
444 d->progress = 1.0;
445 emit statusChanged(d->status);
446 emit progressChanged(1.0);
447 update();
448
449 }
450 }
451}
452
453void QDeclarativeBorderImage::requestFinished()
454{
455 Q_D(QDeclarativeBorderImage);
456
457 QSize impsize = d->pix.implicitSize();
458 if (d->pix.isError()) {
459 d->status = Error;
460 qmlInfo(this) << d->pix.error();
461 } else {
462 d->status = Ready;
463 }
464
465 setImplicitWidth(impsize.width());
466 setImplicitHeight(impsize.height());
467
468 d->progress = 1.0;
469 emit statusChanged(d->status);
470 emit progressChanged(1.0);
471 update();
472}
473
474#define BORDERIMAGE_MAX_REDIRECT 16
475
476void QDeclarativeBorderImage::sciRequestFinished()
477{
478 Q_D(QDeclarativeBorderImage);
479
480 d->redirectCount++;
481 if (d->redirectCount < BORDERIMAGE_MAX_REDIRECT) {
482 QVariant redirect = d->sciReply->attribute(QNetworkRequest::RedirectionTargetAttribute);
483 if (redirect.isValid()) {
484 QUrl url = d->sciReply->url().resolved(redirect.toUrl());
485 setSource(url);
486 return;
487 }
488 }
489 d->redirectCount=0;
490
491 if (d->sciReply->error() != QNetworkReply::NoError) {
492 d->status = Error;
493 d->sciReply->deleteLater();
494 d->sciReply = 0;
495 emit statusChanged(d->status);
496 } else {
497 QDeclarativeGridScaledImage sci(d->sciReply);
498 d->sciReply->deleteLater();
499 d->sciReply = 0;
500 setGridScaledImage(sci);
501 }
502}
503
504void QDeclarativeBorderImage::doUpdate()
505{
506 update();
507}
508
509void QDeclarativeBorderImage::paint(QPainter *p, const QStyleOptionGraphicsItem *, QWidget *)
510{
511 Q_D(QDeclarativeBorderImage);
512 if (d->pix.isNull() || d->width() <= 0.0 || d->height() <= 0.0)
513 return;
514
515 bool oldAA = p->testRenderHint(QPainter::Antialiasing);
516 bool oldSmooth = p->testRenderHint(QPainter::SmoothPixmapTransform);
517 if (d->smooth)
518 p->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform, d->smooth);
519
520 const QDeclarativeScaleGrid *border = d->getScaleGrid();
521 int left = border->left();
522 int right = border->right();
523 qreal borderWidth = left + right;
524 if (borderWidth > 0.0 && d->width() < borderWidth) {
525 qreal diff = borderWidth - d->width() - 1;
526 left -= qRound(diff * qreal(left) / borderWidth);
527 right -= qRound(diff * qreal(right) / borderWidth);
528 }
529 int top = border->top();
530 int bottom = border->bottom();
531 qreal borderHeight = top + bottom;
532 if (borderHeight > 0.0 && d->height() < borderHeight) {
533 qreal diff = borderHeight - d->height() - 1;
534 top -= qRound(diff * qreal(top) / borderHeight);
535 bottom -= qRound(diff * qreal(bottom) / borderHeight);
536 }
537 QMargins margins(left, top, right, bottom);
538 QTileRules rules((Qt::TileRule)d->horizontalTileMode, (Qt::TileRule)d->verticalTileMode);
539 qDrawBorderPixmap(p, QRect(0, 0, (int)d->width(), (int)d->height()), margins, d->pix, d->pix.rect(), margins, rules);
540 if (d->smooth) {
541 p->setRenderHint(QPainter::Antialiasing, oldAA);
542 p->setRenderHint(QPainter::SmoothPixmapTransform, oldSmooth);
543 }
544}
545
546QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.