| 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 | |