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