1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the QtQuick module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 3 requirements |
23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
24 | ** |
25 | ** GNU General Public License Usage |
26 | ** Alternatively, this file may be used under the terms of the GNU |
27 | ** General Public License version 2.0 or (at your option) the GNU General |
28 | ** Public license version 3 or any later version approved by the KDE Free |
29 | ** Qt Foundation. The licenses are as published by the Free Software |
30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
31 | ** included in the packaging of this file. Please review the following |
32 | ** information to ensure the GNU General Public License requirements will |
33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
34 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
35 | ** |
36 | ** $QT_END_LICENSE$ |
37 | ** |
38 | ****************************************************************************/ |
39 | |
40 | #include "qquickborderimage_p.h" |
41 | #include "qquickborderimage_p_p.h" |
42 | |
43 | #include <QtQml/qqmlinfo.h> |
44 | #include <QtQml/qqmlfile.h> |
45 | #include <QtQml/qqmlengine.h> |
46 | #if QT_CONFIG(qml_network) |
47 | #include <QtNetwork/qnetworkreply.h> |
48 | #endif |
49 | #include <QtCore/qfile.h> |
50 | #include <QtCore/qmath.h> |
51 | #include <QtGui/qguiapplication.h> |
52 | |
53 | #include <private/qqmlglobal_p.h> |
54 | #include <private/qsgadaptationlayer_p.h> |
55 | |
56 | QT_BEGIN_NAMESPACE |
57 | |
58 | |
59 | /*! |
60 | \qmltype BorderImage |
61 | \instantiates QQuickBorderImage |
62 | \inqmlmodule QtQuick |
63 | \brief Paints a border based on an image. |
64 | \inherits Item |
65 | \ingroup qtquick-visual |
66 | |
67 | The BorderImage type is used to create borders out of images by scaling or tiling |
68 | parts of each image. |
69 | |
70 | A BorderImage breaks a source image, specified using the \l source property, |
71 | into 9 regions, as shown below: |
72 | |
73 | \image declarative-scalegrid.png |
74 | |
75 | When the image is scaled, regions of the source image are scaled or tiled to |
76 | create the displayed border image in the following way: |
77 | |
78 | \list |
79 | \li The corners (regions 1, 3, 7, and 9) are not scaled at all. |
80 | \li Regions 2 and 8 are scaled according to |
81 | \l{BorderImage::horizontalTileMode}{horizontalTileMode}. |
82 | \li Regions 4 and 6 are scaled according to |
83 | \l{BorderImage::verticalTileMode}{verticalTileMode}. |
84 | \li The middle (region 5) is scaled according to both |
85 | \l{BorderImage::horizontalTileMode}{horizontalTileMode} and |
86 | \l{BorderImage::verticalTileMode}{verticalTileMode}. |
87 | \endlist |
88 | |
89 | The regions of the image are defined using the \l border property group, which |
90 | describes the distance from each edge of the source image to use as a border. |
91 | |
92 | \section1 Example Usage |
93 | |
94 | The following examples show the effects of the different modes on an image. |
95 | Guide lines are overlaid onto the image to show the different regions of the |
96 | image as described above. |
97 | |
98 | \beginfloatleft |
99 | \image qml-borderimage-normal-image.png |
100 | \endfloat |
101 | |
102 | An unscaled image is displayed using an Image. The \l border property is |
103 | used to determine the parts of the image that will lie inside the unscaled corner |
104 | areas and the parts that will be stretched horizontally and vertically. |
105 | |
106 | \snippet qml/borderimage/normal-image.qml normal image |
107 | |
108 | \clearfloat |
109 | \beginfloatleft |
110 | \image qml-borderimage-scaled.png |
111 | \endfloat |
112 | |
113 | A BorderImage is used to display the image, and it is given a size that is |
114 | larger than the original image. Since the \l horizontalTileMode property is set to |
115 | \l{BorderImage::horizontalTileMode}{BorderImage.Stretch}, the parts of image in |
116 | regions 2 and 8 are stretched horizontally. Since the \l verticalTileMode property |
117 | is set to \l{BorderImage::verticalTileMode}{BorderImage.Stretch}, the parts of image |
118 | in regions 4 and 6 are stretched vertically. |
119 | |
120 | \snippet qml/borderimage/borderimage-scaled.qml scaled border image |
121 | |
122 | \clearfloat |
123 | \beginfloatleft |
124 | \image qml-borderimage-tiled.png |
125 | \endfloat |
126 | |
127 | Again, a large BorderImage is used to display the image. With the |
128 | \l horizontalTileMode property set to \l{BorderImage::horizontalTileMode}{BorderImage.Repeat}, |
129 | the parts of image in regions 2 and 8 are tiled so that they fill the space at the |
130 | top and bottom of the item. Similarly, the \l verticalTileMode property is set to |
131 | \l{BorderImage::verticalTileMode}{BorderImage.Repeat}, the parts of image in regions |
132 | 4 and 6 are tiled so that they fill the space at the left and right of the item. |
133 | |
134 | \snippet qml/borderimage/borderimage-tiled.qml tiled border image |
135 | |
136 | \clearfloat |
137 | In some situations, the width of regions 2 and 8 may not be an exact multiple of the width |
138 | of the corresponding regions in the source image. Similarly, the height of regions 4 and 6 |
139 | may not be an exact multiple of the height of the corresponding regions. It can be useful |
140 | to use \l{BorderImage::horizontalTileMode}{BorderImage.Round} instead of |
141 | \l{BorderImage::horizontalTileMode}{BorderImage.Repeat} in cases like these. |
142 | |
143 | The Border Image example in \l{Qt Quick Examples - Image Elements} shows how a BorderImage |
144 | can be used to simulate a shadow effect on a rectangular item. |
145 | |
146 | \section1 Image Loading |
147 | |
148 | The source image may not be loaded instantaneously, depending on its original location. |
149 | Loading progress can be monitored with the \l progress property. |
150 | |
151 | \sa Image, AnimatedImage |
152 | */ |
153 | |
154 | /*! |
155 | \qmlproperty bool QtQuick::BorderImage::asynchronous |
156 | |
157 | Specifies that images on the local filesystem should be loaded |
158 | asynchronously in a separate thread. The default value is |
159 | false, causing the user interface thread to block while the |
160 | image is loaded. Setting \a asynchronous to true is useful where |
161 | maintaining a responsive user interface is more desirable |
162 | than having images immediately visible. |
163 | |
164 | Note that this property is only valid for images read from the |
165 | local filesystem. Images loaded via a network resource (e.g. HTTP) |
166 | are always loaded asynchronously. |
167 | */ |
168 | QQuickBorderImage::QQuickBorderImage(QQuickItem *parent) |
169 | : QQuickImageBase(*(new QQuickBorderImagePrivate), parent) |
170 | { |
171 | connect(sender: this, signal: &QQuickImageBase::sourceSizeChanged, receiver: this, slot: &QQuickBorderImage::sourceSizeChanged); |
172 | } |
173 | |
174 | QQuickBorderImage::~QQuickBorderImage() |
175 | { |
176 | #if QT_CONFIG(qml_network) |
177 | Q_D(QQuickBorderImage); |
178 | if (d->sciReply) |
179 | d->sciReply->deleteLater(); |
180 | #endif |
181 | } |
182 | |
183 | /*! |
184 | \qmlproperty enumeration QtQuick::BorderImage::status |
185 | |
186 | This property describes the status of image loading. It can be one of: |
187 | |
188 | \list |
189 | \li BorderImage.Null - no image has been set |
190 | \li BorderImage.Ready - the image has been loaded |
191 | \li BorderImage.Loading - the image is currently being loaded |
192 | \li BorderImage.Error - an error occurred while loading the image |
193 | \endlist |
194 | |
195 | \sa progress |
196 | */ |
197 | |
198 | /*! |
199 | \qmlproperty real QtQuick::BorderImage::progress |
200 | |
201 | This property holds the progress of image loading, from 0.0 (nothing loaded) |
202 | to 1.0 (finished). |
203 | |
204 | \sa status |
205 | */ |
206 | |
207 | /*! |
208 | \qmlproperty bool QtQuick::BorderImage::smooth |
209 | |
210 | This property holds whether the image is smoothly filtered when scaled or |
211 | transformed. Smooth filtering gives better visual quality, but it may be slower |
212 | on some hardware. If the image is displayed at its natural size, this property |
213 | has no visual or performance effect. |
214 | |
215 | By default, this property is set to true. |
216 | */ |
217 | |
218 | /*! |
219 | \qmlproperty bool QtQuick::BorderImage::cache |
220 | |
221 | Specifies whether the image should be cached. The default value is |
222 | true. Setting \a cache to false is useful when dealing with large images, |
223 | to make sure that they aren't cached at the expense of small 'ui element' images. |
224 | */ |
225 | |
226 | /*! |
227 | \qmlproperty bool QtQuick::BorderImage::mirror |
228 | |
229 | This property holds whether the image should be horizontally inverted |
230 | (effectively displaying a mirrored image). |
231 | |
232 | The default value is false. |
233 | */ |
234 | |
235 | /*! |
236 | \qmlproperty url QtQuick::BorderImage::source |
237 | |
238 | This property holds the URL that refers to the source image. |
239 | |
240 | BorderImage can handle any image format supported by Qt, loaded from any |
241 | URL scheme supported by Qt. |
242 | |
243 | This property can also be used to refer to .sci files, which are |
244 | written in a QML-specific, text-based format that specifies the |
245 | borders, the image file and the tile rules for a given border image. |
246 | |
247 | The following .sci file sets the borders to 10 on each side for the |
248 | image \c picture.png: |
249 | |
250 | \code |
251 | border.left: 10 |
252 | border.top: 10 |
253 | border.bottom: 10 |
254 | border.right: 10 |
255 | source: "picture.png" |
256 | \endcode |
257 | |
258 | The URL may be absolute, or relative to the URL of the component. |
259 | |
260 | \sa QQuickImageProvider |
261 | */ |
262 | |
263 | /*! |
264 | \qmlproperty QSize QtQuick::BorderImage::sourceSize |
265 | |
266 | This property holds the actual width and height of the loaded image. |
267 | |
268 | In BorderImage, this property is read-only. |
269 | |
270 | \sa Image::sourceSize |
271 | */ |
272 | void QQuickBorderImage::setSource(const QUrl &url) |
273 | { |
274 | Q_D(QQuickBorderImage); |
275 | |
276 | if (url == d->url) |
277 | return; |
278 | |
279 | #if QT_CONFIG(qml_network) |
280 | if (d->sciReply) { |
281 | d->sciReply->deleteLater(); |
282 | d->sciReply = nullptr; |
283 | } |
284 | #endif |
285 | |
286 | d->url = url; |
287 | d->sciurl = QUrl(); |
288 | emit sourceChanged(d->url); |
289 | |
290 | if (isComponentComplete()) |
291 | load(); |
292 | } |
293 | |
294 | void QQuickBorderImage::load() |
295 | { |
296 | Q_D(QQuickBorderImage); |
297 | |
298 | if (d->url.isEmpty()) { |
299 | loadEmptyUrl(); |
300 | } else { |
301 | if (d->url.path().endsWith(s: QLatin1String("sci" ))) { |
302 | QString lf = QQmlFile::urlToLocalFileOrQrc(d->url); |
303 | if (!lf.isEmpty()) { |
304 | QFile file(lf); |
305 | file.open(flags: QIODevice::ReadOnly); |
306 | setGridScaledImage(QQuickGridScaledImage(&file)); |
307 | } else { |
308 | #if QT_CONFIG(qml_network) |
309 | if (d->progress != 0.0) { |
310 | d->progress = 0.0; |
311 | emit progressChanged(progress: d->progress); |
312 | } |
313 | d->status = Loading; |
314 | QNetworkRequest req(d->url); |
315 | d->sciReply = qmlEngine(this)->networkAccessManager()->get(request: req); |
316 | qmlobject_connect(d->sciReply, QNetworkReply, SIGNAL(finished()), |
317 | this, QQuickBorderImage, SLOT(sciRequestFinished())); |
318 | emit statusChanged(d->status); |
319 | #endif |
320 | } |
321 | } else { |
322 | loadPixmap(url: d->url, loadOptions: LoadPixmapOptions(HandleDPR | UseProviderOptions)); |
323 | } |
324 | } |
325 | } |
326 | |
327 | /*! |
328 | \qmlpropertygroup QtQuick::BorderImage::border |
329 | \qmlproperty int QtQuick::BorderImage::border.left |
330 | \qmlproperty int QtQuick::BorderImage::border.right |
331 | \qmlproperty int QtQuick::BorderImage::border.top |
332 | \qmlproperty int QtQuick::BorderImage::border.bottom |
333 | |
334 | The 4 border lines (2 horizontal and 2 vertical) break the image into 9 sections, |
335 | as shown below: |
336 | |
337 | \image declarative-scalegrid.png |
338 | |
339 | Each border line (left, right, top, and bottom) specifies an offset in pixels |
340 | from the respective edge of the source image. By default, each border line has |
341 | a value of 0. |
342 | |
343 | For example, the following definition sets the bottom line 10 pixels up from |
344 | the bottom of the image: |
345 | |
346 | \qml |
347 | BorderImage { |
348 | border.bottom: 10 |
349 | // ... |
350 | } |
351 | \endqml |
352 | |
353 | The border lines can also be specified using a |
354 | \l {BorderImage::source}{.sci file}. |
355 | */ |
356 | |
357 | QQuickScaleGrid *QQuickBorderImage::border() |
358 | { |
359 | Q_D(QQuickBorderImage); |
360 | return d->getScaleGrid(); |
361 | } |
362 | |
363 | /*! |
364 | \qmlproperty enumeration QtQuick::BorderImage::horizontalTileMode |
365 | \qmlproperty enumeration QtQuick::BorderImage::verticalTileMode |
366 | |
367 | This property describes how to repeat or stretch the middle parts of the border image. |
368 | |
369 | \list |
370 | \li BorderImage.Stretch - Scales the image to fit to the available area. |
371 | \li BorderImage.Repeat - Tile the image until there is no more space. May crop the last image. |
372 | \li BorderImage.Round - Like Repeat, but scales the images down to ensure that the last image is not cropped. |
373 | \endlist |
374 | |
375 | The default tile mode for each property is BorderImage.Stretch. |
376 | */ |
377 | QQuickBorderImage::TileMode QQuickBorderImage::horizontalTileMode() const |
378 | { |
379 | Q_D(const QQuickBorderImage); |
380 | return d->horizontalTileMode; |
381 | } |
382 | |
383 | void QQuickBorderImage::setHorizontalTileMode(TileMode t) |
384 | { |
385 | Q_D(QQuickBorderImage); |
386 | if (t != d->horizontalTileMode) { |
387 | d->horizontalTileMode = t; |
388 | emit horizontalTileModeChanged(); |
389 | update(); |
390 | } |
391 | } |
392 | |
393 | QQuickBorderImage::TileMode QQuickBorderImage::verticalTileMode() const |
394 | { |
395 | Q_D(const QQuickBorderImage); |
396 | return d->verticalTileMode; |
397 | } |
398 | |
399 | void QQuickBorderImage::setVerticalTileMode(TileMode t) |
400 | { |
401 | Q_D(QQuickBorderImage); |
402 | if (t != d->verticalTileMode) { |
403 | d->verticalTileMode = t; |
404 | emit verticalTileModeChanged(); |
405 | update(); |
406 | } |
407 | } |
408 | |
409 | void QQuickBorderImage::setGridScaledImage(const QQuickGridScaledImage& sci) |
410 | { |
411 | Q_D(QQuickBorderImage); |
412 | if (!sci.isValid()) { |
413 | d->status = Error; |
414 | emit statusChanged(d->status); |
415 | } else { |
416 | QQuickScaleGrid *sg = border(); |
417 | sg->setTop(sci.gridTop()); |
418 | sg->setBottom(sci.gridBottom()); |
419 | sg->setLeft(sci.gridLeft()); |
420 | sg->setRight(sci.gridRight()); |
421 | d->horizontalTileMode = sci.horizontalTileRule(); |
422 | d->verticalTileMode = sci.verticalTileRule(); |
423 | |
424 | d->sciurl = d->url.resolved(relative: QUrl(sci.pixmapUrl())); |
425 | loadPixmap(url: d->sciurl); |
426 | } |
427 | } |
428 | |
429 | void QQuickBorderImage::requestFinished() |
430 | { |
431 | Q_D(QQuickBorderImage); |
432 | |
433 | QSize impsize = d->pix.implicitSize(); |
434 | if (d->pix.isError()) { |
435 | d->status = Error; |
436 | qmlWarning(me: this) << d->pix.error(); |
437 | if (d->progress != 0) { |
438 | d->progress = 0; |
439 | emit progressChanged(progress: d->progress); |
440 | } |
441 | } else { |
442 | d->status = Ready; |
443 | if (d->progress != 1.0) { |
444 | d->progress = 1.0; |
445 | emit progressChanged(progress: d->progress); |
446 | } |
447 | } |
448 | |
449 | setImplicitSize(impsize.width() / d->devicePixelRatio, impsize.height() / d->devicePixelRatio); |
450 | emit statusChanged(d->status); |
451 | if (sourceSize() != d->oldSourceSize) { |
452 | d->oldSourceSize = sourceSize(); |
453 | emit sourceSizeChanged(); |
454 | } |
455 | if (d->frameCount != d->pix.frameCount()) { |
456 | d->frameCount = d->pix.frameCount(); |
457 | emit frameCountChanged(); |
458 | } |
459 | |
460 | pixmapChange(); |
461 | } |
462 | |
463 | #if QT_CONFIG(qml_network) |
464 | #define BORDERIMAGE_MAX_REDIRECT 16 |
465 | |
466 | void QQuickBorderImage::sciRequestFinished() |
467 | { |
468 | Q_D(QQuickBorderImage); |
469 | |
470 | d->redirectCount++; |
471 | if (d->redirectCount < BORDERIMAGE_MAX_REDIRECT) { |
472 | QVariant redirect = d->sciReply->attribute(code: QNetworkRequest::RedirectionTargetAttribute); |
473 | if (redirect.isValid()) { |
474 | QUrl url = d->sciReply->url().resolved(relative: redirect.toUrl()); |
475 | setSource(url); |
476 | return; |
477 | } |
478 | } |
479 | d->redirectCount=0; |
480 | |
481 | if (d->sciReply->error() != QNetworkReply::NoError) { |
482 | d->status = Error; |
483 | d->sciReply->deleteLater(); |
484 | d->sciReply = nullptr; |
485 | emit statusChanged(d->status); |
486 | } else { |
487 | QQuickGridScaledImage sci(d->sciReply); |
488 | d->sciReply->deleteLater(); |
489 | d->sciReply = nullptr; |
490 | setGridScaledImage(sci); |
491 | } |
492 | } |
493 | #endif // qml_network |
494 | |
495 | void QQuickBorderImage::doUpdate() |
496 | { |
497 | update(); |
498 | } |
499 | |
500 | void QQuickBorderImagePrivate::calculateRects(const QQuickScaleGrid *border, |
501 | const QSize &sourceSize, |
502 | const QSizeF &targetSize, |
503 | int horizontalTileMode, |
504 | int verticalTileMode, |
505 | qreal devicePixelRatio, |
506 | QRectF *targetRect, |
507 | QRectF *innerTargetRect, |
508 | QRectF *innerSourceRect, |
509 | QRectF *subSourceRect) |
510 | { |
511 | *innerSourceRect = QRectF(0, 0, 1, 1); |
512 | *targetRect = QRectF(0, 0, targetSize.width(), targetSize.height()); |
513 | *innerTargetRect = *targetRect; |
514 | |
515 | if (border) { |
516 | qreal borderLeft = border->left() * devicePixelRatio; |
517 | qreal borderRight = border->right() * devicePixelRatio; |
518 | qreal borderTop = border->top() * devicePixelRatio; |
519 | qreal borderBottom = border->bottom() * devicePixelRatio; |
520 | if (borderLeft + borderRight > sourceSize.width() && borderLeft < sourceSize.width()) |
521 | borderRight = sourceSize.width() - borderLeft; |
522 | if (borderTop + borderBottom > sourceSize.height() && borderTop < sourceSize.height()) |
523 | borderBottom = sourceSize.height() - borderTop; |
524 | *innerSourceRect = QRectF(QPointF(borderLeft / qreal(sourceSize.width()), |
525 | borderTop / qreal(sourceSize.height())), |
526 | QPointF((sourceSize.width() - borderRight) / qreal(sourceSize.width()), |
527 | (sourceSize.height() - borderBottom) / qreal(sourceSize.height()))), |
528 | *innerTargetRect = QRectF(border->left(), |
529 | border->top(), |
530 | qMax<qreal>(a: 0, b: targetSize.width() - (border->right() + border->left())), |
531 | qMax<qreal>(a: 0, b: targetSize.height() - (border->bottom() + border->top()))); |
532 | } |
533 | |
534 | qreal hTiles = 1; |
535 | qreal vTiles = 1; |
536 | const QSizeF innerTargetSize = innerTargetRect->size() * devicePixelRatio; |
537 | if (innerSourceRect->width() <= 0) |
538 | hTiles = 0; |
539 | else if (horizontalTileMode != QQuickBorderImage::Stretch) { |
540 | hTiles = innerTargetSize.width() / qreal(innerSourceRect->width() * sourceSize.width()); |
541 | if (horizontalTileMode == QQuickBorderImage::Round) |
542 | hTiles = qCeil(v: hTiles); |
543 | } |
544 | if (innerSourceRect->height() <= 0) |
545 | vTiles = 0; |
546 | else if (verticalTileMode != QQuickBorderImage::Stretch) { |
547 | vTiles = innerTargetSize.height() / qreal(innerSourceRect->height() * sourceSize.height()); |
548 | if (verticalTileMode == QQuickBorderImage::Round) |
549 | vTiles = qCeil(v: vTiles); |
550 | } |
551 | |
552 | *subSourceRect = QRectF(0, 0, hTiles, vTiles); |
553 | } |
554 | |
555 | |
556 | QSGNode *QQuickBorderImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) |
557 | { |
558 | Q_D(QQuickBorderImage); |
559 | |
560 | QSGTexture *texture = d->sceneGraphRenderContext()->textureForFactory(factory: d->pix.textureFactory(), window: window()); |
561 | |
562 | if (!texture || width() <= 0 || height() <= 0) { |
563 | delete oldNode; |
564 | return nullptr; |
565 | } |
566 | |
567 | QSGInternalImageNode *node = static_cast<QSGInternalImageNode *>(oldNode); |
568 | |
569 | bool updatePixmap = d->pixmapChanged; |
570 | d->pixmapChanged = false; |
571 | if (!node) { |
572 | node = d->sceneGraphContext()->createInternalImageNode(renderContext: d->sceneGraphRenderContext()); |
573 | updatePixmap = true; |
574 | } |
575 | |
576 | if (updatePixmap) |
577 | node->setTexture(texture); |
578 | |
579 | // Don't implicitly create the scalegrid in the rendering thread... |
580 | QRectF targetRect; |
581 | QRectF innerTargetRect; |
582 | QRectF innerSourceRect; |
583 | QRectF subSourceRect; |
584 | d->calculateRects(border: d->border, |
585 | sourceSize: QSize(d->pix.width(), d->pix.height()), targetSize: QSizeF(width(), height()), |
586 | horizontalTileMode: d->horizontalTileMode, verticalTileMode: d->verticalTileMode, devicePixelRatio: d->devicePixelRatio, |
587 | targetRect: &targetRect, innerTargetRect: &innerTargetRect, |
588 | innerSourceRect: &innerSourceRect, subSourceRect: &subSourceRect); |
589 | |
590 | node->setTargetRect(targetRect); |
591 | node->setInnerSourceRect(innerSourceRect); |
592 | node->setInnerTargetRect(innerTargetRect); |
593 | node->setSubSourceRect(subSourceRect); |
594 | node->setMirror(d->mirror); |
595 | |
596 | node->setMipmapFiltering(QSGTexture::None); |
597 | node->setFiltering(d->smooth ? QSGTexture::Linear : QSGTexture::Nearest); |
598 | if (innerSourceRect == QRectF(0, 0, 1, 1) && (subSourceRect.width() > 1 || subSourceRect.height() > 1)) { |
599 | node->setHorizontalWrapMode(QSGTexture::Repeat); |
600 | node->setVerticalWrapMode(QSGTexture::Repeat); |
601 | } else { |
602 | node->setHorizontalWrapMode(QSGTexture::ClampToEdge); |
603 | node->setVerticalWrapMode(QSGTexture::ClampToEdge); |
604 | } |
605 | node->setAntialiasing(d->antialiasing); |
606 | node->update(); |
607 | |
608 | return node; |
609 | } |
610 | |
611 | void QQuickBorderImage::pixmapChange() |
612 | { |
613 | Q_D(QQuickBorderImage); |
614 | d->pixmapChanged = true; |
615 | update(); |
616 | } |
617 | |
618 | /*! |
619 | \qmlproperty int QtQuick::BorderImage::currentFrame |
620 | \qmlproperty int QtQuick::BorderImage::frameCount |
621 | \since 5.14 |
622 | |
623 | currentFrame is the frame that is currently visible. The default is \c 0. |
624 | You can set it to a number between \c 0 and \c {frameCount - 1} to display a |
625 | different frame, if the image contains multiple frames. |
626 | |
627 | frameCount is the number of frames in the image. Most images have only one frame. |
628 | */ |
629 | |
630 | QT_END_NAMESPACE |
631 | |
632 | #include "moc_qquickborderimage_p.cpp" |
633 | |