| 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 | |