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
5
6#include <QtCore/qmath.h>
7
9
10namespace
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, &g, &b, &a);
24 Color4ub color = { uchar(qRound(r * a * 255)),
25 uchar(qRound(g * a * 255)),
26 uchar(qRound(b * a * 255)),
27 uchar(qRound(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;
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
65 {
66 static QSGGeometry::Attribute data[] = {
67 QSGGeometry::Attribute::createWithAttributeType(0, 2, QSGGeometry::FloatType, QSGGeometry::PositionAttribute),
68 QSGGeometry::Attribute::createWithAttributeType(1, 4, QSGGeometry::UnsignedByteType, QSGGeometry::ColorAttribute),
69 QSGGeometry::Attribute::createWithAttributeType(2, 2, QSGGeometry::FloatType, QSGGeometry::TexCoordAttribute)
70 };
71 static QSGGeometry::AttributeSet attrs = { 3, sizeof(SmoothVertex), data };
72 return attrs;
73 }
74}
75
76QSGBasicInternalRectangleNode::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(this, QLatin1String("internalrectangle"));
89#endif
90}
91
92void QSGBasicInternalRectangleNode::setRect(const QRectF &rect)
93{
94 if (rect == m_rect)
95 return;
96 m_rect = rect;
97 m_dirty_geometry = true;
98}
99
100void 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
109void 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
118void 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
127void 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
140void 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
149void QSGBasicInternalRectangleNode::setRadius(qreal radius)
150{
151 if (radius == m_radius)
152 return;
153 m_radius = radius;
154 m_dirty_geometry = true;
155}
156
157void QSGBasicInternalRectangleNode::setTopLeftRadius(qreal radius)
158{
159 if (radius == m_topLeftRadius)
160 return;
161 m_topLeftRadius = radius;
162 m_dirty_geometry = true;
163}
164void QSGBasicInternalRectangleNode::setTopRightRadius(qreal radius)
165{
166 if (radius == m_topRightRadius)
167 return;
168 m_topRightRadius = radius;
169 m_dirty_geometry = true;
170}
171void QSGBasicInternalRectangleNode::setBottomLeftRadius(qreal radius)
172{
173 if (radius == m_bottomLeftRadius)
174 return;
175 m_bottomLeftRadius = radius;
176 m_dirty_geometry = true;
177}
178void QSGBasicInternalRectangleNode::setBottomRightRadius(qreal radius)
179{
180 if (radius == m_bottomRightRadius)
181 return;
182 m_bottomRightRadius = radius;
183 m_dirty_geometry = true;
184}
185
186void 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
205void QSGBasicInternalRectangleNode::setAligned(bool aligned)
206{
207 if (aligned == m_aligned)
208 return;
209 m_aligned = aligned;
210 m_dirty_geometry = true;
211}
212
213void 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);
221 markDirty(state);
222 }
223}
224
225void QSGBasicInternalRectangleNode::updateGeometry()
226{
227 float width = float(m_rect.width());
228 float height = float(m_rect.height());
229 float penWidth = qMin(qMin(width, height) * 0.5f, float(m_pen_width));
230
231 if (m_aligned)
232 penWidth = qRound(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(m_color);
244 Color4ub borderColor = colorToColor4ub(m_border_color);
245 Color4ub transparent = { 0, 0, 0, 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(nextGradientStop).first <= gradientPos)
255 ++nextGradientStop;
256 int lastGradientStop = stops.size() - 1;
257 float lastGradientPos = 1.0f - penWidth / gradientLength;
258 while (lastGradientStop >= nextGradientStop && stops.at(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(qMin(width, height) * 0.4999f, float(m_topLeftRadius < 0 ? m_radius : m_topLeftRadius));
271 float radiusTR = qMin(qMin(width, height) * 0.4999f, float(m_topRightRadius < 0 ? m_radius : m_topRightRadius));
272 float radiusBL = qMin(qMin(width, height) * 0.4999f, float(m_bottomLeftRadius < 0 ? m_radius : m_bottomLeftRadius));
273 float radiusBR = qMin(qMin(width, height) * 0.4999f, 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(radiusTL - penWidth * 1.0f, 0.01);
289 const float innerRadiusTR = qMax(radiusTR - penWidth * 1.0f, 0.01);
290 const float innerRadiusBL = qMax(radiusBL - penWidth * 1.0f, 0.01);
291 const float innerRadiusBR = qMax(radiusBR - penWidth * 1.0f, 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(width, height) * 0.5f;
297
298 int segmentsTL = radiusTL == 0 ? 0 : qBound(3, qCeil(radiusTL * (M_PI / 6)), 18);
299 int segmentsTR = radiusTR == 0 ? 0 : qBound(3, qCeil(radiusTR * (M_PI / 6)), 18);
300 int segmentsBL = radiusBL == 0 ? 0 : qBound(3, qCeil(radiusBL * (M_PI / 6)), 18);
301 int segmentsBR = radiusBR == 0 ? 0 : qBound(3, qCeil(radiusBR * (M_PI / 6)), 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(vertices, 0, 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(angleTL);
408 const float sinStepTL = qFastSin(angleTL);
409 const float angleTR = 0.5f * float(M_PI) / segmentsTR;
410 const float cosStepTR = qFastCos(angleTR);
411 const float sinStepTR = qFastSin(angleTR);
412 const float angleBL = 0.5f * float(M_PI) / segmentsBL;
413 const float cosStepBL = qFastCos(angleBL);
414 const float sinStepBL = qFastSin(angleBL);
415 const float angleBR = 0.5f * float(M_PI) / segmentsBR;
416 const float cosStepBR = qFastCos(angleBR);
417 const float sinStepBR = qFastSin(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(0.0, t, 1.0);
525
526 int i = 1;
527 if (t < stops.first().first)
528 return colorToColor4ub(stops.first().second);
529 while (i < stops.size()) {
530 const QGradientStop &prev = stops.at(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(prev.second) * (1. - t) + colorToColor4ub(next.second) * t; }
535 i++;
536 }
537 return colorToColor4ub(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(nextGradientStop).first <= gradientPos) {
705 float gradientX;
706 float gradientYLeft;
707 float gradientYRight;
708
709 // Insert vertices at gradient stops
710 gradientX = gradientStart + stops.at(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(nextGradientStop).first > 0.5f;
739 float dp = lower ? qMin(0.0f, gradientLength - gradientX - delta) : qMax(0.0f, delta - gradientX);
740 smoothVertices[index++].set(gradientX, gradientYRight, fillColor, dp, secondaryLength - gradientYRight - delta, m_gradient_is_vertical);
741 smoothVertices[index++].set(gradientX, gradientYLeft, fillColor, dp, delta - gradientYLeft, m_gradient_is_vertical);
742 if (penWidth) {
743 smoothVertices[index++].set(gradientX, gradientYRight, borderColor, -0.49f * penWidth * cosAngleRight, 0.49f * penWidth * sinAngleRight, m_gradient_is_vertical);
744 smoothVertices[index++].set(gradientX, gradientYLeft, borderColor, -0.49f * penWidth * cosAngleLeft, -0.49f * penWidth * sinAngleLeft, m_gradient_is_vertical);
745 } else {
746 dp = lower ? delta : -delta;
747 smoothVertices[index++].set(gradientX, gradientYRight, transparent, dp, delta, m_gradient_is_vertical);
748 smoothVertices[index++].set(gradientX, gradientYLeft, transparent, dp, -delta, m_gradient_is_vertical);
749 }
750 } else {
751 vertices[index++].set(gradientX, gradientYRight, fillColor, m_gradient_is_vertical);
752 vertices[index++].set(gradientX, gradientYLeft, fillColor, m_gradient_is_vertical);
753 if (penWidth) {
754 vertices[index++].set(gradientX, gradientYRight, borderColor, m_gradient_is_vertical);
755 vertices[index++].set(gradientX, gradientYLeft, borderColor, 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(0.0f, gradientLength - xRight - delta) : qMax(0.0f, delta - xRight);
789 smoothVertices[index++].set(xRight, yRight, fillColor, dp, secondaryLength - yRight - delta, m_gradient_is_vertical);
790 smoothVertices[index++].set(xLeft, yLeft, fillColor, dp, delta - yLeft, m_gradient_is_vertical);
791
792 dp = part ? delta : -delta;
793 if (penWidth) {
794 smoothVertices[index++].set(xRight, yRight, borderColor, -0.49f * penWidth * cosAngleRight, 0.49f * penWidth * sinAngleRight, m_gradient_is_vertical);
795 smoothVertices[index++].set(xLeft, yLeft, borderColor, -0.49f * penWidth * cosAngleLeft, -0.49f * penWidth * sinAngleLeft, m_gradient_is_vertical);
796 smoothVertices[index++].set(outerXRight, outerYRight, borderColor, 0.49f * penWidth * cosAngleRight, -0.49f * penWidth * sinAngleRight, m_gradient_is_vertical);
797 smoothVertices[index++].set(outerXLeft, outerYLeft, borderColor, 0.49f * penWidth * cosAngleLeft, 0.49f * penWidth * sinAngleLeft, m_gradient_is_vertical);
798 smoothVertices[index++].set(outerXRight, outerYRight, transparent, dp, delta, m_gradient_is_vertical);
799 smoothVertices[index++].set(outerXLeft, outerYLeft, transparent, dp, -delta, 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(xRight, yRight, transparent, dp, delta, m_gradient_is_vertical);
807 smoothVertices[index++].set(xLeft, yLeft, transparent, dp, -delta, m_gradient_is_vertical);
808 }
809 } else {
810 vertices[index++].set(xRight, yRight, fillColor, m_gradient_is_vertical);
811 vertices[index++].set(xLeft, yLeft, fillColor, m_gradient_is_vertical);
812 if (penWidth) {
813 vertices[index++].set(xRight, yRight, borderColor, m_gradient_is_vertical);
814 vertices[index++].set(xLeft, yLeft, borderColor, m_gradient_is_vertical);
815 vertices[index++].set(outerXRight, outerYRight, borderColor, m_gradient_is_vertical);
816 vertices[index++].set(outerXLeft, outerYLeft, borderColor, 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(1.0f * penWidth, 1.0f * penWidth, -1.0f * penWidth, -1.0f * penWidth);
864
865 float delta = qMin(width, 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(vertices, 0, 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(nextGradientStop).first <= gradientPos) {
926 // Insert vertices at gradient stops.
927 float gp = (innerStart - penWidth) + stops.at(nextGradientStop).first * gradientLength;
928
929 fillColor = colorToColor4ub(stops.at(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(nextGradientStop).first > 0.5f;
952 float dp = lower ? qMin(0.0f, gradientLength - gp - delta) : qMax(0.0f, delta - gp);
953 smoothVertices[index++].set(gp, innerSecondaryEnd, fillColor, dp, secondaryLength - innerSecondaryEnd - delta, m_gradient_is_vertical);
954 smoothVertices[index++].set(gp, innerSecondaryStart, fillColor, dp, delta - innerSecondaryStart, m_gradient_is_vertical);
955 if (penWidth) {
956 smoothVertices[index++].set(gp, innerSecondaryEnd, borderColor, (lower ? 0.49f : -0.49f) * penWidth, 0.49f * penWidth, m_gradient_is_vertical);
957 smoothVertices[index++].set(gp, innerSecondaryStart, borderColor, (lower ? 0.49f : -0.49f) * penWidth, -0.49f * penWidth, m_gradient_is_vertical);
958 } else {
959 smoothVertices[index++].set(gp, innerSecondaryEnd, transparent, lower ? delta : -delta, delta, m_gradient_is_vertical);
960 smoothVertices[index++].set(gp, innerSecondaryStart, transparent, lower ? delta : -delta, -delta, m_gradient_is_vertical);
961 }
962 } else {
963 vertices[index++].set(gp, innerSecondaryEnd, fillColor, m_gradient_is_vertical);
964 vertices[index++].set(gp, innerSecondaryStart, fillColor, m_gradient_is_vertical);
965 if (penWidth) {
966 vertices[index++].set(gp, innerSecondaryEnd, borderColor, m_gradient_is_vertical);
967 vertices[index++].set(gp, innerSecondaryStart, borderColor, m_gradient_is_vertical);
968 }
969 }
970 ++nextGradientStop;
971 }
972
973 if (!stops.isEmpty()) {
974 if (nextGradientStop == 0) {
975 fillColor = colorToColor4ub(stops.at(0).second);
976 } else if (nextGradientStop == stops.size()) {
977 fillColor = colorToColor4ub(stops.last().second);
978 } else {
979 const QGradientStop &prev = stops.at(nextGradientStop - 1);
980 const QGradientStop &next = stops.at(nextGradientStop);
981 float t = (gradientPos - prev.first) / (next.first - prev.first);
982 fillColor = colorToColor4ub(prev.second) * (1 - t) + colorToColor4ub(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(0.0f, gradientLength - innerEdge - delta) : qMax(0.0f, delta - innerEdge);
1005 smoothVertices[index++].set(innerEdge, innerSecondaryEnd, fillColor, dp, secondaryLength - innerSecondaryEnd - delta, m_gradient_is_vertical);
1006 smoothVertices[index++].set(innerEdge, innerSecondaryStart, fillColor, dp, delta - innerSecondaryStart, m_gradient_is_vertical);
1007
1008 if (penWidth) {
1009 smoothVertices[index++].set(innerEdge, innerSecondaryEnd, borderColor, 0.49f * penWidth * part, 0.49f * penWidth, m_gradient_is_vertical);
1010 smoothVertices[index++].set(innerEdge, innerSecondaryStart, borderColor, 0.49f * penWidth * part, -0.49f * penWidth, m_gradient_is_vertical);
1011 smoothVertices[index++].set(outerEdge, outerSecondaryEnd, borderColor, -0.49f * penWidth * part, -0.49f * penWidth, m_gradient_is_vertical);
1012 smoothVertices[index++].set(outerEdge, outerSecondaryStart, borderColor, -0.49f * penWidth * part, 0.49f * penWidth, m_gradient_is_vertical);
1013 smoothVertices[index++].set(outerEdge, outerSecondaryEnd, transparent, delta * part, delta, m_gradient_is_vertical);
1014 smoothVertices[index++].set(outerEdge, outerSecondaryStart, transparent, delta * part, -delta, 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(innerEdge, innerSecondaryEnd, transparent, delta * part, delta, m_gradient_is_vertical);
1022 smoothVertices[index++].set(innerEdge, innerSecondaryStart, transparent, delta * part, -delta, m_gradient_is_vertical);
1023 }
1024 } else {
1025 vertices[index++].set(innerEdge, innerSecondaryEnd, fillColor, m_gradient_is_vertical);
1026 vertices[index++].set(innerEdge, innerSecondaryStart, fillColor, m_gradient_is_vertical);
1027 if (penWidth) {
1028 vertices[index++].set(innerEdge, innerSecondaryEnd, borderColor, m_gradient_is_vertical);
1029 vertices[index++].set(innerEdge, innerSecondaryStart, borderColor, m_gradient_is_vertical);
1030 vertices[index++].set(outerEdge, outerSecondaryEnd, borderColor, m_gradient_is_vertical);
1031 vertices[index++].set(outerEdge, outerSecondaryStart, borderColor, 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
1056QT_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)