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 | : QSGInternalRectangleNode() |
78 | , m_aligned(true) |
79 | , m_antialiasing(false) |
80 | , m_gradient_is_opaque(true) |
81 | , m_dirty_geometry(false) |
82 | , m_gradient_is_vertical(true) |
83 | , m_geometry(QSGGeometry::defaultAttributes_ColoredPoint2D(), 0) |
84 | { |
85 | setGeometry(&m_geometry); |
86 | |
87 | #ifdef QSG_RUNTIME_DESCRIPTION |
88 | qsgnode_set_description(node: this, description: QLatin1String("internalrectangle" )); |
89 | #endif |
90 | } |
91 | |
92 | void QSGBasicInternalRectangleNode::setRect(const QRectF &rect) |
93 | { |
94 | if (rect == m_rect) |
95 | return; |
96 | m_rect = rect; |
97 | m_dirty_geometry = true; |
98 | } |
99 | |
100 | void QSGBasicInternalRectangleNode::setColor(const QColor &color) |
101 | { |
102 | if (color == m_color) |
103 | return; |
104 | m_color = color; |
105 | if (m_gradient_stops.isEmpty()) |
106 | m_dirty_geometry = true; |
107 | } |
108 | |
109 | void QSGBasicInternalRectangleNode::setPenColor(const QColor &color) |
110 | { |
111 | if (color == m_border_color) |
112 | return; |
113 | m_border_color = color; |
114 | if (m_pen_width > 0) |
115 | m_dirty_geometry = true; |
116 | } |
117 | |
118 | void QSGBasicInternalRectangleNode::setPenWidth(qreal width) |
119 | { |
120 | if (width == m_pen_width) |
121 | return; |
122 | m_pen_width = width; |
123 | m_dirty_geometry = true; |
124 | } |
125 | |
126 | |
127 | void QSGBasicInternalRectangleNode::setGradientStops(const QGradientStops &stops) |
128 | { |
129 | if (stops.constData() == m_gradient_stops.constData()) |
130 | return; |
131 | |
132 | m_gradient_stops = stops; |
133 | |
134 | m_gradient_is_opaque = true; |
135 | for (int i = 0; i < stops.size(); ++i) |
136 | m_gradient_is_opaque &= stops.at(i).second.alpha() == 0xff; |
137 | m_dirty_geometry = true; |
138 | } |
139 | |
140 | void QSGBasicInternalRectangleNode::setGradientVertical(bool vertical) |
141 | { |
142 | if (vertical == m_gradient_is_vertical) |
143 | return; |
144 | m_gradient_is_vertical = vertical; |
145 | m_dirty_geometry = true; |
146 | } |
147 | |
148 | |
149 | void QSGBasicInternalRectangleNode::setRadius(qreal radius) |
150 | { |
151 | if (radius == m_radius) |
152 | return; |
153 | m_radius = radius; |
154 | m_dirty_geometry = true; |
155 | } |
156 | |
157 | void QSGBasicInternalRectangleNode::setTopLeftRadius(qreal radius) |
158 | { |
159 | if (radius == m_topLeftRadius) |
160 | return; |
161 | m_topLeftRadius = radius; |
162 | m_dirty_geometry = true; |
163 | } |
164 | void QSGBasicInternalRectangleNode::setTopRightRadius(qreal radius) |
165 | { |
166 | if (radius == m_topRightRadius) |
167 | return; |
168 | m_topRightRadius = radius; |
169 | m_dirty_geometry = true; |
170 | } |
171 | void QSGBasicInternalRectangleNode::setBottomLeftRadius(qreal radius) |
172 | { |
173 | if (radius == m_bottomLeftRadius) |
174 | return; |
175 | m_bottomLeftRadius = radius; |
176 | m_dirty_geometry = true; |
177 | } |
178 | void QSGBasicInternalRectangleNode::setBottomRightRadius(qreal radius) |
179 | { |
180 | if (radius == m_bottomRightRadius) |
181 | return; |
182 | m_bottomRightRadius = radius; |
183 | m_dirty_geometry = true; |
184 | } |
185 | |
186 | void QSGBasicInternalRectangleNode::setAntialiasing(bool antialiasing) |
187 | { |
188 | if (!supportsAntialiasing()) |
189 | return; |
190 | |
191 | if (antialiasing == m_antialiasing) |
192 | return; |
193 | m_antialiasing = antialiasing; |
194 | if (m_antialiasing) { |
195 | setGeometry(new QSGGeometry(smoothAttributeSet(), 0)); |
196 | setFlag(OwnsGeometry, true); |
197 | } else { |
198 | setGeometry(&m_geometry); |
199 | setFlag(OwnsGeometry, false); |
200 | } |
201 | updateMaterialAntialiasing(); |
202 | m_dirty_geometry = true; |
203 | } |
204 | |
205 | void QSGBasicInternalRectangleNode::setAligned(bool aligned) |
206 | { |
207 | if (aligned == m_aligned) |
208 | return; |
209 | m_aligned = aligned; |
210 | m_dirty_geometry = true; |
211 | } |
212 | |
213 | void QSGBasicInternalRectangleNode::update() |
214 | { |
215 | if (m_dirty_geometry) { |
216 | updateGeometry(); |
217 | m_dirty_geometry = false; |
218 | |
219 | QSGNode::DirtyState state = QSGNode::DirtyGeometry; |
220 | updateMaterialBlending(state: &state); |
221 | markDirty(bits: state); |
222 | } |
223 | } |
224 | |
225 | void QSGBasicInternalRectangleNode::updateGeometry() |
226 | { |
227 | float width = float(m_rect.width()); |
228 | float height = float(m_rect.height()); |
229 | float penWidth = qMin(a: qMin(a: width, b: height) * 0.5f, b: float(m_pen_width)); |
230 | |
231 | if (m_aligned) |
232 | penWidth = qRound(f: penWidth); |
233 | |
234 | QSGGeometry *g = geometry(); |
235 | g->setDrawingMode(QSGGeometry::DrawTriangleStrip); |
236 | int vertexStride = g->sizeOfVertex(); |
237 | |
238 | union { |
239 | Vertex *vertices; |
240 | SmoothVertex *smoothVertices; |
241 | }; |
242 | |
243 | Color4ub fillColor = colorToColor4ub(c: m_color); |
244 | Color4ub borderColor = colorToColor4ub(c: m_border_color); |
245 | Color4ub transparent = { .r: 0, .g: 0, .b: 0, .a: 0 }; |
246 | const QGradientStops &stops = m_gradient_stops; |
247 | |
248 | float gradientStart = (m_gradient_is_vertical ? m_rect.top() : m_rect.left()); |
249 | float gradientLength = (m_gradient_is_vertical ? height : width); |
250 | float secondaryLength = (m_gradient_is_vertical ? width : height); |
251 | |
252 | int nextGradientStop = 0; |
253 | float gradientPos = penWidth / gradientLength; |
254 | while (nextGradientStop < stops.size() && stops.at(i: nextGradientStop).first <= gradientPos) |
255 | ++nextGradientStop; |
256 | int lastGradientStop = stops.size() - 1; |
257 | float lastGradientPos = 1.0f - penWidth / gradientLength; |
258 | while (lastGradientStop >= nextGradientStop && stops.at(i: lastGradientStop).first >= lastGradientPos) |
259 | --lastGradientStop; |
260 | int gradientIntersections = (lastGradientStop - nextGradientStop + 1); |
261 | |
262 | if (m_radius > 0 |
263 | || m_topLeftRadius > 0 |
264 | || m_topRightRadius > 0 |
265 | || m_bottomLeftRadius > 0 |
266 | || m_bottomRightRadius > 0) { |
267 | // Rounded corners. |
268 | |
269 | // Radius should never exceed half the width or half the height. |
270 | float radiusTL = qMin(a: qMin(a: width, b: height) * 0.4999f, b: float(m_topLeftRadius < 0 ? m_radius : m_topLeftRadius)); |
271 | float radiusTR = qMin(a: qMin(a: width, b: height) * 0.4999f, b: float(m_topRightRadius < 0 ? m_radius : m_topRightRadius)); |
272 | float radiusBL = qMin(a: qMin(a: width, b: height) * 0.4999f, b: float(m_bottomLeftRadius < 0 ? m_radius : m_bottomLeftRadius)); |
273 | float radiusBR = qMin(a: qMin(a: width, b: height) * 0.4999f, b: float(m_bottomRightRadius < 0 ? m_radius : m_bottomRightRadius)); |
274 | |
275 | // The code produces some artefacts when radius <= 0.5. A radius of half a pixel |
276 | // does not make much sense anyway, so we draw a normal corner in such a case. |
277 | if (radiusTL <= 0.5) |
278 | radiusTL = 0; |
279 | if (radiusTR <= 0.5) |
280 | radiusTR = 0; |
281 | if (radiusBL <= 0.5) |
282 | radiusBL = 0; |
283 | if (radiusBR <= 0.5) |
284 | radiusBR = 0; |
285 | |
286 | // We want to keep a minimal inner radius in order to make the inner |
287 | // x-coordinates of an arc mathematically unique and identifiable. |
288 | const float innerRadiusTL = qMax(a: radiusTL - penWidth * 1.0f, b: 0.01); |
289 | const float innerRadiusTR = qMax(a: radiusTR - penWidth * 1.0f, b: 0.01); |
290 | const float innerRadiusBL = qMax(a: radiusBL - penWidth * 1.0f, b: 0.01); |
291 | const float innerRadiusBR = qMax(a: radiusBR - penWidth * 1.0f, b: 0.01); |
292 | const float outerRadiusTL = radiusTL; |
293 | const float outerRadiusTR = radiusTR; |
294 | const float outerRadiusBL = radiusBL; |
295 | const float outerRadiusBR = radiusBR; |
296 | const float delta = qMin(a: width, b: height) * 0.5f; |
297 | |
298 | int segmentsTL = radiusTL == 0 ? 0 : qBound(min: 3, val: qCeil(v: radiusTL * (M_PI / 6)), max: 18); |
299 | int segmentsTR = radiusTR == 0 ? 0 : qBound(min: 3, val: qCeil(v: radiusTR * (M_PI / 6)), max: 18); |
300 | int segmentsBL = radiusBL == 0 ? 0 : qBound(min: 3, val: qCeil(v: radiusBL * (M_PI / 6)), max: 18); |
301 | int segmentsBR = radiusBR == 0 ? 0 : qBound(min: 3, val: qCeil(v: radiusBR * (M_PI / 6)), max: 18); |
302 | |
303 | // If the radii on opposite sites in genraration direction are the same, |
304 | // we will set the segments of one side to 0 as these points would be |
305 | // calculated twice. Also, this optimizes for the case of similar radii |
306 | if (m_gradient_is_vertical) { |
307 | if (innerRadiusTL == innerRadiusTR) { |
308 | if (segmentsTL <= segmentsTR) |
309 | segmentsTL = 0; |
310 | else |
311 | segmentsTR = 0; |
312 | } |
313 | if (innerRadiusBL == innerRadiusBR){ |
314 | if (segmentsBL <= segmentsBR) |
315 | segmentsBL = 0; |
316 | else |
317 | segmentsBR = 0; |
318 | } |
319 | } else { |
320 | if (innerRadiusTL == innerRadiusBL) { |
321 | if (segmentsTL <= segmentsBL) |
322 | segmentsTL = 0; |
323 | else |
324 | segmentsBL = 0; |
325 | } |
326 | if (innerRadiusTR == innerRadiusBR) { |
327 | if (segmentsTR <= segmentsBR) |
328 | segmentsTR = 0; |
329 | else |
330 | segmentsBR = 0; |
331 | } |
332 | } |
333 | |
334 | const int sumSegments = segmentsTL + segmentsTR + segmentsBL + segmentsBR; |
335 | |
336 | /* |
337 | |
338 | --+--__ |
339 | --+--__--__ |
340 | | --__--__ |
341 | | seg --__--+ |
342 | --+-__ ment _+ \ |
343 | --+-__--__ - \ \ |
344 | --__--+ se \ \ |
345 | + \ g \ \ |
346 | \ \ m \ \ |
347 | -----------+--+ e \ \ <- gradient line |
348 | \ \ nt\ \ |
349 | fill +--+----+--+ |
350 | | | | | |
351 | border |
352 | inner AA outer AA (AA = antialiasing) |
353 | |
354 | */ |
355 | |
356 | const int innerVertexCount = (sumSegments + 4) * 2 + gradientIntersections * 2; |
357 | const int outerVertexCount = (sumSegments + 4) * 2; |
358 | int vertexCount = innerVertexCount; |
359 | if (m_antialiasing || penWidth) |
360 | vertexCount += innerVertexCount; |
361 | if (penWidth) |
362 | vertexCount += outerVertexCount; |
363 | if (m_antialiasing && penWidth) |
364 | vertexCount += outerVertexCount; |
365 | |
366 | |
367 | const int fillIndexCount = innerVertexCount; |
368 | const int innerAAIndexCount = innerVertexCount * 2 + 2; |
369 | const int borderIndexCount = innerVertexCount * 2 + 2; |
370 | const int outerAAIndexCount = outerVertexCount * 2 + 2; |
371 | int indexCount = 0; |
372 | int fillHead = 0; |
373 | int innerAAHead = 0; |
374 | int innerAATail = 0; |
375 | int borderHead = 0; |
376 | int borderTail = 0; |
377 | int outerAAHead = 0; |
378 | int outerAATail = 0; |
379 | bool hasFill = m_color.alpha() > 0 || !stops.isEmpty(); |
380 | if (hasFill) |
381 | indexCount += fillIndexCount; |
382 | if (m_antialiasing) { |
383 | innerAATail = innerAAHead = indexCount + (innerAAIndexCount >> 1) + 1; |
384 | indexCount += innerAAIndexCount; |
385 | } |
386 | if (penWidth) { |
387 | borderTail = borderHead = indexCount + (borderIndexCount >> 1) + 1; |
388 | indexCount += borderIndexCount; |
389 | } |
390 | if (m_antialiasing && penWidth) { |
391 | outerAATail = outerAAHead = indexCount + (outerAAIndexCount >> 1) + 1; |
392 | indexCount += outerAAIndexCount; |
393 | } |
394 | |
395 | g->allocate(vertexCount, indexCount); |
396 | vertices = reinterpret_cast<Vertex *>(g->vertexData()); |
397 | memset(s: vertices, c: 0, n: vertexCount * vertexStride); |
398 | quint16 *indices = g->indexDataAsUShort(); |
399 | quint16 index = 0; |
400 | |
401 | |
402 | float innerXPrev = 0.; // previous inner primary coordinate, both sides. |
403 | float innerYLeftPrev = 0.; // previous inner secondary coordinate, left. |
404 | float innerYRightPrev = 0.; // previous inner secondary coordinate, right. |
405 | |
406 | const float angleTL = 0.5f * float(M_PI) / segmentsTL; |
407 | const float cosStepTL = qFastCos(x: angleTL); |
408 | const float sinStepTL = qFastSin(x: angleTL); |
409 | const float angleTR = 0.5f * float(M_PI) / segmentsTR; |
410 | const float cosStepTR = qFastCos(x: angleTR); |
411 | const float sinStepTR = qFastSin(x: angleTR); |
412 | const float angleBL = 0.5f * float(M_PI) / segmentsBL; |
413 | const float cosStepBL = qFastCos(x: angleBL); |
414 | const float sinStepBL = qFastSin(x: angleBL); |
415 | const float angleBR = 0.5f * float(M_PI) / segmentsBR; |
416 | const float cosStepBR = qFastCos(x: angleBR); |
417 | const float sinStepBR = qFastSin(x: angleBR); |
418 | |
419 | //The x- and y-Axis are transposed, depending on gradient being vertical or horizontal |
420 | //Lets define some coordinates and radii. The first index is the part, the second index |
421 | //is the left or right side, as seen when moving from part0 to part1 |
422 | |
423 | // left vertices | right vertices |
424 | // | |
425 | // *************|************** |
426 | // * | | | * |
427 | // *--o | o--* |
428 | // * innerX/Y | innerX/Y * |
429 | // * | * |
430 | // * | * part 0 |
431 | // * | * |
432 | // * | * |
433 | // * | * |
434 | // -----------------+--------------------> y |
435 | // * | * |
436 | // * | * |
437 | // * | * |
438 | // * | * part 1 |
439 | // * | * |
440 | // * innerX/Y | innerX/Y * |
441 | // *--o | o--* |
442 | // * | | | * |
443 | // *************|************** |
444 | // | |
445 | // v x |
446 | // |
447 | // direction of vertex generation |
448 | |
449 | const float outerXCenter[][2] = {{ |
450 | float(m_gradient_is_vertical ? m_rect.top() + radiusTL : m_rect.left() + radiusTL), |
451 | float(m_gradient_is_vertical ? m_rect.top() + radiusTR : m_rect.left() + radiusBL) |
452 | }, { |
453 | float(m_gradient_is_vertical ? m_rect.bottom() - radiusBL : m_rect.right() - radiusTR), |
454 | float(m_gradient_is_vertical ? m_rect.bottom() - radiusBR : m_rect.right() - radiusBR) |
455 | }}; |
456 | |
457 | const float outerYCenter[][2] = {{ |
458 | float(!m_gradient_is_vertical ? m_rect.top() + outerRadiusTL : m_rect.left() + outerRadiusTL), |
459 | float(!m_gradient_is_vertical ? m_rect.bottom() - outerRadiusBL : m_rect.right() - outerRadiusTR) |
460 | }, { |
461 | float(!m_gradient_is_vertical ? m_rect.top() + outerRadiusTR : m_rect.left() + outerRadiusBL), |
462 | float(!m_gradient_is_vertical ? m_rect.bottom() - outerRadiusBR : m_rect.right() - outerRadiusBR) |
463 | }}; |
464 | |
465 | const float innerXCenter[][2] = { { |
466 | float(m_gradient_is_vertical ? m_rect.top() + innerRadiusTL + penWidth : m_rect.left() + innerRadiusTL + penWidth), |
467 | float(m_gradient_is_vertical ? m_rect.top() + innerRadiusTR + penWidth: m_rect.left() + innerRadiusBL + penWidth) |
468 | }, { |
469 | float(m_gradient_is_vertical ? m_rect.bottom() - innerRadiusBL - penWidth: m_rect.right() - innerRadiusTR - penWidth), |
470 | float(m_gradient_is_vertical ? m_rect.bottom() - innerRadiusBR - penWidth: m_rect.right() - innerRadiusBR - penWidth) |
471 | }}; |
472 | |
473 | const float innerYCenter[][2] = { { |
474 | float(!m_gradient_is_vertical ? m_rect.top() + innerRadiusTL + penWidth : m_rect.left() + innerRadiusTL + penWidth), |
475 | float(!m_gradient_is_vertical ? m_rect.bottom() - innerRadiusBL - penWidth : m_rect.right() - innerRadiusTR - penWidth) |
476 | },{ |
477 | float(!m_gradient_is_vertical ? m_rect.top() + innerRadiusTR + penWidth : m_rect.left() + innerRadiusBL + penWidth), |
478 | float(!m_gradient_is_vertical ? m_rect.bottom() - innerRadiusBR - penWidth : m_rect.right() - innerRadiusBR - penWidth) |
479 | }}; |
480 | |
481 | const float innerRadius[][2] = {{ |
482 | innerRadiusTL, |
483 | !m_gradient_is_vertical ? innerRadiusBL : innerRadiusTR |
484 | }, { |
485 | !m_gradient_is_vertical ? innerRadiusTR : innerRadiusBL, |
486 | innerRadiusBR |
487 | }}; |
488 | |
489 | const float outerRadius[][2] = {{ |
490 | outerRadiusTL, |
491 | !m_gradient_is_vertical ? outerRadiusBL : outerRadiusTR |
492 | }, { |
493 | !m_gradient_is_vertical ? outerRadiusTR : outerRadiusBL, |
494 | outerRadiusBR |
495 | }}; |
496 | |
497 | const int segments[][2] = {{ |
498 | segmentsTL, |
499 | !m_gradient_is_vertical ? segmentsBL : segmentsTR |
500 | }, { |
501 | !m_gradient_is_vertical ? segmentsTR : segmentsBL, |
502 | segmentsBR |
503 | }}; |
504 | |
505 | const float cosStep[][2] = {{ |
506 | cosStepTL, |
507 | !m_gradient_is_vertical ? cosStepBL : cosStepTR |
508 | }, { |
509 | !m_gradient_is_vertical ? cosStepTR : cosStepBL, |
510 | cosStepBR |
511 | }}; |
512 | |
513 | const float sinStep[][2] = {{ |
514 | sinStepTL, |
515 | !m_gradient_is_vertical ? sinStepBL : sinStepTR |
516 | }, { |
517 | !m_gradient_is_vertical ? sinStepTR : sinStepBL, |
518 | sinStepBR |
519 | }}; |
520 | |
521 | auto fillColorFromX = [&](float x) { |
522 | |
523 | float t = (x - gradientStart) / gradientLength; |
524 | t = qBound(min: 0.0, val: t, max: 1.0); |
525 | |
526 | int i = 1; |
527 | if (t < stops.first().first) |
528 | return colorToColor4ub(c: stops.first().second); |
529 | while (i < stops.size()) { |
530 | const QGradientStop &prev = stops.at(i: i - 1); |
531 | const QGradientStop &next = stops.at(i); |
532 | if (prev.first <= t && next.first > t) { |
533 | t = (t - prev.first) / (next.first - prev.first); |
534 | return colorToColor4ub(c: prev.second) * (1. - t) + colorToColor4ub(c: next.second) * t; } |
535 | i++; |
536 | } |
537 | return colorToColor4ub(c: stops.last().second); |
538 | }; |
539 | |
540 | for (int part = 0; part < 2; ++part) { |
541 | // cosine of the angle of the current segment, starting at 1 for part 0 and 0 for part 1 |
542 | float cosSegmentAngleLeft = 1. - part; |
543 | // sine of the angle of the current segment |
544 | float sinSegmentAngleLeft = part; |
545 | |
546 | float cosSegmentAngleRight = 1. - part; |
547 | float sinSegmentAngleRight = part; |
548 | |
549 | bool advanceLeft = true; |
550 | |
551 | // We draw both the left part and the right part of the rectangle at the same time. |
552 | // We also draw a vertex on the left side for every vertex on the right side. This |
553 | // syncronisation is required to make sure that all gradient stops can be inserted. |
554 | for (int iLeft = 0, iRight = 0; iLeft <= segments[part][0] || iRight <= segments[part][1]; ) { |
555 | |
556 | float xLeft, yLeft, |
557 | xRight, yRight; |
558 | |
559 | float outerXLeft, outerYLeft, |
560 | outerXRight, outerYRight; |
561 | |
562 | float sinAngleLeft, cosAngleLeft, |
563 | sinAngleRight, cosAngleRight; |
564 | |
565 | // calculate inner x-coordinates |
566 | xLeft = innerXCenter[part][0] - innerRadius[part][0] * cosSegmentAngleLeft; |
567 | xRight = innerXCenter[part][1] - innerRadius[part][1] * cosSegmentAngleRight; |
568 | |
569 | // calcuate inner y-coordinates |
570 | yLeft = innerYCenter[part][0] - innerRadius[part][0] * sinSegmentAngleLeft; |
571 | yRight = innerYCenter[part][1] + innerRadius[part][1] * sinSegmentAngleRight; |
572 | |
573 | // Synchronize left and right hand x-coordinates. This is required to |
574 | // make sure that we can insert all gradient stops that require exactly two triangles at |
575 | // every x-coordinate. Take the smaller of both x-coordinates and then find the matching |
576 | // y-coordinates. |
577 | if ((iLeft <= segments[part][0] && xLeft <= xRight) || iRight > segments[part][1]) { |
578 | advanceLeft = true; |
579 | } else { |
580 | advanceLeft = false; |
581 | } |
582 | |
583 | // Inner: Find the matching y-coordinates for the x-coordinate found above. |
584 | // Outer: Also set the sine and cosine to make sure that outer vertices are |
585 | // drawn correctly. |
586 | if (innerRadius[part][0] == innerRadius[part][1]) { |
587 | // Special case of equal radii. Optimize to avoid performance regression: |
588 | // Left and right is always equal and we can just copy the angles and |
589 | // mirror the coordinates. |
590 | if (advanceLeft) { |
591 | if (outerRadius[part][0] == 0) { |
592 | sinAngleLeft = 1.; |
593 | cosAngleLeft = part ? -1. : 1.; |
594 | } else { |
595 | sinAngleLeft = sinSegmentAngleLeft; |
596 | cosAngleLeft = cosSegmentAngleLeft; |
597 | } |
598 | if (outerRadius[part][1] == 0) { |
599 | sinAngleRight = 1.; |
600 | cosAngleRight = part ? -1. : 1.; |
601 | } else { |
602 | sinAngleRight = sinSegmentAngleLeft; |
603 | cosAngleRight = cosSegmentAngleLeft; |
604 | } |
605 | xRight = xLeft; |
606 | yRight = innerYCenter[part][1] + innerRadius[part][1] * sinAngleRight; |
607 | } else { |
608 | if (outerRadius[part][0] == 0) { |
609 | sinAngleLeft = 1.; |
610 | cosAngleLeft = part ? -1. : 1.; |
611 | } else { |
612 | sinAngleLeft = sinSegmentAngleRight; |
613 | cosAngleLeft = cosSegmentAngleRight; |
614 | } |
615 | if (outerRadius[part][1] == 0) { |
616 | sinAngleRight = 1.; |
617 | cosAngleRight = part ? -1. : 1.; |
618 | } else { |
619 | sinAngleRight = sinSegmentAngleRight; |
620 | cosAngleRight = cosSegmentAngleRight; |
621 | } |
622 | xLeft = xRight; |
623 | yLeft = innerYCenter[part][0] - innerRadius[part][0] * sinAngleLeft; |
624 | } |
625 | } else if (advanceLeft) { |
626 | if (outerRadius[part][0] == 0) { |
627 | sinAngleLeft = 1.; |
628 | cosAngleLeft = part ? -1. : 1.; |
629 | } else { |
630 | sinAngleLeft = sinSegmentAngleLeft; |
631 | cosAngleLeft = cosSegmentAngleLeft; |
632 | } |
633 | if (outerRadius[part][1] == 0) { |
634 | // Outer: If the outer radius is zero we can return both sin and cos = 1 |
635 | // to form a nice corner. Inner: Accept the x-coordinate from the other |
636 | // side and match the y-coordinate |
637 | sinAngleRight = 1.; |
638 | cosAngleRight = part ? -1. : 1.; |
639 | xRight = xLeft; |
640 | yRight = innerYCenter[part][1] + innerRadius[part][1] * sinAngleRight; |
641 | } else if (xLeft >= innerXCenter[0][1] && xLeft <= innerXCenter[1][1]) { |
642 | // Outer: If we are on the straight line between the inner centers, we can |
643 | // just return sin = 1 and cos = 0. Inner: Accept the x-coordinate from the |
644 | // other side and match the y-coordinate |
645 | sinAngleRight = 1.; |
646 | cosAngleRight = 0.; |
647 | xRight = xLeft; |
648 | yRight = innerYCenter[part][1] + innerRadius[part][1] * sinAngleRight; |
649 | } else { |
650 | // Inner: If we are on the rounded part of the oposite side, we have to find a vertex |
651 | // on that curve that matches the x-coordinate we selected. |
652 | // We always select the smaller x-coordinate and can therefore use a linear |
653 | // interpolation between the last point on this side and the point on this side |
654 | // that was not accepted because it was too large. |
655 | if (xRight != innerXPrev) { |
656 | float t = (xLeft - innerXPrev) / (xRight - innerXPrev); |
657 | yRight = innerYRightPrev * (1. - t) + yRight * t; |
658 | xRight = xLeft; |
659 | } |
660 | // Outer: With the coordinates from the interpolation we can calculate the sine |
661 | // and cosine of the respective angle quickly. |
662 | sinAngleRight = (yRight - innerYCenter[part][1]) / innerRadius[part][1]; |
663 | cosAngleRight = -(xRight - innerXCenter[part][1]) / innerRadius[part][1]; |
664 | } |
665 | } else { |
666 | // same as above but for the other side. |
667 | if (outerRadius[part][1] == 0) { |
668 | sinAngleRight = 1.; |
669 | cosAngleRight = part ? -1. : 1.; |
670 | } else { |
671 | sinAngleRight = sinSegmentAngleRight; |
672 | cosAngleRight = cosSegmentAngleRight; |
673 | } |
674 | if (outerRadius[part][0] == 0) { |
675 | sinAngleLeft = 1.; |
676 | cosAngleLeft = part ? -1. : 1.; |
677 | xLeft = xRight; |
678 | yLeft = innerYCenter[part][0] - innerRadius[part][0] * sinAngleLeft; |
679 | } else if (xRight >= innerXCenter[0][0] && xRight <= innerXCenter[1][0]) { |
680 | sinAngleLeft = 1.; |
681 | cosAngleLeft = 0.; |
682 | xLeft = xRight; |
683 | yLeft = innerYCenter[part][0] - innerRadius[part][0] * sinAngleLeft; |
684 | } else { |
685 | if (xLeft != innerXPrev) { |
686 | float t = (xRight - innerXPrev) / (xLeft - innerXPrev); |
687 | yLeft = innerYLeftPrev * (1. - t) + yLeft * t; |
688 | xLeft = xRight; |
689 | } |
690 | sinAngleLeft = -(yLeft - innerYCenter[part][0]) / innerRadius[part][0]; |
691 | cosAngleLeft = -(xLeft - innerXCenter[part][0]) / innerRadius[part][0]; |
692 | } |
693 | } |
694 | |
695 | gradientPos = (xLeft - gradientStart) / gradientLength; |
696 | |
697 | // calculate the matching outer coordinates |
698 | outerXLeft = outerXCenter[part][0] - outerRadius[part][0] * cosAngleLeft; |
699 | outerYLeft = outerYCenter[part][0] - outerRadius[part][0] * sinAngleLeft; |
700 | outerXRight = outerXCenter[part][1] - outerRadius[part][1] * cosAngleRight; |
701 | outerYRight = outerYCenter[part][1] + outerRadius[part][1] * sinAngleRight; |
702 | |
703 | // insert gradient stops as required |
704 | while (nextGradientStop <= lastGradientStop && stops.at(i: nextGradientStop).first <= gradientPos) { |
705 | float gradientX; |
706 | float gradientYLeft; |
707 | float gradientYRight; |
708 | |
709 | // Insert vertices at gradient stops |
710 | gradientX = gradientStart + stops.at(i: nextGradientStop).first * gradientLength; |
711 | // bilinear interpolation of known vertices |
712 | float t = (gradientX - innerXPrev) / (xLeft - innerXPrev); |
713 | gradientYLeft = innerYLeftPrev * (1. - t) + t * yLeft; |
714 | gradientYRight = innerYRightPrev * (1. - t) + t * yRight; |
715 | |
716 | fillColor = fillColorFromX(gradientX); |
717 | |
718 | if (hasFill) { |
719 | indices[fillHead++] = index; |
720 | indices[fillHead++] = index + 1; |
721 | } |
722 | |
723 | if (penWidth) { |
724 | --borderHead; |
725 | indices[borderHead] = indices[borderHead + 2]; |
726 | indices[--borderHead] = index + 2; |
727 | indices[borderTail++] = index + 3; |
728 | indices[borderTail] = indices[borderTail - 2]; |
729 | ++borderTail; |
730 | } |
731 | |
732 | if (m_antialiasing) { |
733 | indices[--innerAAHead] = index + 2; |
734 | indices[--innerAAHead] = index; |
735 | indices[innerAATail++] = index + 1; |
736 | indices[innerAATail++] = index + 3; |
737 | |
738 | bool lower = stops.at(i: nextGradientStop).first > 0.5f; |
739 | float dp = lower ? qMin(a: 0.0f, b: gradientLength - gradientX - delta) : qMax(a: 0.0f, b: delta - gradientX); |
740 | smoothVertices[index++].set(primary: gradientX, secondary: gradientYRight, ncolor: fillColor, dPrimary: dp, dSecondary: secondaryLength - gradientYRight - delta, vertical: m_gradient_is_vertical); |
741 | smoothVertices[index++].set(primary: gradientX, secondary: gradientYLeft, ncolor: fillColor, dPrimary: dp, dSecondary: delta - gradientYLeft, vertical: m_gradient_is_vertical); |
742 | if (penWidth) { |
743 | smoothVertices[index++].set(primary: gradientX, secondary: gradientYRight, ncolor: borderColor, dPrimary: -0.49f * penWidth * cosAngleRight, dSecondary: 0.49f * penWidth * sinAngleRight, vertical: m_gradient_is_vertical); |
744 | smoothVertices[index++].set(primary: gradientX, secondary: gradientYLeft, ncolor: borderColor, dPrimary: -0.49f * penWidth * cosAngleLeft, dSecondary: -0.49f * penWidth * sinAngleLeft, vertical: m_gradient_is_vertical); |
745 | } else { |
746 | dp = lower ? delta : -delta; |
747 | smoothVertices[index++].set(primary: gradientX, secondary: gradientYRight, ncolor: transparent, dPrimary: dp, dSecondary: delta, vertical: m_gradient_is_vertical); |
748 | smoothVertices[index++].set(primary: gradientX, secondary: gradientYLeft, ncolor: transparent, dPrimary: dp, dSecondary: -delta, vertical: m_gradient_is_vertical); |
749 | } |
750 | } else { |
751 | vertices[index++].set(primary: gradientX, secondary: gradientYRight, ncolor: fillColor, vertical: m_gradient_is_vertical); |
752 | vertices[index++].set(primary: gradientX, secondary: gradientYLeft, ncolor: fillColor, vertical: m_gradient_is_vertical); |
753 | if (penWidth) { |
754 | vertices[index++].set(primary: gradientX, secondary: gradientYRight, ncolor: borderColor, vertical: m_gradient_is_vertical); |
755 | vertices[index++].set(primary: gradientX, secondary: gradientYLeft, ncolor: borderColor, vertical: m_gradient_is_vertical); |
756 | } |
757 | } |
758 | |
759 | innerXPrev = gradientX; |
760 | innerYLeftPrev = gradientYLeft; |
761 | innerYRightPrev = gradientYRight; |
762 | |
763 | nextGradientStop++; |
764 | } |
765 | |
766 | if (!stops.isEmpty()) { |
767 | fillColor = fillColorFromX(xLeft); |
768 | } |
769 | |
770 | if (hasFill) { |
771 | indices[fillHead++] = index; |
772 | indices[fillHead++] = index + 1; |
773 | } |
774 | |
775 | if (penWidth) { |
776 | indices[--borderHead] = index + 4; |
777 | indices[--borderHead] = index + 2; |
778 | indices[borderTail++] = index + 3; |
779 | indices[borderTail++] = index + 5; |
780 | } |
781 | |
782 | if (m_antialiasing) { |
783 | indices[--innerAAHead] = index + 2; |
784 | indices[--innerAAHead] = index; |
785 | indices[innerAATail++] = index + 1; |
786 | indices[innerAATail++] = index + 3; |
787 | |
788 | float dp = part ? qMin(a: 0.0f, b: gradientLength - xRight - delta) : qMax(a: 0.0f, b: delta - xRight); |
789 | smoothVertices[index++].set(primary: xRight, secondary: yRight, ncolor: fillColor, dPrimary: dp, dSecondary: secondaryLength - yRight - delta, vertical: m_gradient_is_vertical); |
790 | smoothVertices[index++].set(primary: xLeft, secondary: yLeft, ncolor: fillColor, dPrimary: dp, dSecondary: delta - yLeft, vertical: m_gradient_is_vertical); |
791 | |
792 | dp = part ? delta : -delta; |
793 | if (penWidth) { |
794 | smoothVertices[index++].set(primary: xRight, secondary: yRight, ncolor: borderColor, dPrimary: -0.49f * penWidth * cosAngleRight, dSecondary: 0.49f * penWidth * sinAngleRight, vertical: m_gradient_is_vertical); |
795 | smoothVertices[index++].set(primary: xLeft, secondary: yLeft, ncolor: borderColor, dPrimary: -0.49f * penWidth * cosAngleLeft, dSecondary: -0.49f * penWidth * sinAngleLeft, vertical: m_gradient_is_vertical); |
796 | smoothVertices[index++].set(primary: outerXRight, secondary: outerYRight, ncolor: borderColor, dPrimary: 0.49f * penWidth * cosAngleRight, dSecondary: -0.49f * penWidth * sinAngleRight, vertical: m_gradient_is_vertical); |
797 | smoothVertices[index++].set(primary: outerXLeft, secondary: outerYLeft, ncolor: borderColor, dPrimary: 0.49f * penWidth * cosAngleLeft, dSecondary: 0.49f * penWidth * sinAngleLeft, vertical: m_gradient_is_vertical); |
798 | smoothVertices[index++].set(primary: outerXRight, secondary: outerYRight, ncolor: transparent, dPrimary: dp, dSecondary: delta, vertical: m_gradient_is_vertical); |
799 | smoothVertices[index++].set(primary: outerXLeft, secondary: outerYLeft, ncolor: transparent, dPrimary: dp, dSecondary: -delta, vertical: m_gradient_is_vertical); |
800 | |
801 | indices[--outerAAHead] = index - 2; |
802 | indices[--outerAAHead] = index - 4; |
803 | indices[outerAATail++] = index - 3; |
804 | indices[outerAATail++] = index - 1; |
805 | } else { |
806 | smoothVertices[index++].set(primary: xRight, secondary: yRight, ncolor: transparent, dPrimary: dp, dSecondary: delta, vertical: m_gradient_is_vertical); |
807 | smoothVertices[index++].set(primary: xLeft, secondary: yLeft, ncolor: transparent, dPrimary: dp, dSecondary: -delta, vertical: m_gradient_is_vertical); |
808 | } |
809 | } else { |
810 | vertices[index++].set(primary: xRight, secondary: yRight, ncolor: fillColor, vertical: m_gradient_is_vertical); |
811 | vertices[index++].set(primary: xLeft, secondary: yLeft, ncolor: fillColor, vertical: m_gradient_is_vertical); |
812 | if (penWidth) { |
813 | vertices[index++].set(primary: xRight, secondary: yRight, ncolor: borderColor, vertical: m_gradient_is_vertical); |
814 | vertices[index++].set(primary: xLeft, secondary: yLeft, ncolor: borderColor, vertical: m_gradient_is_vertical); |
815 | vertices[index++].set(primary: outerXRight, secondary: outerYRight, ncolor: borderColor, vertical: m_gradient_is_vertical); |
816 | vertices[index++].set(primary: outerXLeft, secondary: outerYLeft, ncolor: borderColor, vertical: m_gradient_is_vertical); |
817 | } |
818 | } |
819 | |
820 | innerXPrev = xLeft; |
821 | innerYLeftPrev = yLeft; |
822 | innerYRightPrev = yRight; |
823 | |
824 | // Advance the point. This corresponds to a rotation of the respective segment |
825 | if (advanceLeft) { |
826 | iLeft++; |
827 | qreal tmp = cosSegmentAngleLeft; |
828 | cosSegmentAngleLeft = cosSegmentAngleLeft * cosStep[part][0] - sinSegmentAngleLeft * sinStep[part][0]; |
829 | sinSegmentAngleLeft = sinSegmentAngleLeft * cosStep[part][0] + tmp * sinStep[part][0]; |
830 | } else { |
831 | iRight++; |
832 | qreal tmp = cosSegmentAngleRight; |
833 | cosSegmentAngleRight = cosSegmentAngleRight * cosStep[part][1] - sinSegmentAngleRight * sinStep[part][1]; |
834 | sinSegmentAngleRight = sinSegmentAngleRight * cosStep[part][1] + tmp * sinStep[part][1]; |
835 | } |
836 | } |
837 | } |
838 | |
839 | Q_ASSERT(index == vertexCount); |
840 | |
841 | // Close the triangle strips. |
842 | if (m_antialiasing) { |
843 | indices[--innerAAHead] = indices[innerAATail - 1]; |
844 | indices[--innerAAHead] = indices[innerAATail - 2]; |
845 | Q_ASSERT(innerAATail <= indexCount); |
846 | } |
847 | if (penWidth) { |
848 | indices[--borderHead] = indices[borderTail - 1]; |
849 | indices[--borderHead] = indices[borderTail - 2]; |
850 | Q_ASSERT(borderTail <= indexCount); |
851 | } |
852 | if (m_antialiasing && penWidth) { |
853 | indices[--outerAAHead] = indices[outerAATail - 1]; |
854 | indices[--outerAAHead] = indices[outerAATail - 2]; |
855 | Q_ASSERT(outerAATail == indexCount); |
856 | } |
857 | } else { |
858 | // Straight corners. |
859 | QRectF innerRect = m_rect; |
860 | QRectF outerRect = m_rect; |
861 | |
862 | if (penWidth) |
863 | innerRect.adjust(xp1: 1.0f * penWidth, yp1: 1.0f * penWidth, xp2: -1.0f * penWidth, yp2: -1.0f * penWidth); |
864 | |
865 | float delta = qMin(a: width, b: height) * 0.5f; |
866 | int innerVertexCount = 4 + gradientIntersections * 2; |
867 | int outerVertexCount = 4; |
868 | int vertexCount = innerVertexCount; |
869 | if (m_antialiasing || penWidth) |
870 | vertexCount += innerVertexCount; |
871 | if (penWidth) |
872 | vertexCount += outerVertexCount; |
873 | if (m_antialiasing && penWidth) |
874 | vertexCount += outerVertexCount; |
875 | |
876 | int fillIndexCount = innerVertexCount; |
877 | int innerAAIndexCount = innerVertexCount * 2 + 2; |
878 | int borderIndexCount = innerVertexCount * 2 + 2; |
879 | int outerAAIndexCount = outerVertexCount * 2 + 2; |
880 | int indexCount = 0; |
881 | int fillHead = 0; |
882 | int innerAAHead = 0; |
883 | int innerAATail = 0; |
884 | int borderHead = 0; |
885 | int borderTail = 0; |
886 | int outerAAHead = 0; |
887 | int outerAATail = 0; |
888 | bool hasFill = m_color.alpha() > 0 || !stops.isEmpty(); |
889 | if (hasFill) |
890 | indexCount += fillIndexCount; |
891 | if (m_antialiasing) { |
892 | innerAATail = innerAAHead = indexCount + (innerAAIndexCount >> 1) + 1; |
893 | indexCount += innerAAIndexCount; |
894 | } |
895 | if (penWidth) { |
896 | borderTail = borderHead = indexCount + (borderIndexCount >> 1) + 1; |
897 | indexCount += borderIndexCount; |
898 | } |
899 | if (m_antialiasing && penWidth) { |
900 | outerAATail = outerAAHead = indexCount + (outerAAIndexCount >> 1) + 1; |
901 | indexCount += outerAAIndexCount; |
902 | } |
903 | |
904 | g->allocate(vertexCount, indexCount); |
905 | vertices = reinterpret_cast<Vertex *>(g->vertexData()); |
906 | memset(s: vertices, c: 0, n: vertexCount * vertexStride); |
907 | quint16 *indices = g->indexDataAsUShort(); |
908 | quint16 index = 0; |
909 | |
910 | float innerStart = (m_gradient_is_vertical ? innerRect.top() : innerRect.left()); |
911 | float innerEnd = (m_gradient_is_vertical ? innerRect.bottom() : innerRect.right()); |
912 | float outerStart = (m_gradient_is_vertical ? outerRect.top() : outerRect.left()); |
913 | float outerEnd = (m_gradient_is_vertical ? outerRect.bottom() : outerRect.right()); |
914 | |
915 | float innerSecondaryStart = (m_gradient_is_vertical ? innerRect.left() : innerRect.top()); |
916 | float innerSecondaryEnd = (m_gradient_is_vertical ? innerRect.right() : innerRect.bottom()); |
917 | float outerSecondaryStart = (m_gradient_is_vertical ? outerRect.left() : outerRect.top()); |
918 | float outerSecondaryEnd = (m_gradient_is_vertical ? outerRect.right() : outerRect.bottom()); |
919 | |
920 | for (int part = -1; part <= 1; part += 2) { |
921 | float innerEdge = (part == 1 ? innerEnd : innerStart); |
922 | float outerEdge = (part == 1 ? outerEnd : outerStart); |
923 | gradientPos = (innerEdge - innerStart + penWidth) / gradientLength; |
924 | |
925 | while (nextGradientStop <= lastGradientStop && stops.at(i: nextGradientStop).first <= gradientPos) { |
926 | // Insert vertices at gradient stops. |
927 | float gp = (innerStart - penWidth) + stops.at(i: nextGradientStop).first * gradientLength; |
928 | |
929 | fillColor = colorToColor4ub(c: stops.at(i: nextGradientStop).second); |
930 | |
931 | if (hasFill) { |
932 | indices[fillHead++] = index; |
933 | indices[fillHead++] = index + 1; |
934 | } |
935 | |
936 | if (penWidth) { |
937 | --borderHead; |
938 | indices[borderHead] = indices[borderHead + 2]; |
939 | indices[--borderHead] = index + 2; |
940 | indices[borderTail++] = index + 3; |
941 | indices[borderTail] = indices[borderTail - 2]; |
942 | ++borderTail; |
943 | } |
944 | |
945 | if (m_antialiasing) { |
946 | indices[--innerAAHead] = index + 2; |
947 | indices[--innerAAHead] = index; |
948 | indices[innerAATail++] = index + 1; |
949 | indices[innerAATail++] = index + 3; |
950 | |
951 | bool lower = stops.at(i: nextGradientStop).first > 0.5f; |
952 | float dp = lower ? qMin(a: 0.0f, b: gradientLength - gp - delta) : qMax(a: 0.0f, b: delta - gp); |
953 | smoothVertices[index++].set(primary: gp, secondary: innerSecondaryEnd, ncolor: fillColor, dPrimary: dp, dSecondary: secondaryLength - innerSecondaryEnd - delta, vertical: m_gradient_is_vertical); |
954 | smoothVertices[index++].set(primary: gp, secondary: innerSecondaryStart, ncolor: fillColor, dPrimary: dp, dSecondary: delta - innerSecondaryStart, vertical: m_gradient_is_vertical); |
955 | if (penWidth) { |
956 | 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); |
957 | 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); |
958 | } else { |
959 | smoothVertices[index++].set(primary: gp, secondary: innerSecondaryEnd, ncolor: transparent, dPrimary: lower ? delta : -delta, dSecondary: delta, vertical: m_gradient_is_vertical); |
960 | smoothVertices[index++].set(primary: gp, secondary: innerSecondaryStart, ncolor: transparent, dPrimary: lower ? delta : -delta, dSecondary: -delta, vertical: m_gradient_is_vertical); |
961 | } |
962 | } else { |
963 | vertices[index++].set(primary: gp, secondary: innerSecondaryEnd, ncolor: fillColor, vertical: m_gradient_is_vertical); |
964 | vertices[index++].set(primary: gp, secondary: innerSecondaryStart, ncolor: fillColor, vertical: m_gradient_is_vertical); |
965 | if (penWidth) { |
966 | vertices[index++].set(primary: gp, secondary: innerSecondaryEnd, ncolor: borderColor, vertical: m_gradient_is_vertical); |
967 | vertices[index++].set(primary: gp, secondary: innerSecondaryStart, ncolor: borderColor, vertical: m_gradient_is_vertical); |
968 | } |
969 | } |
970 | ++nextGradientStop; |
971 | } |
972 | |
973 | if (!stops.isEmpty()) { |
974 | if (nextGradientStop == 0) { |
975 | fillColor = colorToColor4ub(c: stops.at(i: 0).second); |
976 | } else if (nextGradientStop == stops.size()) { |
977 | fillColor = colorToColor4ub(c: stops.last().second); |
978 | } else { |
979 | const QGradientStop &prev = stops.at(i: nextGradientStop - 1); |
980 | const QGradientStop &next = stops.at(i: nextGradientStop); |
981 | float t = (gradientPos - prev.first) / (next.first - prev.first); |
982 | fillColor = colorToColor4ub(c: prev.second) * (1 - t) + colorToColor4ub(c: next.second) * t; |
983 | } |
984 | } |
985 | |
986 | if (hasFill) { |
987 | indices[fillHead++] = index; |
988 | indices[fillHead++] = index + 1; |
989 | } |
990 | |
991 | if (penWidth) { |
992 | indices[--borderHead] = index + 4; |
993 | indices[--borderHead] = index + 2; |
994 | indices[borderTail++] = index + 3; |
995 | indices[borderTail++] = index + 5; |
996 | } |
997 | |
998 | if (m_antialiasing) { |
999 | indices[--innerAAHead] = index + 2; |
1000 | indices[--innerAAHead] = index; |
1001 | indices[innerAATail++] = index + 1; |
1002 | indices[innerAATail++] = index + 3; |
1003 | |
1004 | float dp = part == 1 ? qMin(a: 0.0f, b: gradientLength - innerEdge - delta) : qMax(a: 0.0f, b: delta - innerEdge); |
1005 | smoothVertices[index++].set(primary: innerEdge, secondary: innerSecondaryEnd, ncolor: fillColor, dPrimary: dp, dSecondary: secondaryLength - innerSecondaryEnd - delta, vertical: m_gradient_is_vertical); |
1006 | smoothVertices[index++].set(primary: innerEdge, secondary: innerSecondaryStart, ncolor: fillColor, dPrimary: dp, dSecondary: delta - innerSecondaryStart, vertical: m_gradient_is_vertical); |
1007 | |
1008 | if (penWidth) { |
1009 | smoothVertices[index++].set(primary: innerEdge, secondary: innerSecondaryEnd, ncolor: borderColor, dPrimary: 0.49f * penWidth * part, dSecondary: 0.49f * penWidth, vertical: m_gradient_is_vertical); |
1010 | smoothVertices[index++].set(primary: innerEdge, secondary: innerSecondaryStart, ncolor: borderColor, dPrimary: 0.49f * penWidth * part, dSecondary: -0.49f * penWidth, vertical: m_gradient_is_vertical); |
1011 | smoothVertices[index++].set(primary: outerEdge, secondary: outerSecondaryEnd, ncolor: borderColor, dPrimary: -0.49f * penWidth * part, dSecondary: -0.49f * penWidth, vertical: m_gradient_is_vertical); |
1012 | smoothVertices[index++].set(primary: outerEdge, secondary: outerSecondaryStart, ncolor: borderColor, dPrimary: -0.49f * penWidth * part, dSecondary: 0.49f * penWidth, vertical: m_gradient_is_vertical); |
1013 | smoothVertices[index++].set(primary: outerEdge, secondary: outerSecondaryEnd, ncolor: transparent, dPrimary: delta * part, dSecondary: delta, vertical: m_gradient_is_vertical); |
1014 | smoothVertices[index++].set(primary: outerEdge, secondary: outerSecondaryStart, ncolor: transparent, dPrimary: delta * part, dSecondary: -delta, vertical: m_gradient_is_vertical); |
1015 | |
1016 | indices[--outerAAHead] = index - 2; |
1017 | indices[--outerAAHead] = index - 4; |
1018 | indices[outerAATail++] = index - 3; |
1019 | indices[outerAATail++] = index - 1; |
1020 | } else { |
1021 | smoothVertices[index++].set(primary: innerEdge, secondary: innerSecondaryEnd, ncolor: transparent, dPrimary: delta * part, dSecondary: delta, vertical: m_gradient_is_vertical); |
1022 | smoothVertices[index++].set(primary: innerEdge, secondary: innerSecondaryStart, ncolor: transparent, dPrimary: delta * part, dSecondary: -delta, vertical: m_gradient_is_vertical); |
1023 | } |
1024 | } else { |
1025 | vertices[index++].set(primary: innerEdge, secondary: innerSecondaryEnd, ncolor: fillColor, vertical: m_gradient_is_vertical); |
1026 | vertices[index++].set(primary: innerEdge, secondary: innerSecondaryStart, ncolor: fillColor, vertical: m_gradient_is_vertical); |
1027 | if (penWidth) { |
1028 | vertices[index++].set(primary: innerEdge, secondary: innerSecondaryEnd, ncolor: borderColor, vertical: m_gradient_is_vertical); |
1029 | vertices[index++].set(primary: innerEdge, secondary: innerSecondaryStart, ncolor: borderColor, vertical: m_gradient_is_vertical); |
1030 | vertices[index++].set(primary: outerEdge, secondary: outerSecondaryEnd, ncolor: borderColor, vertical: m_gradient_is_vertical); |
1031 | vertices[index++].set(primary: outerEdge, secondary: outerSecondaryStart, ncolor: borderColor, vertical: m_gradient_is_vertical); |
1032 | } |
1033 | } |
1034 | } |
1035 | Q_ASSERT(index == vertexCount); |
1036 | |
1037 | // Close the triangle strips. |
1038 | if (m_antialiasing) { |
1039 | indices[--innerAAHead] = indices[innerAATail - 1]; |
1040 | indices[--innerAAHead] = indices[innerAATail - 2]; |
1041 | Q_ASSERT(innerAATail <= indexCount); |
1042 | } |
1043 | if (penWidth) { |
1044 | indices[--borderHead] = indices[borderTail - 1]; |
1045 | indices[--borderHead] = indices[borderTail - 2]; |
1046 | Q_ASSERT(borderTail <= indexCount); |
1047 | } |
1048 | if (m_antialiasing && penWidth) { |
1049 | indices[--outerAAHead] = indices[outerAATail - 1]; |
1050 | indices[--outerAAHead] = indices[outerAATail - 2]; |
1051 | Q_ASSERT(outerAATail == indexCount); |
1052 | } |
1053 | } |
1054 | } |
1055 | |
1056 | QT_END_NAMESPACE |
1057 | |