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 "qsgbasicinternalrectanglenode_p.h" |
41 | |
42 | #include <QtCore/qmath.h> |
43 | |
44 | QT_BEGIN_NAMESPACE |
45 | |
46 | namespace |
47 | { |
48 | struct Color4ub |
49 | { |
50 | unsigned char r, g, b, a; |
51 | }; |
52 | |
53 | Color4ub operator *(Color4ub c, float t) { c.a *= t; c.r *= t; c.g *= t; c.b *= t; return c; } |
54 | Color4ub operator +(Color4ub a, Color4ub b) { a.a += b.a; a.r += b.r; a.g += b.g; a.b += b.b; return a; } |
55 | |
56 | inline Color4ub colorToColor4ub(const QColor &c) |
57 | { |
58 | Color4ub color = { .r: uchar(qRound(d: c.redF() * c.alphaF() * 255)), |
59 | .g: uchar(qRound(d: c.greenF() * c.alphaF() * 255)), |
60 | .b: uchar(qRound(d: c.blueF() * c.alphaF() * 255)), |
61 | .a: uchar(qRound(d: c.alphaF() * 255)) |
62 | }; |
63 | return color; |
64 | } |
65 | |
66 | // Same layout as QSGGeometry::ColoredPoint2D, but uses Color4ub for convenience. |
67 | struct Vertex |
68 | { |
69 | float x, y; |
70 | Color4ub color; |
71 | |
72 | void set(float primary, float secondary, Color4ub ncolor, bool vertical) |
73 | { |
74 | if (vertical) { |
75 | x = secondary; y = primary; |
76 | } else { |
77 | x = primary; y = secondary; |
78 | } |
79 | color = ncolor; |
80 | } |
81 | }; |
82 | |
83 | struct SmoothVertex : public Vertex |
84 | { |
85 | float dx, dy; |
86 | |
87 | void set(float primary, float secondary, Color4ub ncolor, float dPrimary, float dSecondary, bool vertical) |
88 | { |
89 | Vertex::set(primary, secondary, ncolor, vertical); |
90 | if (vertical) { |
91 | dx = dSecondary; dy = dPrimary; |
92 | } else { |
93 | dx = dPrimary; dy = dSecondary; |
94 | } |
95 | } |
96 | }; |
97 | |
98 | const QSGGeometry::AttributeSet &smoothAttributeSet() |
99 | { |
100 | static QSGGeometry::Attribute data[] = { |
101 | QSGGeometry::Attribute::createWithAttributeType(pos: 0, tupleSize: 2, primitiveType: QSGGeometry::FloatType, attributeType: QSGGeometry::PositionAttribute), |
102 | QSGGeometry::Attribute::createWithAttributeType(pos: 1, tupleSize: 4, primitiveType: QSGGeometry::UnsignedByteType, attributeType: QSGGeometry::ColorAttribute), |
103 | QSGGeometry::Attribute::createWithAttributeType(pos: 2, tupleSize: 2, primitiveType: QSGGeometry::FloatType, attributeType: QSGGeometry::TexCoordAttribute) |
104 | }; |
105 | static QSGGeometry::AttributeSet attrs = { .count: 3, .stride: sizeof(SmoothVertex), .attributes: data }; |
106 | return attrs; |
107 | } |
108 | } |
109 | |
110 | QSGBasicInternalRectangleNode::QSGBasicInternalRectangleNode() |
111 | : m_radius(0) |
112 | , m_pen_width(0) |
113 | , m_aligned(true) |
114 | , m_antialiasing(false) |
115 | , m_gradient_is_opaque(true) |
116 | , m_dirty_geometry(false) |
117 | , m_gradient_is_vertical(true) |
118 | , m_geometry(QSGGeometry::defaultAttributes_ColoredPoint2D(), 0) |
119 | { |
120 | setGeometry(&m_geometry); |
121 | |
122 | #ifdef QSG_RUNTIME_DESCRIPTION |
123 | qsgnode_set_description(node: this, description: QLatin1String("internalrectangle" )); |
124 | #endif |
125 | } |
126 | |
127 | void QSGBasicInternalRectangleNode::setRect(const QRectF &rect) |
128 | { |
129 | if (rect == m_rect) |
130 | return; |
131 | m_rect = rect; |
132 | m_dirty_geometry = true; |
133 | } |
134 | |
135 | void QSGBasicInternalRectangleNode::setColor(const QColor &color) |
136 | { |
137 | if (color == m_color) |
138 | return; |
139 | m_color = color; |
140 | if (m_gradient_stops.isEmpty()) |
141 | m_dirty_geometry = true; |
142 | } |
143 | |
144 | void QSGBasicInternalRectangleNode::setPenColor(const QColor &color) |
145 | { |
146 | if (color == m_border_color) |
147 | return; |
148 | m_border_color = color; |
149 | if (m_pen_width > 0) |
150 | m_dirty_geometry = true; |
151 | } |
152 | |
153 | void QSGBasicInternalRectangleNode::setPenWidth(qreal width) |
154 | { |
155 | if (width == m_pen_width) |
156 | return; |
157 | m_pen_width = width; |
158 | m_dirty_geometry = true; |
159 | } |
160 | |
161 | |
162 | void QSGBasicInternalRectangleNode::setGradientStops(const QGradientStops &stops) |
163 | { |
164 | if (stops.constData() == m_gradient_stops.constData()) |
165 | return; |
166 | |
167 | m_gradient_stops = stops; |
168 | |
169 | m_gradient_is_opaque = true; |
170 | for (int i = 0; i < stops.size(); ++i) |
171 | m_gradient_is_opaque &= stops.at(i).second.alpha() == 0xff; |
172 | m_dirty_geometry = true; |
173 | } |
174 | |
175 | void QSGBasicInternalRectangleNode::setGradientVertical(bool vertical) |
176 | { |
177 | if (vertical == m_gradient_is_vertical) |
178 | return; |
179 | m_gradient_is_vertical = vertical; |
180 | m_dirty_geometry = true; |
181 | } |
182 | |
183 | |
184 | void QSGBasicInternalRectangleNode::setRadius(qreal radius) |
185 | { |
186 | if (radius == m_radius) |
187 | return; |
188 | m_radius = radius; |
189 | m_dirty_geometry = true; |
190 | } |
191 | |
192 | void QSGBasicInternalRectangleNode::setAntialiasing(bool antialiasing) |
193 | { |
194 | if (!supportsAntialiasing()) |
195 | return; |
196 | |
197 | if (antialiasing == m_antialiasing) |
198 | return; |
199 | m_antialiasing = antialiasing; |
200 | if (m_antialiasing) { |
201 | setGeometry(new QSGGeometry(smoothAttributeSet(), 0)); |
202 | setFlag(OwnsGeometry, true); |
203 | } else { |
204 | setGeometry(&m_geometry); |
205 | setFlag(OwnsGeometry, false); |
206 | } |
207 | updateMaterialAntialiasing(); |
208 | m_dirty_geometry = true; |
209 | } |
210 | |
211 | void QSGBasicInternalRectangleNode::setAligned(bool aligned) |
212 | { |
213 | if (aligned == m_aligned) |
214 | return; |
215 | m_aligned = aligned; |
216 | m_dirty_geometry = true; |
217 | } |
218 | |
219 | void QSGBasicInternalRectangleNode::update() |
220 | { |
221 | if (m_dirty_geometry) { |
222 | updateGeometry(); |
223 | m_dirty_geometry = false; |
224 | |
225 | QSGNode::DirtyState state = QSGNode::DirtyGeometry; |
226 | updateMaterialBlending(state: &state); |
227 | markDirty(bits: state); |
228 | } |
229 | } |
230 | |
231 | void QSGBasicInternalRectangleNode::updateGeometry() |
232 | { |
233 | float width = float(m_rect.width()); |
234 | float height = float(m_rect.height()); |
235 | float penWidth = qMin(a: qMin(a: width, b: height) * 0.5f, b: float(m_pen_width)); |
236 | |
237 | if (m_aligned) |
238 | penWidth = qRound(d: penWidth); |
239 | |
240 | QSGGeometry *g = geometry(); |
241 | g->setDrawingMode(QSGGeometry::DrawTriangleStrip); |
242 | int vertexStride = g->sizeOfVertex(); |
243 | |
244 | union { |
245 | Vertex *vertices; |
246 | SmoothVertex *smoothVertices; |
247 | }; |
248 | |
249 | Color4ub fillColor = colorToColor4ub(c: m_color); |
250 | Color4ub borderColor = colorToColor4ub(c: m_border_color); |
251 | Color4ub transparent = { .r: 0, .g: 0, .b: 0, .a: 0 }; |
252 | const QGradientStops &stops = m_gradient_stops; |
253 | |
254 | float length = (m_gradient_is_vertical ? height : width); |
255 | float secondaryLength = (m_gradient_is_vertical ? width : height); |
256 | |
257 | int nextGradientStop = 0; |
258 | float gradientPos = penWidth / length; |
259 | while (nextGradientStop < stops.size() && stops.at(i: nextGradientStop).first <= gradientPos) |
260 | ++nextGradientStop; |
261 | int lastGradientStop = stops.size() - 1; |
262 | float lastGradientPos = 1.0f - penWidth / length; |
263 | while (lastGradientStop >= nextGradientStop && stops.at(i: lastGradientStop).first >= lastGradientPos) |
264 | --lastGradientStop; |
265 | int gradientIntersections = (lastGradientStop - nextGradientStop + 1); |
266 | |
267 | if (m_radius > 0) { |
268 | // Rounded corners. |
269 | |
270 | // Radius should never exceeds half of the width or half of the height |
271 | float radius = qMin(a: qMin(a: width, b: height) * 0.5f, b: float(m_radius)); |
272 | QRectF innerRect = m_rect; |
273 | innerRect.adjust(xp1: radius, yp1: radius, xp2: -radius, yp2: -radius); |
274 | |
275 | float innerRadius = radius - penWidth * 1.0f; |
276 | float outerRadius = radius; |
277 | float delta = qMin(a: width, b: height) * 0.5f; |
278 | |
279 | // Number of segments per corner, approximately one per 3 pixels. |
280 | int segments = qBound(min: 3, val: qCeil(v: outerRadius * (M_PI / 6)), max: 18); |
281 | |
282 | /* |
283 | |
284 | --+--__ |
285 | --+--__--__ |
286 | | --__--__ |
287 | | seg --__--+ |
288 | --+-__ ment _+ \ |
289 | --+-__--__ - \ \ |
290 | --__--+ se \ \ |
291 | + \ g \ \ |
292 | \ \ m \ \ |
293 | -----------+--+ e \ \ <- gradient line |
294 | \ \ nt\ \ |
295 | fill +--+----+--+ |
296 | | | | | |
297 | border |
298 | inner AA outer AA (AA = antialiasing) |
299 | |
300 | */ |
301 | |
302 | int innerVertexCount = (segments + 1) * 4 + gradientIntersections * 2; |
303 | int outerVertexCount = (segments + 1) * 4; |
304 | int vertexCount = innerVertexCount; |
305 | if (m_antialiasing || penWidth) |
306 | vertexCount += innerVertexCount; |
307 | if (penWidth) |
308 | vertexCount += outerVertexCount; |
309 | if (m_antialiasing && penWidth) |
310 | vertexCount += outerVertexCount; |
311 | |
312 | int fillIndexCount = innerVertexCount; |
313 | int innerAAIndexCount = innerVertexCount * 2 + 2; |
314 | int borderIndexCount = innerVertexCount * 2 + 2; |
315 | int outerAAIndexCount = outerVertexCount * 2 + 2; |
316 | int indexCount = 0; |
317 | int fillHead = 0; |
318 | int innerAAHead = 0; |
319 | int innerAATail = 0; |
320 | int borderHead = 0; |
321 | int borderTail = 0; |
322 | int outerAAHead = 0; |
323 | int outerAATail = 0; |
324 | bool hasFill = m_color.alpha() > 0 || !stops.isEmpty(); |
325 | if (hasFill) |
326 | indexCount += fillIndexCount; |
327 | if (m_antialiasing) { |
328 | innerAATail = innerAAHead = indexCount + (innerAAIndexCount >> 1) + 1; |
329 | indexCount += innerAAIndexCount; |
330 | } |
331 | if (penWidth) { |
332 | borderTail = borderHead = indexCount + (borderIndexCount >> 1) + 1; |
333 | indexCount += borderIndexCount; |
334 | } |
335 | if (m_antialiasing && penWidth) { |
336 | outerAATail = outerAAHead = indexCount + (outerAAIndexCount >> 1) + 1; |
337 | indexCount += outerAAIndexCount; |
338 | } |
339 | |
340 | g->allocate(vertexCount, indexCount); |
341 | vertices = reinterpret_cast<Vertex *>(g->vertexData()); |
342 | memset(s: vertices, c: 0, n: vertexCount * vertexStride); |
343 | quint16 *indices = g->indexDataAsUShort(); |
344 | quint16 index = 0; |
345 | |
346 | float pp = 0; // previous inner primary coordinate. |
347 | float pss = 0; // previous inner secondary start coordinate. |
348 | float pse = 0; // previous inner secondary end coordinate. |
349 | |
350 | float angle = 0.5f * float(M_PI) / segments; |
351 | float cosStep = qFastCos(x: angle); |
352 | float sinStep = qFastSin(x: angle); |
353 | |
354 | float innerStart = (m_gradient_is_vertical ? innerRect.top() : innerRect.left()); |
355 | float innerEnd = (m_gradient_is_vertical ? innerRect.bottom() : innerRect.right()); |
356 | float innerLength = (m_gradient_is_vertical ? innerRect.height() : innerRect.width()); |
357 | float innerSecondaryStart = (m_gradient_is_vertical ? innerRect.left() : innerRect.top()); |
358 | float innerSecondaryEnd = (m_gradient_is_vertical ? innerRect.right() : innerRect.bottom()); |
359 | |
360 | for (int part = 0; part < 2; ++part) { |
361 | float c = 1 - part; |
362 | float s = part; |
363 | for (int i = 0; i <= segments; ++i) { |
364 | float p, ss, se; |
365 | if (innerRadius > 0) { |
366 | p = (part ? innerEnd : innerStart) - innerRadius * c; // current inner primary coordinate. |
367 | ss = innerSecondaryStart - innerRadius * s; // current inner secondary start coordinate. |
368 | se = innerSecondaryEnd + innerRadius * s; // current inner secondary end coordinate. |
369 | gradientPos = ((part ? innerLength : 0) + radius - innerRadius * c) / length; |
370 | } else { |
371 | p = (part ? innerEnd + innerRadius : innerStart - innerRadius); // current inner primary coordinate. |
372 | ss = innerSecondaryStart - innerRadius; // current inner secondary start coordinate. |
373 | se = innerSecondaryEnd + innerRadius; // current inner secondary end coordinate. |
374 | gradientPos = ((part ? innerLength + innerRadius : -innerRadius) + radius) / length; |
375 | } |
376 | float outerEdge = (part ? innerEnd : innerStart) - outerRadius * c; // current outer primary coordinate. |
377 | float outerSecondaryStart = innerSecondaryStart - outerRadius * s; // current outer secondary start coordinate. |
378 | float outerSecondaryEnd = innerSecondaryEnd + outerRadius * s; // current outer secondary end coordinate. |
379 | |
380 | while (nextGradientStop <= lastGradientStop && stops.at(i: nextGradientStop).first <= gradientPos) { |
381 | // Insert vertices at gradient stops. |
382 | float gp = (innerStart - radius) + stops.at(i: nextGradientStop).first * length; |
383 | float t = (gp - pp) / (p - pp); |
384 | float gis = pss * (1 - t) + t * ss; // gradient inner start |
385 | float gie = pse * (1 - t) + t * se; // gradient inner end |
386 | |
387 | fillColor = colorToColor4ub(c: stops.at(i: nextGradientStop).second); |
388 | |
389 | if (hasFill) { |
390 | indices[fillHead++] = index; |
391 | indices[fillHead++] = index + 1; |
392 | } |
393 | |
394 | if (penWidth) { |
395 | --borderHead; |
396 | indices[borderHead] = indices[borderHead + 2]; |
397 | indices[--borderHead] = index + 2; |
398 | indices[borderTail++] = index + 3; |
399 | indices[borderTail] = indices[borderTail - 2]; |
400 | ++borderTail; |
401 | } |
402 | |
403 | if (m_antialiasing) { |
404 | indices[--innerAAHead] = index + 2; |
405 | indices[--innerAAHead] = index; |
406 | indices[innerAATail++] = index + 1; |
407 | indices[innerAATail++] = index + 3; |
408 | |
409 | bool lower = stops.at(i: nextGradientStop).first > 0.5f; |
410 | float dp = lower ? qMin(a: 0.0f, b: length - gp - delta) : qMax(a: 0.0f, b: delta - gp); |
411 | smoothVertices[index++].set(primary: gp, secondary: gie, ncolor: fillColor, dPrimary: dp, dSecondary: secondaryLength - gie - delta, vertical: m_gradient_is_vertical); |
412 | smoothVertices[index++].set(primary: gp, secondary: gis, ncolor: fillColor, dPrimary: dp, dSecondary: delta - gis, vertical: m_gradient_is_vertical); |
413 | if (penWidth) { |
414 | smoothVertices[index++].set(primary: gp, secondary: gie, ncolor: borderColor, dPrimary: -0.49f * penWidth * c, dSecondary: 0.49f * penWidth * s, vertical: m_gradient_is_vertical); |
415 | smoothVertices[index++].set(primary: gp, secondary: gis, ncolor: borderColor, dPrimary: -0.49f * penWidth * c, dSecondary: -0.49f * penWidth * s, vertical: m_gradient_is_vertical); |
416 | } else { |
417 | dp = lower ? delta : -delta; |
418 | smoothVertices[index++].set(primary: gp, secondary: gie, ncolor: transparent, dPrimary: dp, dSecondary: delta, vertical: m_gradient_is_vertical); |
419 | smoothVertices[index++].set(primary: gp, secondary: gis, ncolor: transparent, dPrimary: dp, dSecondary: -delta, vertical: m_gradient_is_vertical); |
420 | } |
421 | } else { |
422 | vertices[index++].set(primary: gp, secondary: gie, ncolor: fillColor, vertical: m_gradient_is_vertical); |
423 | vertices[index++].set(primary: gp, secondary: gis, ncolor: fillColor, vertical: m_gradient_is_vertical); |
424 | if (penWidth) { |
425 | vertices[index++].set(primary: gp, secondary: gie, ncolor: borderColor, vertical: m_gradient_is_vertical); |
426 | vertices[index++].set(primary: gp, secondary: gis, ncolor: borderColor, vertical: m_gradient_is_vertical); |
427 | } |
428 | } |
429 | ++nextGradientStop; |
430 | } |
431 | |
432 | if (!stops.isEmpty()) { |
433 | if (nextGradientStop == 0) { |
434 | fillColor = colorToColor4ub(c: stops.at(i: 0).second); |
435 | } else if (nextGradientStop == stops.size()) { |
436 | fillColor = colorToColor4ub(c: stops.last().second); |
437 | } else { |
438 | const QGradientStop &prev = stops.at(i: nextGradientStop - 1); |
439 | const QGradientStop &next = stops.at(i: nextGradientStop); |
440 | float t = (gradientPos - prev.first) / (next.first - prev.first); |
441 | fillColor = colorToColor4ub(c: prev.second) * (1 - t) + colorToColor4ub(c: next.second) * t; |
442 | } |
443 | } |
444 | |
445 | if (hasFill) { |
446 | indices[fillHead++] = index; |
447 | indices[fillHead++] = index + 1; |
448 | } |
449 | |
450 | if (penWidth) { |
451 | indices[--borderHead] = index + 4; |
452 | indices[--borderHead] = index + 2; |
453 | indices[borderTail++] = index + 3; |
454 | indices[borderTail++] = index + 5; |
455 | } |
456 | |
457 | if (m_antialiasing) { |
458 | indices[--innerAAHead] = index + 2; |
459 | indices[--innerAAHead] = index; |
460 | indices[innerAATail++] = index + 1; |
461 | indices[innerAATail++] = index + 3; |
462 | |
463 | float dp = part ? qMin(a: 0.0f, b: length - p - delta) : qMax(a: 0.0f, b: delta - p); |
464 | smoothVertices[index++].set(primary: p, secondary: se, ncolor: fillColor, dPrimary: dp, dSecondary: secondaryLength - se - delta, vertical: m_gradient_is_vertical); |
465 | smoothVertices[index++].set(primary: p, secondary: ss, ncolor: fillColor, dPrimary: dp, dSecondary: delta - ss, vertical: m_gradient_is_vertical); |
466 | |
467 | dp = part ? delta : -delta; |
468 | if (penWidth) { |
469 | smoothVertices[index++].set(primary: p, secondary: se, ncolor: borderColor, dPrimary: -0.49f * penWidth * c, dSecondary: 0.49f * penWidth * s, vertical: m_gradient_is_vertical); |
470 | smoothVertices[index++].set(primary: p, secondary: ss, ncolor: borderColor, dPrimary: -0.49f * penWidth * c, dSecondary: -0.49f * penWidth * s, vertical: m_gradient_is_vertical); |
471 | smoothVertices[index++].set(primary: outerEdge, secondary: outerSecondaryEnd, ncolor: borderColor, dPrimary: 0.49f * penWidth * c, dSecondary: -0.49f * penWidth * s, vertical: m_gradient_is_vertical); |
472 | smoothVertices[index++].set(primary: outerEdge, secondary: outerSecondaryStart, ncolor: borderColor, dPrimary: 0.49f * penWidth * c, dSecondary: 0.49f * penWidth * s, vertical: m_gradient_is_vertical); |
473 | smoothVertices[index++].set(primary: outerEdge, secondary: outerSecondaryEnd, ncolor: transparent, dPrimary: dp, dSecondary: delta, vertical: m_gradient_is_vertical); |
474 | smoothVertices[index++].set(primary: outerEdge, secondary: outerSecondaryStart, ncolor: transparent, dPrimary: dp, dSecondary: -delta, vertical: m_gradient_is_vertical); |
475 | |
476 | indices[--outerAAHead] = index - 2; |
477 | indices[--outerAAHead] = index - 4; |
478 | indices[outerAATail++] = index - 3; |
479 | indices[outerAATail++] = index - 1; |
480 | } else { |
481 | smoothVertices[index++].set(primary: p, secondary: se, ncolor: transparent, dPrimary: dp, dSecondary: delta, vertical: m_gradient_is_vertical); |
482 | smoothVertices[index++].set(primary: p, secondary: ss, ncolor: transparent, dPrimary: dp, dSecondary: -delta, vertical: m_gradient_is_vertical); |
483 | } |
484 | } else { |
485 | vertices[index++].set(primary: p, secondary: se, ncolor: fillColor, vertical: m_gradient_is_vertical); |
486 | vertices[index++].set(primary: p, secondary: ss, ncolor: fillColor, vertical: m_gradient_is_vertical); |
487 | if (penWidth) { |
488 | vertices[index++].set(primary: p, secondary: se, ncolor: borderColor, vertical: m_gradient_is_vertical); |
489 | vertices[index++].set(primary: p, secondary: ss, ncolor: borderColor, vertical: m_gradient_is_vertical); |
490 | vertices[index++].set(primary: outerEdge, secondary: outerSecondaryEnd, ncolor: borderColor, vertical: m_gradient_is_vertical); |
491 | vertices[index++].set(primary: outerEdge, secondary: outerSecondaryStart, ncolor: borderColor, vertical: m_gradient_is_vertical); |
492 | } |
493 | } |
494 | |
495 | pp = p; |
496 | pss = ss; |
497 | pse = se; |
498 | |
499 | // Rotate |
500 | qreal tmp = c; |
501 | c = c * cosStep - s * sinStep; |
502 | s = s * cosStep + tmp * sinStep; |
503 | } |
504 | } |
505 | Q_ASSERT(index == vertexCount); |
506 | |
507 | // Close the triangle strips. |
508 | if (m_antialiasing) { |
509 | indices[--innerAAHead] = indices[innerAATail - 1]; |
510 | indices[--innerAAHead] = indices[innerAATail - 2]; |
511 | Q_ASSERT(innerAATail <= indexCount); |
512 | } |
513 | if (penWidth) { |
514 | indices[--borderHead] = indices[borderTail - 1]; |
515 | indices[--borderHead] = indices[borderTail - 2]; |
516 | Q_ASSERT(borderTail <= indexCount); |
517 | } |
518 | if (m_antialiasing && penWidth) { |
519 | indices[--outerAAHead] = indices[outerAATail - 1]; |
520 | indices[--outerAAHead] = indices[outerAATail - 2]; |
521 | Q_ASSERT(outerAATail == indexCount); |
522 | } |
523 | } else { |
524 | // Straight corners. |
525 | QRectF innerRect = m_rect; |
526 | QRectF outerRect = m_rect; |
527 | |
528 | if (penWidth) |
529 | innerRect.adjust(xp1: 1.0f * penWidth, yp1: 1.0f * penWidth, xp2: -1.0f * penWidth, yp2: -1.0f * penWidth); |
530 | |
531 | float delta = qMin(a: width, b: height) * 0.5f; |
532 | int innerVertexCount = 4 + gradientIntersections * 2; |
533 | int outerVertexCount = 4; |
534 | int vertexCount = innerVertexCount; |
535 | if (m_antialiasing || penWidth) |
536 | vertexCount += innerVertexCount; |
537 | if (penWidth) |
538 | vertexCount += outerVertexCount; |
539 | if (m_antialiasing && penWidth) |
540 | vertexCount += outerVertexCount; |
541 | |
542 | int fillIndexCount = innerVertexCount; |
543 | int innerAAIndexCount = innerVertexCount * 2 + 2; |
544 | int borderIndexCount = innerVertexCount * 2 + 2; |
545 | int outerAAIndexCount = outerVertexCount * 2 + 2; |
546 | int indexCount = 0; |
547 | int fillHead = 0; |
548 | int innerAAHead = 0; |
549 | int innerAATail = 0; |
550 | int borderHead = 0; |
551 | int borderTail = 0; |
552 | int outerAAHead = 0; |
553 | int outerAATail = 0; |
554 | bool hasFill = m_color.alpha() > 0 || !stops.isEmpty(); |
555 | if (hasFill) |
556 | indexCount += fillIndexCount; |
557 | if (m_antialiasing) { |
558 | innerAATail = innerAAHead = indexCount + (innerAAIndexCount >> 1) + 1; |
559 | indexCount += innerAAIndexCount; |
560 | } |
561 | if (penWidth) { |
562 | borderTail = borderHead = indexCount + (borderIndexCount >> 1) + 1; |
563 | indexCount += borderIndexCount; |
564 | } |
565 | if (m_antialiasing && penWidth) { |
566 | outerAATail = outerAAHead = indexCount + (outerAAIndexCount >> 1) + 1; |
567 | indexCount += outerAAIndexCount; |
568 | } |
569 | |
570 | g->allocate(vertexCount, indexCount); |
571 | vertices = reinterpret_cast<Vertex *>(g->vertexData()); |
572 | memset(s: vertices, c: 0, n: vertexCount * vertexStride); |
573 | quint16 *indices = g->indexDataAsUShort(); |
574 | quint16 index = 0; |
575 | |
576 | float innerStart = (m_gradient_is_vertical ? innerRect.top() : innerRect.left()); |
577 | float innerEnd = (m_gradient_is_vertical ? innerRect.bottom() : innerRect.right()); |
578 | float outerStart = (m_gradient_is_vertical ? outerRect.top() : outerRect.left()); |
579 | float outerEnd = (m_gradient_is_vertical ? outerRect.bottom() : outerRect.right()); |
580 | |
581 | float innerSecondaryStart = (m_gradient_is_vertical ? innerRect.left() : innerRect.top()); |
582 | float innerSecondaryEnd = (m_gradient_is_vertical ? innerRect.right() : innerRect.bottom()); |
583 | float outerSecondaryStart = (m_gradient_is_vertical ? outerRect.left() : outerRect.top()); |
584 | float outerSecondaryEnd = (m_gradient_is_vertical ? outerRect.right() : outerRect.bottom()); |
585 | |
586 | for (int part = -1; part <= 1; part += 2) { |
587 | float innerEdge = (part == 1 ? innerEnd : innerStart); |
588 | float outerEdge = (part == 1 ? outerEnd : outerStart); |
589 | gradientPos = (innerEdge - innerStart + penWidth) / length; |
590 | |
591 | while (nextGradientStop <= lastGradientStop && stops.at(i: nextGradientStop).first <= gradientPos) { |
592 | // Insert vertices at gradient stops. |
593 | float gp = (innerStart - penWidth) + stops.at(i: nextGradientStop).first * length; |
594 | |
595 | fillColor = colorToColor4ub(c: stops.at(i: nextGradientStop).second); |
596 | |
597 | if (hasFill) { |
598 | indices[fillHead++] = index; |
599 | indices[fillHead++] = index + 1; |
600 | } |
601 | |
602 | if (penWidth) { |
603 | --borderHead; |
604 | indices[borderHead] = indices[borderHead + 2]; |
605 | indices[--borderHead] = index + 2; |
606 | indices[borderTail++] = index + 3; |
607 | indices[borderTail] = indices[borderTail - 2]; |
608 | ++borderTail; |
609 | } |
610 | |
611 | if (m_antialiasing) { |
612 | indices[--innerAAHead] = index + 2; |
613 | indices[--innerAAHead] = index; |
614 | indices[innerAATail++] = index + 1; |
615 | indices[innerAATail++] = index + 3; |
616 | |
617 | bool lower = stops.at(i: nextGradientStop).first > 0.5f; |
618 | float dp = lower ? qMin(a: 0.0f, b: length - gp - delta) : qMax(a: 0.0f, b: delta - gp); |
619 | smoothVertices[index++].set(primary: gp, secondary: innerSecondaryEnd, ncolor: fillColor, dPrimary: dp, dSecondary: secondaryLength - innerSecondaryEnd - delta, vertical: m_gradient_is_vertical); |
620 | smoothVertices[index++].set(primary: gp, secondary: innerSecondaryStart, ncolor: fillColor, dPrimary: dp, dSecondary: delta - innerSecondaryStart, vertical: m_gradient_is_vertical); |
621 | if (penWidth) { |
622 | smoothVertices[index++].set(primary: gp, secondary: innerSecondaryEnd, ncolor: borderColor, dPrimary: (lower ? 0.49f : -0.49f) * penWidth, dSecondary: 0.49f * penWidth, vertical: m_gradient_is_vertical); |
623 | smoothVertices[index++].set(primary: gp, secondary: innerSecondaryStart, ncolor: borderColor, dPrimary: (lower ? 0.49f : -0.49f) * penWidth, dSecondary: -0.49f * penWidth, vertical: m_gradient_is_vertical); |
624 | } else { |
625 | smoothVertices[index++].set(primary: gp, secondary: innerSecondaryEnd, ncolor: transparent, dPrimary: lower ? delta : -delta, dSecondary: delta, vertical: m_gradient_is_vertical); |
626 | smoothVertices[index++].set(primary: gp, secondary: innerSecondaryStart, ncolor: transparent, dPrimary: lower ? delta : -delta, dSecondary: -delta, vertical: m_gradient_is_vertical); |
627 | } |
628 | } else { |
629 | vertices[index++].set(primary: gp, secondary: innerSecondaryEnd, ncolor: fillColor, vertical: m_gradient_is_vertical); |
630 | vertices[index++].set(primary: gp, secondary: innerSecondaryStart, ncolor: fillColor, vertical: m_gradient_is_vertical); |
631 | if (penWidth) { |
632 | vertices[index++].set(primary: gp, secondary: innerSecondaryEnd, ncolor: borderColor, vertical: m_gradient_is_vertical); |
633 | vertices[index++].set(primary: gp, secondary: innerSecondaryStart, ncolor: borderColor, vertical: m_gradient_is_vertical); |
634 | } |
635 | } |
636 | ++nextGradientStop; |
637 | } |
638 | |
639 | if (!stops.isEmpty()) { |
640 | if (nextGradientStop == 0) { |
641 | fillColor = colorToColor4ub(c: stops.at(i: 0).second); |
642 | } else if (nextGradientStop == stops.size()) { |
643 | fillColor = colorToColor4ub(c: stops.last().second); |
644 | } else { |
645 | const QGradientStop &prev = stops.at(i: nextGradientStop - 1); |
646 | const QGradientStop &next = stops.at(i: nextGradientStop); |
647 | float t = (gradientPos - prev.first) / (next.first - prev.first); |
648 | fillColor = colorToColor4ub(c: prev.second) * (1 - t) + colorToColor4ub(c: next.second) * t; |
649 | } |
650 | } |
651 | |
652 | if (hasFill) { |
653 | indices[fillHead++] = index; |
654 | indices[fillHead++] = index + 1; |
655 | } |
656 | |
657 | if (penWidth) { |
658 | indices[--borderHead] = index + 4; |
659 | indices[--borderHead] = index + 2; |
660 | indices[borderTail++] = index + 3; |
661 | indices[borderTail++] = index + 5; |
662 | } |
663 | |
664 | if (m_antialiasing) { |
665 | indices[--innerAAHead] = index + 2; |
666 | indices[--innerAAHead] = index; |
667 | indices[innerAATail++] = index + 1; |
668 | indices[innerAATail++] = index + 3; |
669 | |
670 | float dp = part == 1 ? qMin(a: 0.0f, b: length - innerEdge - delta) : qMax(a: 0.0f, b: delta - innerEdge); |
671 | smoothVertices[index++].set(primary: innerEdge, secondary: innerSecondaryEnd, ncolor: fillColor, dPrimary: dp, dSecondary: secondaryLength - innerSecondaryEnd - delta, vertical: m_gradient_is_vertical); |
672 | smoothVertices[index++].set(primary: innerEdge, secondary: innerSecondaryStart, ncolor: fillColor, dPrimary: dp, dSecondary: delta - innerSecondaryStart, vertical: m_gradient_is_vertical); |
673 | |
674 | if (penWidth) { |
675 | smoothVertices[index++].set(primary: innerEdge, secondary: innerSecondaryEnd, ncolor: borderColor, dPrimary: 0.49f * penWidth * part, dSecondary: 0.49f * penWidth, vertical: m_gradient_is_vertical); |
676 | smoothVertices[index++].set(primary: innerEdge, secondary: innerSecondaryStart, ncolor: borderColor, dPrimary: 0.49f * penWidth * part, dSecondary: -0.49f * penWidth, vertical: m_gradient_is_vertical); |
677 | smoothVertices[index++].set(primary: outerEdge, secondary: outerSecondaryEnd, ncolor: borderColor, dPrimary: -0.49f * penWidth * part, dSecondary: -0.49f * penWidth, vertical: m_gradient_is_vertical); |
678 | smoothVertices[index++].set(primary: outerEdge, secondary: outerSecondaryStart, ncolor: borderColor, dPrimary: -0.49f * penWidth * part, dSecondary: 0.49f * penWidth, vertical: m_gradient_is_vertical); |
679 | smoothVertices[index++].set(primary: outerEdge, secondary: outerSecondaryEnd, ncolor: transparent, dPrimary: delta * part, dSecondary: delta, vertical: m_gradient_is_vertical); |
680 | smoothVertices[index++].set(primary: outerEdge, secondary: outerSecondaryStart, ncolor: transparent, dPrimary: delta * part, dSecondary: -delta, vertical: m_gradient_is_vertical); |
681 | |
682 | indices[--outerAAHead] = index - 2; |
683 | indices[--outerAAHead] = index - 4; |
684 | indices[outerAATail++] = index - 3; |
685 | indices[outerAATail++] = index - 1; |
686 | } else { |
687 | smoothVertices[index++].set(primary: innerEdge, secondary: innerSecondaryEnd, ncolor: transparent, dPrimary: delta * part, dSecondary: delta, vertical: m_gradient_is_vertical); |
688 | smoothVertices[index++].set(primary: innerEdge, secondary: innerSecondaryStart, ncolor: transparent, dPrimary: delta * part, dSecondary: -delta, vertical: m_gradient_is_vertical); |
689 | } |
690 | } else { |
691 | vertices[index++].set(primary: innerEdge, secondary: innerSecondaryEnd, ncolor: fillColor, vertical: m_gradient_is_vertical); |
692 | vertices[index++].set(primary: innerEdge, secondary: innerSecondaryStart, ncolor: fillColor, vertical: m_gradient_is_vertical); |
693 | if (penWidth) { |
694 | vertices[index++].set(primary: innerEdge, secondary: innerSecondaryEnd, ncolor: borderColor, vertical: m_gradient_is_vertical); |
695 | vertices[index++].set(primary: innerEdge, secondary: innerSecondaryStart, ncolor: borderColor, vertical: m_gradient_is_vertical); |
696 | vertices[index++].set(primary: outerEdge, secondary: outerSecondaryEnd, ncolor: borderColor, vertical: m_gradient_is_vertical); |
697 | vertices[index++].set(primary: outerEdge, secondary: outerSecondaryStart, ncolor: borderColor, vertical: m_gradient_is_vertical); |
698 | } |
699 | } |
700 | } |
701 | Q_ASSERT(index == vertexCount); |
702 | |
703 | // Close the triangle strips. |
704 | if (m_antialiasing) { |
705 | indices[--innerAAHead] = indices[innerAATail - 1]; |
706 | indices[--innerAAHead] = indices[innerAATail - 2]; |
707 | Q_ASSERT(innerAATail <= indexCount); |
708 | } |
709 | if (penWidth) { |
710 | indices[--borderHead] = indices[borderTail - 1]; |
711 | indices[--borderHead] = indices[borderTail - 2]; |
712 | Q_ASSERT(borderTail <= indexCount); |
713 | } |
714 | if (m_antialiasing && penWidth) { |
715 | indices[--outerAAHead] = indices[outerAATail - 1]; |
716 | indices[--outerAAHead] = indices[outerAATail - 2]; |
717 | Q_ASSERT(outerAATail == indexCount); |
718 | } |
719 | } |
720 | } |
721 | |
722 | QT_END_NAMESPACE |
723 | |