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