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