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