| 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 "qsgsoftwareinternalimagenode_p.h" | 
| 41 |  | 
| 42 | #include "qsgsoftwarepixmaptexture_p.h" | 
| 43 | #include "qsgsoftwarelayer_p.h" | 
| 44 | #include <QPainter> | 
| 45 | #include <qmath.h> | 
| 46 |  | 
| 47 | QT_BEGIN_NAMESPACE | 
| 48 |  | 
| 49 | namespace QSGSoftwareHelpers { | 
| 50 | // Helper from widgets/styles/qdrawutil.cpp | 
| 51 |  | 
| 52 | static inline QMargins normalizedMargins(const QMargins &m) | 
| 53 | { | 
| 54 |     return QMargins(qMax(a: m.left(), b: 0), qMax(a: m.top(), b: 0), qMax(a: m.right(), b: 0), qMax(a: m.bottom(), b: 0)); | 
| 55 | } | 
| 56 |  | 
| 57 | void qDrawBorderPixmap(QPainter *painter, const QRect &targetRect, const QMargins &targetMarginsIn, | 
| 58 |                        const QPixmap &pixmap, const QRect &sourceRect, const QMargins &sourceMarginsIn, | 
| 59 |                        const QTileRules &rules, QDrawBorderPixmap::DrawingHints hints) | 
| 60 | { | 
| 61 |     QPainter::PixmapFragment d; | 
| 62 |     d.opacity = 1.0; | 
| 63 |     d.rotation = 0.0; | 
| 64 |  | 
| 65 |     QPixmapFragmentsArray opaqueData; | 
| 66 |     QPixmapFragmentsArray translucentData; | 
| 67 |  | 
| 68 |     QMargins sourceMargins = normalizedMargins(m: sourceMarginsIn); | 
| 69 |     QMargins targetMargins = normalizedMargins(m: targetMarginsIn); | 
| 70 |  | 
| 71 |     const qreal sourceDpr = pixmap.devicePixelRatioF(); | 
| 72 |     sourceMargins *= sourceDpr; | 
| 73 |  | 
| 74 |     // source center | 
| 75 |     const int sourceCenterTop = sourceRect.top() + sourceMargins.top(); | 
| 76 |     const int sourceCenterLeft = sourceRect.left() + sourceMargins.left(); | 
| 77 |     const int sourceCenterBottom = sourceRect.bottom() - sourceMargins.bottom() + 1; | 
| 78 |     const int sourceCenterRight = sourceRect.right() - sourceMargins.right() + 1; | 
| 79 |     const int sourceCenterWidth = sourceCenterRight - sourceCenterLeft; | 
| 80 |     const int sourceCenterHeight = sourceCenterBottom - sourceCenterTop; | 
| 81 |     // target center | 
| 82 |     const int targetCenterTop = targetRect.top() + targetMargins.top(); | 
| 83 |     const int targetCenterLeft = targetRect.left() + targetMargins.left(); | 
| 84 |     const int targetCenterBottom = targetRect.bottom() - targetMargins.bottom() + 1; | 
| 85 |     const int targetCenterRight = targetRect.right() - targetMargins.right() + 1; | 
| 86 |     const int targetCenterWidth = targetCenterRight - targetCenterLeft; | 
| 87 |     const int targetCenterHeight = targetCenterBottom - targetCenterTop; | 
| 88 |  | 
| 89 |     QVarLengthArray<qreal, 16> xTarget; // x-coordinates of target rectangles | 
| 90 |     QVarLengthArray<qreal, 16> yTarget; // y-coordinates of target rectangles | 
| 91 |  | 
| 92 |     int columns = 3; | 
| 93 |     int rows = 3; | 
| 94 |     if (rules.horizontal != Qt::StretchTile && sourceCenterWidth != 0) | 
| 95 |         columns = qMax(a: 3, b: 2 + qCeil(v: (targetCenterWidth * sourceDpr) / qreal(sourceCenterWidth))); | 
| 96 |     if (rules.vertical != Qt::StretchTile && sourceCenterHeight != 0) | 
| 97 |         rows = qMax(a: 3, b: 2 + qCeil(v: (targetCenterHeight * sourceDpr) / qreal(sourceCenterHeight))); | 
| 98 |  | 
| 99 |     xTarget.resize(asize: columns + 1); | 
| 100 |     yTarget.resize(asize: rows + 1); | 
| 101 |  | 
| 102 |     xTarget[0] = targetRect.left(); | 
| 103 |     xTarget[1] = targetCenterLeft; | 
| 104 |     xTarget[columns - 1] = targetCenterRight; | 
| 105 |     xTarget[columns] = targetRect.left() + targetRect.width(); | 
| 106 |  | 
| 107 |     yTarget[0] = targetRect.top(); | 
| 108 |     yTarget[1] = targetCenterTop; | 
| 109 |     yTarget[rows - 1] = targetCenterBottom; | 
| 110 |     yTarget[rows] = targetRect.top() + targetRect.height(); | 
| 111 |  | 
| 112 |     qreal dx = targetCenterWidth; | 
| 113 |     qreal dy = targetCenterHeight; | 
| 114 |  | 
| 115 |     switch (rules.horizontal) { | 
| 116 |     case Qt::StretchTile: | 
| 117 |         dx = targetCenterWidth; | 
| 118 |         break; | 
| 119 |     case Qt::RepeatTile: | 
| 120 |         dx = sourceCenterWidth / sourceDpr; | 
| 121 |         break; | 
| 122 |     case Qt::RoundTile: | 
| 123 |         dx = targetCenterWidth / qreal(columns - 2); | 
| 124 |         break; | 
| 125 |     } | 
| 126 |  | 
| 127 |     for (int i = 2; i < columns - 1; ++i) | 
| 128 |         xTarget[i] = xTarget[i - 1] + dx; | 
| 129 |  | 
| 130 |     switch (rules.vertical) { | 
| 131 |     case Qt::StretchTile: | 
| 132 |         dy = targetCenterHeight; | 
| 133 |         break; | 
| 134 |     case Qt::RepeatTile: | 
| 135 |         dy = sourceCenterHeight / sourceDpr; | 
| 136 |         break; | 
| 137 |     case Qt::RoundTile: | 
| 138 |         dy = targetCenterHeight / qreal(rows - 2); | 
| 139 |         break; | 
| 140 |     } | 
| 141 |  | 
| 142 |     for (int i = 2; i < rows - 1; ++i) | 
| 143 |         yTarget[i] = yTarget[i - 1] + dy; | 
| 144 |  | 
| 145 |     // corners | 
| 146 |     if (targetMargins.top() > 0 && targetMargins.left() > 0 && sourceMargins.top() > 0 && sourceMargins.left() > 0) { // top left | 
| 147 |         d.x = (0.5 * (xTarget[1] + xTarget[0])); | 
| 148 |         d.y = (0.5 * (yTarget[1] + yTarget[0])); | 
| 149 |         d.sourceLeft = sourceRect.left(); | 
| 150 |         d.sourceTop = sourceRect.top(); | 
| 151 |         d.width = sourceMargins.left(); | 
| 152 |         d.height = sourceMargins.top(); | 
| 153 |         d.scaleX = qreal(xTarget[1] - xTarget[0]) / d.width; | 
| 154 |         d.scaleY = qreal(yTarget[1] - yTarget[0]) / d.height; | 
| 155 |         if (hints & QDrawBorderPixmap::OpaqueTopLeft) | 
| 156 |             opaqueData.append(t: d); | 
| 157 |         else | 
| 158 |             translucentData.append(t: d); | 
| 159 |     } | 
| 160 |     if (targetMargins.top() > 0 && targetMargins.right() > 0 && sourceMargins.top() > 0 && sourceMargins.right() > 0) { // top right | 
| 161 |         d.x = (0.5 * (xTarget[columns] + xTarget[columns - 1])); | 
| 162 |         d.y = (0.5 * (yTarget[1] + yTarget[0])); | 
| 163 |         d.sourceLeft = sourceCenterRight; | 
| 164 |         d.sourceTop = sourceRect.top(); | 
| 165 |         d.width = sourceMargins.right(); | 
| 166 |         d.height = sourceMargins.top(); | 
| 167 |         d.scaleX = qreal(xTarget[columns] - xTarget[columns - 1]) / d.width; | 
| 168 |         d.scaleY = qreal(yTarget[1] - yTarget[0]) / d.height; | 
| 169 |         if (hints & QDrawBorderPixmap::OpaqueTopRight) | 
| 170 |             opaqueData.append(t: d); | 
| 171 |         else | 
| 172 |             translucentData.append(t: d); | 
| 173 |     } | 
| 174 |     if (targetMargins.bottom() > 0 && targetMargins.left() > 0 && sourceMargins.bottom() > 0 && sourceMargins.left() > 0) { // bottom left | 
| 175 |         d.x = (0.5 * (xTarget[1] + xTarget[0])); | 
| 176 |         d.y =(0.5 * (yTarget[rows] + yTarget[rows - 1])); | 
| 177 |         d.sourceLeft = sourceRect.left(); | 
| 178 |         d.sourceTop = sourceCenterBottom; | 
| 179 |         d.width = sourceMargins.left(); | 
| 180 |         d.height = sourceMargins.bottom(); | 
| 181 |         d.scaleX = qreal(xTarget[1] - xTarget[0]) / d.width; | 
| 182 |         d.scaleY = qreal(yTarget[rows] - yTarget[rows - 1]) / d.height; | 
| 183 |         if (hints & QDrawBorderPixmap::OpaqueBottomLeft) | 
| 184 |             opaqueData.append(t: d); | 
| 185 |         else | 
| 186 |             translucentData.append(t: d); | 
| 187 |     } | 
| 188 |     if (targetMargins.bottom() > 0 && targetMargins.right() > 0 && sourceMargins.bottom() > 0 && sourceMargins.right() > 0) { // bottom right | 
| 189 |         d.x = (0.5 * (xTarget[columns] + xTarget[columns - 1])); | 
| 190 |         d.y = (0.5 * (yTarget[rows] + yTarget[rows - 1])); | 
| 191 |         d.sourceLeft = sourceCenterRight; | 
| 192 |         d.sourceTop = sourceCenterBottom; | 
| 193 |         d.width = sourceMargins.right(); | 
| 194 |         d.height = sourceMargins.bottom(); | 
| 195 |         d.scaleX = qreal(xTarget[columns] - xTarget[columns - 1]) / d.width; | 
| 196 |         d.scaleY = qreal(yTarget[rows] - yTarget[rows - 1]) / d.height; | 
| 197 |         if (hints & QDrawBorderPixmap::OpaqueBottomRight) | 
| 198 |             opaqueData.append(t: d); | 
| 199 |         else | 
| 200 |             translucentData.append(t: d); | 
| 201 |     } | 
| 202 |  | 
| 203 |     // horizontal edges | 
| 204 |     if (targetCenterWidth > 0 && sourceCenterWidth > 0) { | 
| 205 |         if (targetMargins.top() > 0 && sourceMargins.top() > 0) { // top | 
| 206 |             QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueTop ? opaqueData : translucentData; | 
| 207 |             d.sourceLeft = sourceCenterLeft; | 
| 208 |             d.sourceTop = sourceRect.top(); | 
| 209 |             d.width = sourceCenterWidth; | 
| 210 |             d.height = sourceMargins.top(); | 
| 211 |             d.y = (0.5 * (yTarget[1] + yTarget[0])); | 
| 212 |             d.scaleX = dx / d.width; | 
| 213 |             d.scaleY = qreal(yTarget[1] - yTarget[0]) / d.height; | 
| 214 |             for (int i = 1; i < columns - 1; ++i) { | 
| 215 |                 d.x = (0.5 * (xTarget[i + 1] + xTarget[i])); | 
| 216 |                 data.append(t: d); | 
| 217 |             } | 
| 218 |             if (rules.horizontal == Qt::RepeatTile) | 
| 219 |                 data[data.size() - 1].width = ((xTarget[columns - 1] - xTarget[columns - 2]) / d.scaleX); | 
| 220 |         } | 
| 221 |         if (targetMargins.bottom() > 0 && sourceMargins.bottom() > 0) { // bottom | 
| 222 |             QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueBottom ? opaqueData : translucentData; | 
| 223 |             d.sourceLeft = sourceCenterLeft; | 
| 224 |             d.sourceTop = sourceCenterBottom; | 
| 225 |             d.width = sourceCenterWidth; | 
| 226 |             d.height = sourceMargins.bottom(); | 
| 227 |             d.y = (0.5 * (yTarget[rows] + yTarget[rows - 1])); | 
| 228 |             d.scaleX = dx / d.width; | 
| 229 |             d.scaleY = qreal(yTarget[rows] - yTarget[rows - 1]) / d.height; | 
| 230 |             for (int i = 1; i < columns - 1; ++i) { | 
| 231 |                 d.x = (0.5 * (xTarget[i + 1] + xTarget[i])); | 
| 232 |                 data.append(t: d); | 
| 233 |             } | 
| 234 |             if (rules.horizontal == Qt::RepeatTile) | 
| 235 |                 data[data.size() - 1].width = ((xTarget[columns - 1] - xTarget[columns - 2]) / d.scaleX); | 
| 236 |         } | 
| 237 |     } | 
| 238 |  | 
| 239 |     // vertical edges | 
| 240 |     if (targetCenterHeight > 0 && sourceCenterHeight > 0) { | 
| 241 |         if (targetMargins.left() > 0 && sourceMargins.left() > 0) { // left | 
| 242 |             QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueLeft ? opaqueData : translucentData; | 
| 243 |             d.sourceLeft = sourceRect.left(); | 
| 244 |             d.sourceTop = sourceCenterTop; | 
| 245 |             d.width = sourceMargins.left(); | 
| 246 |             d.height = sourceCenterHeight; | 
| 247 |             d.x = (0.5 * (xTarget[1] + xTarget[0])); | 
| 248 |             d.scaleX = qreal(xTarget[1] - xTarget[0]) / d.width; | 
| 249 |             d.scaleY = dy / d.height; | 
| 250 |             for (int i = 1; i < rows - 1; ++i) { | 
| 251 |                 d.y = (0.5 * (yTarget[i + 1] + yTarget[i])); | 
| 252 |                 data.append(t: d); | 
| 253 |             } | 
| 254 |             if (rules.vertical == Qt::RepeatTile) | 
| 255 |                 data[data.size() - 1].height = ((yTarget[rows - 1] - yTarget[rows - 2]) / d.scaleY); | 
| 256 |         } | 
| 257 |         if (targetMargins.right() > 0 && sourceMargins.right() > 0) { // right | 
| 258 |             QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueRight ? opaqueData : translucentData; | 
| 259 |             d.sourceLeft = sourceCenterRight; | 
| 260 |             d.sourceTop = sourceCenterTop; | 
| 261 |             d.width = sourceMargins.right(); | 
| 262 |             d.height = sourceCenterHeight; | 
| 263 |             d.x = (0.5 * (xTarget[columns] + xTarget[columns - 1])); | 
| 264 |             d.scaleX = qreal(xTarget[columns] - xTarget[columns - 1]) / d.width; | 
| 265 |             d.scaleY = dy / d.height; | 
| 266 |             for (int i = 1; i < rows - 1; ++i) { | 
| 267 |                 d.y = (0.5 * (yTarget[i + 1] + yTarget[i])); | 
| 268 |                 data.append(t: d); | 
| 269 |             } | 
| 270 |             if (rules.vertical == Qt::RepeatTile) | 
| 271 |                 data[data.size() - 1].height = ((yTarget[rows - 1] - yTarget[rows - 2]) / d.scaleY); | 
| 272 |         } | 
| 273 |     } | 
| 274 |  | 
| 275 |     // center | 
| 276 |     if (targetCenterWidth > 0 && targetCenterHeight > 0 && sourceCenterWidth > 0 && sourceCenterHeight > 0) { | 
| 277 |         QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueCenter ? opaqueData : translucentData; | 
| 278 |         d.sourceLeft = sourceCenterLeft; | 
| 279 |         d.sourceTop = sourceCenterTop; | 
| 280 |         d.width = sourceCenterWidth; | 
| 281 |         d.height = sourceCenterHeight; | 
| 282 |         d.scaleX = dx / d.width; | 
| 283 |         d.scaleY = dy / d.height; | 
| 284 |  | 
| 285 |         qreal repeatWidth = (xTarget[columns - 1] - xTarget[columns - 2]) / d.scaleX; | 
| 286 |         qreal repeatHeight = (yTarget[rows - 1] - yTarget[rows - 2]) / d.scaleY; | 
| 287 |  | 
| 288 |         for (int j = 1; j < rows - 1; ++j) { | 
| 289 |             d.y = (0.5 * (yTarget[j + 1] + yTarget[j])); | 
| 290 |             for (int i = 1; i < columns - 1; ++i) { | 
| 291 |                 d.x = (0.5 * (xTarget[i + 1] + xTarget[i])); | 
| 292 |                 data.append(t: d); | 
| 293 |             } | 
| 294 |             if (rules.horizontal == Qt::RepeatTile) | 
| 295 |                 data[data.size() - 1].width = repeatWidth; | 
| 296 |         } | 
| 297 |         if (rules.vertical == Qt::RepeatTile) { | 
| 298 |             for (int i = 1; i < columns - 1; ++i) | 
| 299 |                 data[data.size() - i].height = repeatHeight; | 
| 300 |         } | 
| 301 |     } | 
| 302 |  | 
| 303 |     if (opaqueData.size()) | 
| 304 |         painter->drawPixmapFragments(fragments: opaqueData.data(), fragmentCount: opaqueData.size(), pixmap, hints: QPainter::OpaqueHint); | 
| 305 |     if (translucentData.size()) | 
| 306 |         painter->drawPixmapFragments(fragments: translucentData.data(), fragmentCount: translucentData.size(), pixmap); | 
| 307 | } | 
| 308 |  | 
| 309 | } // QSGSoftwareHelpers namespace | 
| 310 |  | 
| 311 | QSGSoftwareInternalImageNode::QSGSoftwareInternalImageNode() | 
| 312 |     : m_innerSourceRect(0, 0, 1, 1) | 
| 313 |     , m_subSourceRect(0, 0, 1, 1) | 
| 314 |     , m_texture(nullptr) | 
| 315 |     , m_mirror(false) | 
| 316 |     , m_textureIsLayer(false) | 
| 317 |     , m_smooth(true) | 
| 318 |     , m_tileHorizontal(false) | 
| 319 |     , m_tileVertical(false) | 
| 320 |     , m_cachedMirroredPixmapIsDirty(false) | 
| 321 | { | 
| 322 |     setMaterial((QSGMaterial*)1); | 
| 323 |     setGeometry((QSGGeometry*)1); | 
| 324 | } | 
| 325 |  | 
| 326 |  | 
| 327 | void QSGSoftwareInternalImageNode::setTargetRect(const QRectF &rect) | 
| 328 | { | 
| 329 |     if (rect == m_targetRect) | 
| 330 |         return; | 
| 331 |     m_targetRect = rect; | 
| 332 |     markDirty(bits: DirtyGeometry); | 
| 333 | } | 
| 334 |  | 
| 335 | void QSGSoftwareInternalImageNode::setInnerTargetRect(const QRectF &rect) | 
| 336 | { | 
| 337 |     if (rect == m_innerTargetRect) | 
| 338 |         return; | 
| 339 |     m_innerTargetRect = rect; | 
| 340 |     markDirty(bits: DirtyGeometry); | 
| 341 | } | 
| 342 |  | 
| 343 | void QSGSoftwareInternalImageNode::setInnerSourceRect(const QRectF &rect) | 
| 344 | { | 
| 345 |     if (rect == m_innerSourceRect) | 
| 346 |         return; | 
| 347 |     m_innerSourceRect = rect; | 
| 348 |     markDirty(bits: DirtyGeometry); | 
| 349 | } | 
| 350 |  | 
| 351 | void QSGSoftwareInternalImageNode::setSubSourceRect(const QRectF &rect) | 
| 352 | { | 
| 353 |     if (rect == m_subSourceRect) | 
| 354 |         return; | 
| 355 |     m_subSourceRect = rect; | 
| 356 |     markDirty(bits: DirtyGeometry); | 
| 357 | } | 
| 358 |  | 
| 359 | void QSGSoftwareInternalImageNode::setTexture(QSGTexture *texture) | 
| 360 | { | 
| 361 |     m_texture = texture; | 
| 362 |     m_cachedMirroredPixmapIsDirty = true; | 
| 363 |     m_textureIsLayer = static_cast<bool>(qobject_cast<QSGSoftwareLayer*>(object: texture)); | 
| 364 |     markDirty(bits: DirtyMaterial); | 
| 365 | } | 
| 366 |  | 
| 367 | void QSGSoftwareInternalImageNode::setMirror(bool mirror) | 
| 368 | { | 
| 369 |     if (m_mirror != mirror) { | 
| 370 |         m_mirror = mirror; | 
| 371 |         m_cachedMirroredPixmapIsDirty = true; | 
| 372 |         markDirty(bits: DirtyMaterial); | 
| 373 |     } | 
| 374 | } | 
| 375 |  | 
| 376 | void QSGSoftwareInternalImageNode::setMipmapFiltering(QSGTexture::Filtering /*filtering*/) | 
| 377 | { | 
| 378 | } | 
| 379 |  | 
| 380 | void QSGSoftwareInternalImageNode::setFiltering(QSGTexture::Filtering filtering) | 
| 381 | { | 
| 382 |     bool smooth = (filtering == QSGTexture::Linear); | 
| 383 |     if (smooth == m_smooth) | 
| 384 |         return; | 
| 385 |  | 
| 386 |     m_smooth = smooth; | 
| 387 |     markDirty(bits: DirtyMaterial); | 
| 388 | } | 
| 389 |  | 
| 390 | void QSGSoftwareInternalImageNode::setHorizontalWrapMode(QSGTexture::WrapMode wrapMode) | 
| 391 | { | 
| 392 |     bool tileHorizontal = (wrapMode == QSGTexture::Repeat); | 
| 393 |     if (tileHorizontal == m_tileHorizontal) | 
| 394 |         return; | 
| 395 |  | 
| 396 |     m_tileHorizontal = tileHorizontal; | 
| 397 |     markDirty(bits: DirtyMaterial); | 
| 398 | } | 
| 399 |  | 
| 400 | void QSGSoftwareInternalImageNode::setVerticalWrapMode(QSGTexture::WrapMode wrapMode) | 
| 401 | { | 
| 402 |     bool tileVertical = (wrapMode == QSGTexture::Repeat); | 
| 403 |     if (tileVertical == m_tileVertical) | 
| 404 |         return; | 
| 405 |  | 
| 406 |     m_tileVertical = (wrapMode == QSGTexture::Repeat); | 
| 407 |     markDirty(bits: DirtyMaterial); | 
| 408 | } | 
| 409 |  | 
| 410 | void QSGSoftwareInternalImageNode::update() | 
| 411 | { | 
| 412 |     if (m_cachedMirroredPixmapIsDirty) { | 
| 413 |         if (m_mirror || m_textureIsLayer) { | 
| 414 |             QTransform transform( | 
| 415 |                         (m_mirror ? -1 : 1), 0, | 
| 416 |                         0                  , (m_textureIsLayer ? -1 :1), | 
| 417 |                         0                  , 0 | 
| 418 |             ); | 
| 419 |             m_cachedMirroredPixmap = pixmap().transformed(transform); | 
| 420 |         } else { | 
| 421 |             //Cleanup cached pixmap if necessary | 
| 422 |             if (!m_cachedMirroredPixmap.isNull()) | 
| 423 |                 m_cachedMirroredPixmap = QPixmap(); | 
| 424 |         } | 
| 425 |         m_cachedMirroredPixmapIsDirty = false; | 
| 426 |     } | 
| 427 | } | 
| 428 |  | 
| 429 | void QSGSoftwareInternalImageNode::preprocess() | 
| 430 | { | 
| 431 |     bool doDirty = false; | 
| 432 |     QSGLayer *t = qobject_cast<QSGLayer *>(object: m_texture); | 
| 433 |     if (t) { | 
| 434 |         doDirty = t->updateTexture(); | 
| 435 |         markDirty(bits: DirtyGeometry); | 
| 436 |     } | 
| 437 |     if (doDirty) | 
| 438 |         markDirty(bits: DirtyMaterial); | 
| 439 |     m_cachedMirroredPixmapIsDirty = doDirty; | 
| 440 | } | 
| 441 |  | 
| 442 | static Qt::TileRule getTileRule(qreal factor) | 
| 443 | { | 
| 444 |     int ifactor = qRound(d: factor); | 
| 445 |     if (qFuzzyCompare(p1: factor, p2: ifactor )) { | 
| 446 |         if (ifactor  == 1 || ifactor  == 0) | 
| 447 |             return Qt::StretchTile; | 
| 448 |         return Qt::RoundTile; | 
| 449 |     } | 
| 450 |     return Qt::RepeatTile; | 
| 451 | } | 
| 452 |  | 
| 453 |  | 
| 454 | void QSGSoftwareInternalImageNode::paint(QPainter *painter) | 
| 455 | { | 
| 456 |     painter->setRenderHint(hint: QPainter::SmoothPixmapTransform, on: m_smooth); | 
| 457 |     // Disable antialiased clipping. It causes transformed tiles to have gaps. | 
| 458 |     painter->setRenderHint(hint: QPainter::Antialiasing, on: false); | 
| 459 |  | 
| 460 |     const QPixmap &pm = m_mirror || m_textureIsLayer ? m_cachedMirroredPixmap : pixmap(); | 
| 461 |  | 
| 462 |     if (m_innerTargetRect != m_targetRect) { | 
| 463 |         // border image | 
| 464 |         QMargins margins(m_innerTargetRect.left() - m_targetRect.left(), m_innerTargetRect.top() - m_targetRect.top(), | 
| 465 |                          m_targetRect.right() - m_innerTargetRect.right(), m_targetRect.bottom() - m_innerTargetRect.bottom()); | 
| 466 |         QSGSoftwareHelpers::QTileRules tilerules(getTileRule(factor: m_subSourceRect.width()), getTileRule(factor: m_subSourceRect.height())); | 
| 467 |         QSGSoftwareHelpers::qDrawBorderPixmap(painter, targetRect: m_targetRect.toRect(), targetMarginsIn: margins, pixmap: pm, sourceRect: QRect(0, 0, pm.width(), pm.height()), | 
| 468 |                                               sourceMarginsIn: margins, rules: tilerules, hints: QSGSoftwareHelpers::QDrawBorderPixmap::DrawingHints{}); | 
| 469 |         return; | 
| 470 |     } | 
| 471 |  | 
| 472 |     if (m_tileHorizontal || m_tileVertical) { | 
| 473 |         painter->save(); | 
| 474 |         qreal sx = m_targetRect.width()/(m_subSourceRect.width()*pm.width()); | 
| 475 |         qreal sy = m_targetRect.height()/(m_subSourceRect.height()*pm.height()); | 
| 476 |         painter->setTransform(transform: QTransform::fromScale(dx: sx, dy: sy), combine: true); | 
| 477 |         painter->drawTiledPixmap(rect: QRectF(m_targetRect.x()/sx, m_targetRect.y()/sy, m_targetRect.width()/sx, m_targetRect.height()/sy), | 
| 478 |                                  pm, | 
| 479 |                                  offset: QPointF(m_subSourceRect.left()*pm.width(), m_subSourceRect.top()*pm.height())); | 
| 480 |         painter->restore(); | 
| 481 |     } else { | 
| 482 |         QRectF sr(m_subSourceRect.left()*pm.width(), m_subSourceRect.top()*pm.height(), | 
| 483 |                   m_subSourceRect.width()*pm.width(), m_subSourceRect.height()*pm.height()); | 
| 484 |         painter->drawPixmap(targetRect: m_targetRect, pixmap: pm, sourceRect: sr); | 
| 485 |     } | 
| 486 | } | 
| 487 |  | 
| 488 |  | 
| 489 | QRectF QSGSoftwareInternalImageNode::rect() const | 
| 490 | { | 
| 491 |     return m_targetRect; | 
| 492 | } | 
| 493 |  | 
| 494 | const QPixmap &QSGSoftwareInternalImageNode::pixmap() const | 
| 495 | { | 
| 496 |     if (QSGSoftwarePixmapTexture *pt = qobject_cast<QSGSoftwarePixmapTexture*>(object: m_texture)) | 
| 497 |         return pt->pixmap(); | 
| 498 |     if (QSGSoftwareLayer *layer = qobject_cast<QSGSoftwareLayer*>(object: m_texture)) | 
| 499 |         return layer->pixmap(); | 
| 500 |     Q_ASSERT(m_texture == nullptr); | 
| 501 |     static const QPixmap nullPixmap; | 
| 502 |     return nullPixmap; | 
| 503 | } | 
| 504 |  | 
| 505 | QT_END_NAMESPACE | 
| 506 |  |