1 /* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #define LOG_TAG "OpenGLRenderer" 18 #define LOG_NDEBUG 1 19 #define ATRACE_TAG ATRACE_TAG_VIEW 20 21 #define VERTEX_DEBUG 0 22 23 #if VERTEX_DEBUG 24 #define DEBUG_DUMP_ALPHA_BUFFER() \ 25 for (unsigned int i = 0; i < vertexBuffer.getSize(); i++) { \ 26 ALOGD("point %d at %f %f, alpha %f", \ 27 i, buffer[i].x, buffer[i].y, buffer[i].alpha); \ 28 } 29 #define DEBUG_DUMP_BUFFER() \ 30 for (unsigned int i = 0; i < vertexBuffer.getSize(); i++) { \ 31 ALOGD("point %d at %f %f", i, buffer[i].x, buffer[i].y); \ 32 } 33 #else 34 #define DEBUG_DUMP_ALPHA_BUFFER() 35 #define DEBUG_DUMP_BUFFER() 36 #endif 37 38 #include <SkPath.h> 39 #include <SkPaint.h> 40 41 #include <stdlib.h> 42 #include <stdint.h> 43 #include <sys/types.h> 44 45 #include <utils/Log.h> 46 #include <utils/Trace.h> 47 48 #include "PathTessellator.h" 49 #include "Matrix.h" 50 #include "Vector.h" 51 #include "Vertex.h" 52 #include "utils/MathUtils.h" 53 54 namespace android { 55 namespace uirenderer { 56 57 #define OUTLINE_REFINE_THRESHOLD_SQUARED (0.5f * 0.5f) 58 #define ROUND_CAP_THRESH 0.25f 59 #define PI 3.1415926535897932f 60 #define MAX_DEPTH 15 61 62 /** 63 * Extracts the x and y scale from the transform as positive values, and clamps them 64 */ 65 void PathTessellator::extractTessellationScales(const Matrix4& transform, 66 float* scaleX, float* scaleY) { 67 if (CC_LIKELY(transform.isPureTranslate())) { 68 *scaleX = 1.0f; 69 *scaleY = 1.0f; 70 } else { 71 float m00 = transform.data[Matrix4::kScaleX]; 72 float m01 = transform.data[Matrix4::kSkewY]; 73 float m10 = transform.data[Matrix4::kSkewX]; 74 float m11 = transform.data[Matrix4::kScaleY]; 75 *scaleX = MathUtils::clampTessellationScale(sqrt(m00 * m00 + m01 * m01)); 76 *scaleY = MathUtils::clampTessellationScale(sqrt(m10 * m10 + m11 * m11)); 77 } 78 } 79 80 /** 81 * Produces a pseudo-normal for a vertex, given the normals of the two incoming lines. If the offset 82 * from each vertex in a perimeter is calculated, the resultant lines connecting the offset vertices 83 * will be offset by 1.0 84 * 85 * Note that we can't add and normalize the two vectors, that would result in a rectangle having an 86 * offset of (sqrt(2)/2, sqrt(2)/2) at each corner, instead of (1, 1) 87 * 88 * NOTE: assumes angles between normals 90 degrees or less 89 */ 90 inline static Vector2 totalOffsetFromNormals(const Vector2& normalA, const Vector2& normalB) { 91 return (normalA + normalB) / (1 + fabs(normalA.dot(normalB))); 92 } 93 94 /** 95 * Structure used for storing useful information about the SkPaint and scale used for tessellating 96 */ 97 struct PaintInfo { 98 public: 99 PaintInfo(const SkPaint* paint, const mat4& transform) : 100 style(paint->getStyle()), cap(paint->getStrokeCap()), isAA(paint->isAntiAlias()), 101 halfStrokeWidth(paint->getStrokeWidth() * 0.5f), maxAlpha(1.0f) { 102 // compute inverse scales 103 if (CC_LIKELY(transform.isPureTranslate())) { 104 inverseScaleX = 1.0f; 105 inverseScaleY = 1.0f; 106 } else { 107 float scaleX, scaleY; 108 PathTessellator::extractTessellationScales(transform, &scaleX, &scaleY); 109 inverseScaleX = 1.0f / scaleX; 110 inverseScaleY = 1.0f / scaleY; 111 } 112 113 if (isAA && halfStrokeWidth != 0 && inverseScaleX == inverseScaleY && 114 2 * halfStrokeWidth < inverseScaleX) { 115 // AA, with non-hairline stroke, width < 1 pixel. Scale alpha and treat as hairline. 116 maxAlpha *= (2 * halfStrokeWidth) / inverseScaleX; 117 halfStrokeWidth = 0.0f; 118 } 119 } 120 121 SkPaint::Style style; 122 SkPaint::Cap cap; 123 bool isAA; 124 float inverseScaleX; 125 float inverseScaleY; 126 float halfStrokeWidth; 127 float maxAlpha; 128 129 inline void scaleOffsetForStrokeWidth(Vector2& offset) const { 130 if (halfStrokeWidth == 0.0f) { 131 // hairline - compensate for scale 132 offset.x *= 0.5f * inverseScaleX; 133 offset.y *= 0.5f * inverseScaleY; 134 } else { 135 offset *= halfStrokeWidth; 136 } 137 } 138 139 /** 140 * NOTE: the input will not always be a normal, especially for sharp edges - it should be the 141 * result of totalOffsetFromNormals (see documentation there) 142 */ 143 inline Vector2 deriveAAOffset(const Vector2& offset) const { 144 return (Vector2){offset.x * 0.5f * inverseScaleX, offset.y * 0.5f * inverseScaleY}; 145 } 146 147 /** 148 * Returns the number of cap divisions beyond the minimum 2 (kButt_Cap/kSquareCap will return 0) 149 * Should only be used when stroking and drawing caps 150 */ 151 inline int capExtraDivisions() const { 152 if (cap == SkPaint::kRound_Cap) { 153 if (halfStrokeWidth == 0.0f) return 2; 154 155 // ROUND_CAP_THRESH is the maximum error for polygonal approximation of the round cap 156 const float errConst = (-ROUND_CAP_THRESH / halfStrokeWidth + 1); 157 const float targetCosVal = 2 * errConst * errConst - 1; 158 int neededDivisions = (int)(ceilf(PI / acos(targetCosVal)/2)) * 2; 159 return neededDivisions; 160 } 161 return 0; 162 } 163 164 /** 165 * Outset the bounds of point data (for line endpoints or points) to account for stroke 166 * geometry. 167 * 168 * bounds are in pre-scaled space. 169 */ 170 void expandBoundsForStroke(Rect* bounds) const { 171 if (halfStrokeWidth == 0) { 172 // hairline, outset by (0.5f + fudge factor) in post-scaling space 173 bounds->outset(fabs(inverseScaleX) * (0.5f + Vertex::GeometryFudgeFactor()), 174 fabs(inverseScaleY) * (0.5f + Vertex::GeometryFudgeFactor())); 175 } else { 176 // non hairline, outset by half stroke width pre-scaled, and fudge factor post scaled 177 bounds->outset(halfStrokeWidth + fabs(inverseScaleX) * Vertex::GeometryFudgeFactor(), 178 halfStrokeWidth + fabs(inverseScaleY) * Vertex::GeometryFudgeFactor()); 179 } 180 } 181 }; 182 183 void getFillVerticesFromPerimeter(const Vector<Vertex>& perimeter, VertexBuffer& vertexBuffer) { 184 Vertex* buffer = vertexBuffer.alloc<Vertex>(perimeter.size()); 185 186 int currentIndex = 0; 187 // zig zag between all previous points on the inside of the hull to create a 188 // triangle strip that fills the hull 189 int srcAindex = 0; 190 int srcBindex = perimeter.size() - 1; 191 while (srcAindex <= srcBindex) { 192 buffer[currentIndex++] = perimeter[srcAindex]; 193 if (srcAindex == srcBindex) break; 194 buffer[currentIndex++] = perimeter[srcBindex]; 195 srcAindex++; 196 srcBindex--; 197 } 198 } 199 200 /* 201 * Fills a vertexBuffer with non-alpha vertices, zig-zagging at each perimeter point to create a 202 * tri-strip as wide as the stroke. 203 * 204 * Uses an additional 2 vertices at the end to wrap around, closing the tri-strip 205 * (for a total of perimeter.size() * 2 + 2 vertices) 206 */ 207 void getStrokeVerticesFromPerimeter(const PaintInfo& paintInfo, const Vector<Vertex>& perimeter, 208 VertexBuffer& vertexBuffer) { 209 Vertex* buffer = vertexBuffer.alloc<Vertex>(perimeter.size() * 2 + 2); 210 211 int currentIndex = 0; 212 const Vertex* last = &(perimeter[perimeter.size() - 1]); 213 const Vertex* current = &(perimeter[0]); 214 Vector2 lastNormal = {current->y - last->y, last->x - current->x}; 215 lastNormal.normalize(); 216 for (unsigned int i = 0; i < perimeter.size(); i++) { 217 const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]); 218 Vector2 nextNormal = {next->y - current->y, current->x - next->x}; 219 nextNormal.normalize(); 220 221 Vector2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal); 222 paintInfo.scaleOffsetForStrokeWidth(totalOffset); 223 224 Vertex::set(&buffer[currentIndex++], 225 current->x + totalOffset.x, 226 current->y + totalOffset.y); 227 228 Vertex::set(&buffer[currentIndex++], 229 current->x - totalOffset.x, 230 current->y - totalOffset.y); 231 232 last = current; 233 current = next; 234 lastNormal = nextNormal; 235 } 236 237 // wrap around to beginning 238 buffer[currentIndex++] = buffer[0]; 239 buffer[currentIndex++] = buffer[1]; 240 241 DEBUG_DUMP_BUFFER(); 242 } 243 244 static inline void storeBeginEnd(const PaintInfo& paintInfo, const Vertex& center, 245 const Vector2& normal, Vertex* buffer, int& currentIndex, bool begin) { 246 Vector2 strokeOffset = normal; 247 paintInfo.scaleOffsetForStrokeWidth(strokeOffset); 248 249 Vector2 referencePoint = {center.x, center.y}; 250 if (paintInfo.cap == SkPaint::kSquare_Cap) { 251 Vector2 rotated = {-strokeOffset.y, strokeOffset.x}; 252 referencePoint += rotated * (begin ? -1 : 1); 253 } 254 255 Vertex::set(&buffer[currentIndex++], referencePoint + strokeOffset); 256 Vertex::set(&buffer[currentIndex++], referencePoint - strokeOffset); 257 } 258 259 /** 260 * Fills a vertexBuffer with non-alpha vertices similar to getStrokeVerticesFromPerimeter, except: 261 * 262 * 1 - Doesn't need to wrap around, since the input vertices are unclosed 263 * 264 * 2 - can zig-zag across 'extra' vertices at either end, to create round caps 265 */ 266 void getStrokeVerticesFromUnclosedVertices(const PaintInfo& paintInfo, 267 const Vector<Vertex>& vertices, VertexBuffer& vertexBuffer) { 268 const int extra = paintInfo.capExtraDivisions(); 269 const int allocSize = (vertices.size() + extra) * 2; 270 Vertex* buffer = vertexBuffer.alloc<Vertex>(allocSize); 271 272 const int lastIndex = vertices.size() - 1; 273 if (extra > 0) { 274 // tessellate both round caps 275 float beginTheta = atan2( 276 - (vertices[0].x - vertices[1].x), 277 vertices[0].y - vertices[1].y); 278 float endTheta = atan2( 279 - (vertices[lastIndex].x - vertices[lastIndex - 1].x), 280 vertices[lastIndex].y - vertices[lastIndex - 1].y); 281 const float dTheta = PI / (extra + 1); 282 const float radialScale = 2.0f / (1 + cos(dTheta)); 283 284 int capOffset; 285 for (int i = 0; i < extra; i++) { 286 if (i < extra / 2) { 287 capOffset = extra - 2 * i - 1; 288 } else { 289 capOffset = 2 * i - extra; 290 } 291 292 beginTheta += dTheta; 293 Vector2 beginRadialOffset = {cos(beginTheta), sin(beginTheta)}; 294 paintInfo.scaleOffsetForStrokeWidth(beginRadialOffset); 295 Vertex::set(&buffer[capOffset], 296 vertices[0].x + beginRadialOffset.x, 297 vertices[0].y + beginRadialOffset.y); 298 299 endTheta += dTheta; 300 Vector2 endRadialOffset = {cos(endTheta), sin(endTheta)}; 301 paintInfo.scaleOffsetForStrokeWidth(endRadialOffset); 302 Vertex::set(&buffer[allocSize - 1 - capOffset], 303 vertices[lastIndex].x + endRadialOffset.x, 304 vertices[lastIndex].y + endRadialOffset.y); 305 } 306 } 307 308 int currentIndex = extra; 309 const Vertex* last = &(vertices[0]); 310 const Vertex* current = &(vertices[1]); 311 Vector2 lastNormal = {current->y - last->y, last->x - current->x}; 312 lastNormal.normalize(); 313 314 storeBeginEnd(paintInfo, vertices[0], lastNormal, buffer, currentIndex, true); 315 316 for (unsigned int i = 1; i < vertices.size() - 1; i++) { 317 const Vertex* next = &(vertices[i + 1]); 318 Vector2 nextNormal = {next->y - current->y, current->x - next->x}; 319 nextNormal.normalize(); 320 321 Vector2 strokeOffset = totalOffsetFromNormals(lastNormal, nextNormal); 322 paintInfo.scaleOffsetForStrokeWidth(strokeOffset); 323 324 Vector2 center = {current->x, current->y}; 325 Vertex::set(&buffer[currentIndex++], center + strokeOffset); 326 Vertex::set(&buffer[currentIndex++], center - strokeOffset); 327 328 current = next; 329 lastNormal = nextNormal; 330 } 331 332 storeBeginEnd(paintInfo, vertices[lastIndex], lastNormal, buffer, currentIndex, false); 333 334 DEBUG_DUMP_BUFFER(); 335 } 336 337 /** 338 * Populates a vertexBuffer with AlphaVertices to create an anti-aliased fill shape tessellation 339 * 340 * 1 - create the AA perimeter of unit width, by zig-zagging at each point around the perimeter of 341 * the shape (using 2 * perimeter.size() vertices) 342 * 343 * 2 - wrap around to the beginning to complete the perimeter (2 vertices) 344 * 345 * 3 - zig zag back and forth inside the shape to fill it (using perimeter.size() vertices) 346 */ 347 void getFillVerticesFromPerimeterAA(const PaintInfo& paintInfo, const Vector<Vertex>& perimeter, 348 VertexBuffer& vertexBuffer, float maxAlpha = 1.0f) { 349 AlphaVertex* buffer = vertexBuffer.alloc<AlphaVertex>(perimeter.size() * 3 + 2); 350 351 // generate alpha points - fill Alpha vertex gaps in between each point with 352 // alpha 0 vertex, offset by a scaled normal. 353 int currentIndex = 0; 354 const Vertex* last = &(perimeter[perimeter.size() - 1]); 355 const Vertex* current = &(perimeter[0]); 356 Vector2 lastNormal = {current->y - last->y, last->x - current->x}; 357 lastNormal.normalize(); 358 for (unsigned int i = 0; i < perimeter.size(); i++) { 359 const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]); 360 Vector2 nextNormal = {next->y - current->y, current->x - next->x}; 361 nextNormal.normalize(); 362 363 // AA point offset from original point is that point's normal, such that each side is offset 364 // by .5 pixels 365 Vector2 totalOffset = paintInfo.deriveAAOffset(totalOffsetFromNormals(lastNormal, nextNormal)); 366 367 AlphaVertex::set(&buffer[currentIndex++], 368 current->x + totalOffset.x, 369 current->y + totalOffset.y, 370 0.0f); 371 AlphaVertex::set(&buffer[currentIndex++], 372 current->x - totalOffset.x, 373 current->y - totalOffset.y, 374 maxAlpha); 375 376 last = current; 377 current = next; 378 lastNormal = nextNormal; 379 } 380 381 // wrap around to beginning 382 buffer[currentIndex++] = buffer[0]; 383 buffer[currentIndex++] = buffer[1]; 384 385 // zig zag between all previous points on the inside of the hull to create a 386 // triangle strip that fills the hull, repeating the first inner point to 387 // create degenerate tris to start inside path 388 int srcAindex = 0; 389 int srcBindex = perimeter.size() - 1; 390 while (srcAindex <= srcBindex) { 391 buffer[currentIndex++] = buffer[srcAindex * 2 + 1]; 392 if (srcAindex == srcBindex) break; 393 buffer[currentIndex++] = buffer[srcBindex * 2 + 1]; 394 srcAindex++; 395 srcBindex--; 396 } 397 398 DEBUG_DUMP_BUFFER(); 399 } 400 401 /** 402 * Stores geometry for a single, AA-perimeter (potentially rounded) cap 403 * 404 * For explanation of constants and general methodoloyg, see comments for 405 * getStrokeVerticesFromUnclosedVerticesAA() below. 406 */ 407 inline static void storeCapAA(const PaintInfo& paintInfo, const Vector<Vertex>& vertices, 408 AlphaVertex* buffer, bool isFirst, Vector2 normal, int offset) { 409 const int extra = paintInfo.capExtraDivisions(); 410 const int extraOffset = (extra + 1) / 2; 411 const int capIndex = isFirst 412 ? 2 * offset + 6 + 2 * (extra + extraOffset) 413 : offset + 2 + 2 * extraOffset; 414 if (isFirst) normal *= -1; 415 416 // TODO: this normal should be scaled by radialScale if extra != 0, see totalOffsetFromNormals() 417 Vector2 AAOffset = paintInfo.deriveAAOffset(normal); 418 419 Vector2 strokeOffset = normal; 420 paintInfo.scaleOffsetForStrokeWidth(strokeOffset); 421 Vector2 outerOffset = strokeOffset + AAOffset; 422 Vector2 innerOffset = strokeOffset - AAOffset; 423 424 Vector2 capAAOffset = {0, 0}; 425 if (paintInfo.cap != SkPaint::kRound_Cap) { 426 // if the cap is square or butt, the inside primary cap vertices will be inset in two 427 // directions - both normal to the stroke, and parallel to it. 428 capAAOffset = (Vector2){-AAOffset.y, AAOffset.x}; 429 } 430 431 // determine referencePoint, the center point for the 4 primary cap vertices 432 const Vertex* point = isFirst ? vertices.begin() : (vertices.end() - 1); 433 Vector2 referencePoint = {point->x, point->y}; 434 if (paintInfo.cap == SkPaint::kSquare_Cap) { 435 // To account for square cap, move the primary cap vertices (that create the AA edge) by the 436 // stroke offset vector (rotated to be parallel to the stroke) 437 Vector2 rotated = {-strokeOffset.y, strokeOffset.x}; 438 referencePoint += rotated; 439 } 440 441 AlphaVertex::set(&buffer[capIndex + 0], 442 referencePoint.x + outerOffset.x + capAAOffset.x, 443 referencePoint.y + outerOffset.y + capAAOffset.y, 444 0.0f); 445 AlphaVertex::set(&buffer[capIndex + 1], 446 referencePoint.x + innerOffset.x - capAAOffset.x, 447 referencePoint.y + innerOffset.y - capAAOffset.y, 448 paintInfo.maxAlpha); 449 450 bool isRound = paintInfo.cap == SkPaint::kRound_Cap; 451 452 const int postCapIndex = (isRound && isFirst) ? (2 * extraOffset - 2) : capIndex + (2 * extra); 453 AlphaVertex::set(&buffer[postCapIndex + 2], 454 referencePoint.x - outerOffset.x + capAAOffset.x, 455 referencePoint.y - outerOffset.y + capAAOffset.y, 456 0.0f); 457 AlphaVertex::set(&buffer[postCapIndex + 3], 458 referencePoint.x - innerOffset.x - capAAOffset.x, 459 referencePoint.y - innerOffset.y - capAAOffset.y, 460 paintInfo.maxAlpha); 461 462 if (isRound) { 463 const float dTheta = PI / (extra + 1); 464 const float radialScale = 2.0f / (1 + cos(dTheta)); 465 float theta = atan2(normal.y, normal.x); 466 int capPerimIndex = capIndex + 2; 467 468 for (int i = 0; i < extra; i++) { 469 theta += dTheta; 470 471 Vector2 radialOffset = {cos(theta), sin(theta)}; 472 473 // scale to compensate for pinching at sharp angles, see totalOffsetFromNormals() 474 radialOffset *= radialScale; 475 476 AAOffset = paintInfo.deriveAAOffset(radialOffset); 477 paintInfo.scaleOffsetForStrokeWidth(radialOffset); 478 AlphaVertex::set(&buffer[capPerimIndex++], 479 referencePoint.x + radialOffset.x + AAOffset.x, 480 referencePoint.y + radialOffset.y + AAOffset.y, 481 0.0f); 482 AlphaVertex::set(&buffer[capPerimIndex++], 483 referencePoint.x + radialOffset.x - AAOffset.x, 484 referencePoint.y + radialOffset.y - AAOffset.y, 485 paintInfo.maxAlpha); 486 487 if (isFirst && i == extra - extraOffset) { 488 //copy most recent two points to first two points 489 buffer[0] = buffer[capPerimIndex - 2]; 490 buffer[1] = buffer[capPerimIndex - 1]; 491 492 capPerimIndex = 2; // start writing the rest of the round cap at index 2 493 } 494 } 495 496 if (isFirst) { 497 const int startCapFillIndex = capIndex + 2 * (extra - extraOffset) + 4; 498 int capFillIndex = startCapFillIndex; 499 for (int i = 0; i < extra + 2; i += 2) { 500 buffer[capFillIndex++] = buffer[1 + i]; 501 // TODO: to support odd numbers of divisions, break here on the last iteration 502 buffer[capFillIndex++] = buffer[startCapFillIndex - 3 - i]; 503 } 504 } else { 505 int capFillIndex = 6 * vertices.size() + 2 + 6 * extra - (extra + 2); 506 for (int i = 0; i < extra + 2; i += 2) { 507 buffer[capFillIndex++] = buffer[capIndex + 1 + i]; 508 // TODO: to support odd numbers of divisions, break here on the last iteration 509 buffer[capFillIndex++] = buffer[capIndex + 3 + 2 * extra - i]; 510 } 511 } 512 return; 513 } 514 if (isFirst) { 515 buffer[0] = buffer[postCapIndex + 2]; 516 buffer[1] = buffer[postCapIndex + 3]; 517 buffer[postCapIndex + 4] = buffer[1]; // degenerate tris (the only two!) 518 buffer[postCapIndex + 5] = buffer[postCapIndex + 1]; 519 } else { 520 buffer[6 * vertices.size()] = buffer[postCapIndex + 1]; 521 buffer[6 * vertices.size() + 1] = buffer[postCapIndex + 3]; 522 } 523 } 524 525 /* 526 the geometry for an aa, capped stroke consists of the following: 527 528 # vertices | function 529 ---------------------------------------------------------------------- 530 a) 2 | Start AA perimeter 531 b) 2, 2 * roundDivOff | First half of begin cap's perimeter 532 | 533 2 * middlePts | 'Outer' or 'Top' AA perimeter half (between caps) 534 | 535 a) 4 | End cap's 536 b) 2, 2 * roundDivs, 2 | AA perimeter 537 | 538 2 * middlePts | 'Inner' or 'bottom' AA perimeter half 539 | 540 a) 6 | Begin cap's perimeter 541 b) 2, 2*(rD - rDO + 1), | Last half of begin cap's perimeter 542 roundDivs, 2 | 543 | 544 2 * middlePts | Stroke's full opacity center strip 545 | 546 a) 2 | end stroke 547 b) 2, roundDivs | (and end cap fill, for round) 548 549 Notes: 550 * rows starting with 'a)' denote the Butt or Square cap vertex use, 'b)' denote Round 551 552 * 'middlePts' is (number of points in the unclosed input vertex list, minus 2) times two 553 554 * 'roundDivs' or 'rD' is the number of extra vertices (beyond the minimum of 2) that define the 555 round cap's shape, and is at least two. This will increase with cap size to sufficiently 556 define the cap's level of tessellation. 557 558 * 'roundDivOffset' or 'rDO' is the point about halfway along the start cap's round perimeter, where 559 the stream of vertices for the AA perimeter starts. By starting and ending the perimeter at 560 this offset, the fill of the stroke is drawn from this point with minimal extra vertices. 561 562 This means the outer perimeter starts at: 563 outerIndex = (2) OR (2 + 2 * roundDivOff) 564 the inner perimeter (since it is filled in reverse) starts at: 565 innerIndex = outerIndex + (4 * middlePts) + ((4) OR (4 + 2 * roundDivs)) - 1 566 the stroke starts at: 567 strokeIndex = innerIndex + 1 + ((6) OR (6 + 3 * roundDivs - 2 * roundDivOffset)) 568 569 The total needed allocated space is either: 570 2 + 4 + 6 + 2 + 3 * (2 * middlePts) = 14 + 6 * middlePts = 2 + 6 * pts 571 or, for rounded caps: 572 (2 + 2 * rDO) + (4 + 2 * rD) + (2 * (rD - rDO + 1) 573 + roundDivs + 4) + (2 + roundDivs) + 3 * (2 * middlePts) 574 = 14 + 6 * middlePts + 6 * roundDivs 575 = 2 + 6 * pts + 6 * roundDivs 576 */ 577 void getStrokeVerticesFromUnclosedVerticesAA(const PaintInfo& paintInfo, 578 const Vector<Vertex>& vertices, VertexBuffer& vertexBuffer) { 579 580 const int extra = paintInfo.capExtraDivisions(); 581 const int allocSize = 6 * vertices.size() + 2 + 6 * extra; 582 583 AlphaVertex* buffer = vertexBuffer.alloc<AlphaVertex>(allocSize); 584 585 const int extraOffset = (extra + 1) / 2; 586 int offset = 2 * (vertices.size() - 2); 587 // there is no outer/inner here, using them for consistency with below approach 588 int currentAAOuterIndex = 2 + 2 * extraOffset; 589 int currentAAInnerIndex = currentAAOuterIndex + (2 * offset) + 3 + (2 * extra); 590 int currentStrokeIndex = currentAAInnerIndex + 7 + (3 * extra - 2 * extraOffset); 591 592 const Vertex* last = &(vertices[0]); 593 const Vertex* current = &(vertices[1]); 594 Vector2 lastNormal = {current->y - last->y, last->x - current->x}; 595 lastNormal.normalize(); 596 597 // TODO: use normal from bezier traversal for cap, instead of from vertices 598 storeCapAA(paintInfo, vertices, buffer, true, lastNormal, offset); 599 600 for (unsigned int i = 1; i < vertices.size() - 1; i++) { 601 const Vertex* next = &(vertices[i + 1]); 602 Vector2 nextNormal = {next->y - current->y, current->x - next->x}; 603 nextNormal.normalize(); 604 605 Vector2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal); 606 Vector2 AAOffset = paintInfo.deriveAAOffset(totalOffset); 607 608 Vector2 innerOffset = totalOffset; 609 paintInfo.scaleOffsetForStrokeWidth(innerOffset); 610 Vector2 outerOffset = innerOffset + AAOffset; 611 innerOffset -= AAOffset; 612 613 AlphaVertex::set(&buffer[currentAAOuterIndex++], 614 current->x + outerOffset.x, 615 current->y + outerOffset.y, 616 0.0f); 617 AlphaVertex::set(&buffer[currentAAOuterIndex++], 618 current->x + innerOffset.x, 619 current->y + innerOffset.y, 620 paintInfo.maxAlpha); 621 622 AlphaVertex::set(&buffer[currentStrokeIndex++], 623 current->x + innerOffset.x, 624 current->y + innerOffset.y, 625 paintInfo.maxAlpha); 626 AlphaVertex::set(&buffer[currentStrokeIndex++], 627 current->x - innerOffset.x, 628 current->y - innerOffset.y, 629 paintInfo.maxAlpha); 630 631 AlphaVertex::set(&buffer[currentAAInnerIndex--], 632 current->x - innerOffset.x, 633 current->y - innerOffset.y, 634 paintInfo.maxAlpha); 635 AlphaVertex::set(&buffer[currentAAInnerIndex--], 636 current->x - outerOffset.x, 637 current->y - outerOffset.y, 638 0.0f); 639 640 current = next; 641 lastNormal = nextNormal; 642 } 643 644 // TODO: use normal from bezier traversal for cap, instead of from vertices 645 storeCapAA(paintInfo, vertices, buffer, false, lastNormal, offset); 646 647 DEBUG_DUMP_ALPHA_BUFFER(); 648 } 649 650 651 void getStrokeVerticesFromPerimeterAA(const PaintInfo& paintInfo, const Vector<Vertex>& perimeter, 652 VertexBuffer& vertexBuffer) { 653 AlphaVertex* buffer = vertexBuffer.alloc<AlphaVertex>(6 * perimeter.size() + 8); 654 655 int offset = 2 * perimeter.size() + 3; 656 int currentAAOuterIndex = 0; 657 int currentStrokeIndex = offset; 658 int currentAAInnerIndex = offset * 2; 659 660 const Vertex* last = &(perimeter[perimeter.size() - 1]); 661 const Vertex* current = &(perimeter[0]); 662 Vector2 lastNormal = {current->y - last->y, last->x - current->x}; 663 lastNormal.normalize(); 664 for (unsigned int i = 0; i < perimeter.size(); i++) { 665 const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]); 666 Vector2 nextNormal = {next->y - current->y, current->x - next->x}; 667 nextNormal.normalize(); 668 669 Vector2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal); 670 Vector2 AAOffset = paintInfo.deriveAAOffset(totalOffset); 671 672 Vector2 innerOffset = totalOffset; 673 paintInfo.scaleOffsetForStrokeWidth(innerOffset); 674 Vector2 outerOffset = innerOffset + AAOffset; 675 innerOffset -= AAOffset; 676 677 AlphaVertex::set(&buffer[currentAAOuterIndex++], 678 current->x + outerOffset.x, 679 current->y + outerOffset.y, 680 0.0f); 681 AlphaVertex::set(&buffer[currentAAOuterIndex++], 682 current->x + innerOffset.x, 683 current->y + innerOffset.y, 684 paintInfo.maxAlpha); 685 686 AlphaVertex::set(&buffer[currentStrokeIndex++], 687 current->x + innerOffset.x, 688 current->y + innerOffset.y, 689 paintInfo.maxAlpha); 690 AlphaVertex::set(&buffer[currentStrokeIndex++], 691 current->x - innerOffset.x, 692 current->y - innerOffset.y, 693 paintInfo.maxAlpha); 694 695 AlphaVertex::set(&buffer[currentAAInnerIndex++], 696 current->x - innerOffset.x, 697 current->y - innerOffset.y, 698 paintInfo.maxAlpha); 699 AlphaVertex::set(&buffer[currentAAInnerIndex++], 700 current->x - outerOffset.x, 701 current->y - outerOffset.y, 702 0.0f); 703 704 last = current; 705 current = next; 706 lastNormal = nextNormal; 707 } 708 709 // wrap each strip around to beginning, creating degenerate tris to bridge strips 710 buffer[currentAAOuterIndex++] = buffer[0]; 711 buffer[currentAAOuterIndex++] = buffer[1]; 712 buffer[currentAAOuterIndex++] = buffer[1]; 713 714 buffer[currentStrokeIndex++] = buffer[offset]; 715 buffer[currentStrokeIndex++] = buffer[offset + 1]; 716 buffer[currentStrokeIndex++] = buffer[offset + 1]; 717 718 buffer[currentAAInnerIndex++] = buffer[2 * offset]; 719 buffer[currentAAInnerIndex++] = buffer[2 * offset + 1]; 720 // don't need to create last degenerate tri 721 722 DEBUG_DUMP_ALPHA_BUFFER(); 723 } 724 725 void PathTessellator::tessellatePath(const SkPath &path, const SkPaint* paint, 726 const mat4& transform, VertexBuffer& vertexBuffer) { 727 ATRACE_CALL(); 728 729 const PaintInfo paintInfo(paint, transform); 730 731 Vector<Vertex> tempVertices; 732 float threshInvScaleX = paintInfo.inverseScaleX; 733 float threshInvScaleY = paintInfo.inverseScaleY; 734 if (paintInfo.style == SkPaint::kStroke_Style) { 735 // alter the bezier recursion threshold values we calculate in order to compensate for 736 // expansion done after the path vertices are found 737 SkRect bounds = path.getBounds(); 738 if (!bounds.isEmpty()) { 739 threshInvScaleX *= bounds.width() / (bounds.width() + paint->getStrokeWidth()); 740 threshInvScaleY *= bounds.height() / (bounds.height() + paint->getStrokeWidth()); 741 } 742 } 743 744 // force close if we're filling the path, since fill path expects closed perimeter. 745 bool forceClose = paintInfo.style != SkPaint::kStroke_Style; 746 bool wasClosed = approximatePathOutlineVertices(path, forceClose, 747 threshInvScaleX * threshInvScaleX, threshInvScaleY * threshInvScaleY, 748 OUTLINE_REFINE_THRESHOLD_SQUARED, tempVertices); 749 750 if (!tempVertices.size()) { 751 // path was empty, return without allocating vertex buffer 752 return; 753 } 754 755 #if VERTEX_DEBUG 756 for (unsigned int i = 0; i < tempVertices.size(); i++) { 757 ALOGD("orig path: point at %f %f", 758 tempVertices[i].x, tempVertices[i].y); 759 } 760 #endif 761 762 if (paintInfo.style == SkPaint::kStroke_Style) { 763 if (!paintInfo.isAA) { 764 if (wasClosed) { 765 getStrokeVerticesFromPerimeter(paintInfo, tempVertices, vertexBuffer); 766 } else { 767 getStrokeVerticesFromUnclosedVertices(paintInfo, tempVertices, vertexBuffer); 768 } 769 770 } else { 771 if (wasClosed) { 772 getStrokeVerticesFromPerimeterAA(paintInfo, tempVertices, vertexBuffer); 773 } else { 774 getStrokeVerticesFromUnclosedVerticesAA(paintInfo, tempVertices, vertexBuffer); 775 } 776 } 777 } else { 778 // For kStrokeAndFill style, the path should be adjusted externally. 779 // It will be treated as a fill here. 780 if (!paintInfo.isAA) { 781 getFillVerticesFromPerimeter(tempVertices, vertexBuffer); 782 } else { 783 getFillVerticesFromPerimeterAA(paintInfo, tempVertices, vertexBuffer); 784 } 785 } 786 787 Rect bounds(path.getBounds()); 788 paintInfo.expandBoundsForStroke(&bounds); 789 vertexBuffer.setBounds(bounds); 790 } 791 792 template <class TYPE> 793 static void instanceVertices(VertexBuffer& srcBuffer, VertexBuffer& dstBuffer, 794 const float* points, int count, Rect& bounds) { 795 bounds.set(points[0], points[1], points[0], points[1]); 796 797 int numPoints = count / 2; 798 int verticesPerPoint = srcBuffer.getVertexCount(); 799 dstBuffer.alloc<TYPE>(numPoints * verticesPerPoint + (numPoints - 1) * 2); 800 801 for (int i = 0; i < count; i += 2) { 802 bounds.expandToCoverVertex(points[i + 0], points[i + 1]); 803 dstBuffer.copyInto<TYPE>(srcBuffer, points[i + 0], points[i + 1]); 804 } 805 dstBuffer.createDegenerateSeparators<TYPE>(verticesPerPoint); 806 } 807 808 void PathTessellator::tessellatePoints(const float* points, int count, const SkPaint* paint, 809 const mat4& transform, VertexBuffer& vertexBuffer) { 810 const PaintInfo paintInfo(paint, transform); 811 812 // determine point shape 813 SkPath path; 814 float radius = paintInfo.halfStrokeWidth; 815 if (radius == 0.0f) radius = 0.5f; 816 817 if (paintInfo.cap == SkPaint::kRound_Cap) { 818 path.addCircle(0, 0, radius); 819 } else { 820 path.addRect(-radius, -radius, radius, radius); 821 } 822 823 // calculate outline 824 Vector<Vertex> outlineVertices; 825 approximatePathOutlineVertices(path, true, 826 paintInfo.inverseScaleX * paintInfo.inverseScaleX, 827 paintInfo.inverseScaleY * paintInfo.inverseScaleY, 828 OUTLINE_REFINE_THRESHOLD_SQUARED, outlineVertices); 829 830 if (!outlineVertices.size()) return; 831 832 Rect bounds; 833 // tessellate, then duplicate outline across points 834 int numPoints = count / 2; 835 VertexBuffer tempBuffer; 836 if (!paintInfo.isAA) { 837 getFillVerticesFromPerimeter(outlineVertices, tempBuffer); 838 instanceVertices<Vertex>(tempBuffer, vertexBuffer, points, count, bounds); 839 } else { 840 // note: pass maxAlpha directly, since we want fill to be alpha modulated 841 getFillVerticesFromPerimeterAA(paintInfo, outlineVertices, tempBuffer, paintInfo.maxAlpha); 842 instanceVertices<AlphaVertex>(tempBuffer, vertexBuffer, points, count, bounds); 843 } 844 845 // expand bounds from vertex coords to pixel data 846 paintInfo.expandBoundsForStroke(&bounds); 847 vertexBuffer.setBounds(bounds); 848 } 849 850 void PathTessellator::tessellateLines(const float* points, int count, const SkPaint* paint, 851 const mat4& transform, VertexBuffer& vertexBuffer) { 852 ATRACE_CALL(); 853 const PaintInfo paintInfo(paint, transform); 854 855 const int extra = paintInfo.capExtraDivisions(); 856 int numLines = count / 4; 857 int lineAllocSize; 858 // pre-allocate space for lines in the buffer, and degenerate tris in between 859 if (paintInfo.isAA) { 860 lineAllocSize = 6 * (2) + 2 + 6 * extra; 861 vertexBuffer.alloc<AlphaVertex>(numLines * lineAllocSize + (numLines - 1) * 2); 862 } else { 863 lineAllocSize = 2 * ((2) + extra); 864 vertexBuffer.alloc<Vertex>(numLines * lineAllocSize + (numLines - 1) * 2); 865 } 866 867 Vector<Vertex> tempVertices; 868 tempVertices.push(); 869 tempVertices.push(); 870 Vertex* tempVerticesData = tempVertices.editArray(); 871 Rect bounds; 872 bounds.set(points[0], points[1], points[0], points[1]); 873 for (int i = 0; i < count; i += 4) { 874 Vertex::set(&(tempVerticesData[0]), points[i + 0], points[i + 1]); 875 Vertex::set(&(tempVerticesData[1]), points[i + 2], points[i + 3]); 876 877 if (paintInfo.isAA) { 878 getStrokeVerticesFromUnclosedVerticesAA(paintInfo, tempVertices, vertexBuffer); 879 } else { 880 getStrokeVerticesFromUnclosedVertices(paintInfo, tempVertices, vertexBuffer); 881 } 882 883 // calculate bounds 884 bounds.expandToCoverVertex(tempVerticesData[0].x, tempVerticesData[0].y); 885 bounds.expandToCoverVertex(tempVerticesData[1].x, tempVerticesData[1].y); 886 } 887 888 // since multiple objects tessellated into buffer, separate them with degen tris 889 if (paintInfo.isAA) { 890 vertexBuffer.createDegenerateSeparators<AlphaVertex>(lineAllocSize); 891 } else { 892 vertexBuffer.createDegenerateSeparators<Vertex>(lineAllocSize); 893 } 894 895 // expand bounds from vertex coords to pixel data 896 paintInfo.expandBoundsForStroke(&bounds); 897 vertexBuffer.setBounds(bounds); 898 } 899 900 /////////////////////////////////////////////////////////////////////////////// 901 // Simple path line approximation 902 /////////////////////////////////////////////////////////////////////////////// 903 904 bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, float thresholdSquared, 905 Vector<Vertex>& outputVertices) { 906 return approximatePathOutlineVertices(path, true, 1.0f, 1.0f, thresholdSquared, outputVertices); 907 } 908 909 void pushToVector(Vector<Vertex>& vertices, float x, float y) { 910 // TODO: make this not yuck 911 vertices.push(); 912 Vertex* newVertex = &(vertices.editArray()[vertices.size() - 1]); 913 Vertex::set(newVertex, x, y); 914 } 915 916 bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool forceClose, 917 float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared, 918 Vector<Vertex>& outputVertices) { 919 ATRACE_CALL(); 920 921 // TODO: to support joins other than sharp miter, join vertices should be labelled in the 922 // perimeter, or resolved into more vertices. Reconsider forceClose-ing in that case. 923 SkPath::Iter iter(path, forceClose); 924 SkPoint pts[4]; 925 SkPath::Verb v; 926 while (SkPath::kDone_Verb != (v = iter.next(pts))) { 927 switch (v) { 928 case SkPath::kMove_Verb: 929 pushToVector(outputVertices, pts[0].x(), pts[0].y()); 930 ALOGV("Move to pos %f %f", pts[0].x(), pts[0].y()); 931 break; 932 case SkPath::kClose_Verb: 933 ALOGV("Close at pos %f %f", pts[0].x(), pts[0].y()); 934 break; 935 case SkPath::kLine_Verb: 936 ALOGV("kLine_Verb %f %f -> %f %f", pts[0].x(), pts[0].y(), pts[1].x(), pts[1].y()); 937 pushToVector(outputVertices, pts[1].x(), pts[1].y()); 938 break; 939 case SkPath::kQuad_Verb: 940 ALOGV("kQuad_Verb"); 941 recursiveQuadraticBezierVertices( 942 pts[0].x(), pts[0].y(), 943 pts[2].x(), pts[2].y(), 944 pts[1].x(), pts[1].y(), 945 sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices); 946 break; 947 case SkPath::kCubic_Verb: 948 ALOGV("kCubic_Verb"); 949 recursiveCubicBezierVertices( 950 pts[0].x(), pts[0].y(), 951 pts[1].x(), pts[1].y(), 952 pts[3].x(), pts[3].y(), 953 pts[2].x(), pts[2].y(), 954 sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices); 955 break; 956 default: 957 break; 958 } 959 } 960 961 int size = outputVertices.size(); 962 if (size >= 2 && outputVertices[0].x == outputVertices[size - 1].x && 963 outputVertices[0].y == outputVertices[size - 1].y) { 964 outputVertices.pop(); 965 return true; 966 } 967 return false; 968 } 969 970 /////////////////////////////////////////////////////////////////////////////// 971 // Bezier approximation 972 /////////////////////////////////////////////////////////////////////////////// 973 974 void PathTessellator::recursiveCubicBezierVertices( 975 float p1x, float p1y, float c1x, float c1y, 976 float p2x, float p2y, float c2x, float c2y, 977 float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared, 978 Vector<Vertex>& outputVertices, int depth) { 979 float dx = p2x - p1x; 980 float dy = p2y - p1y; 981 float d1 = fabs((c1x - p2x) * dy - (c1y - p2y) * dx); 982 float d2 = fabs((c2x - p2x) * dy - (c2y - p2y) * dx); 983 float d = d1 + d2; 984 985 // multiplying by sqrInvScaleY/X equivalent to multiplying in dimensional scale factors 986 if (depth >= MAX_DEPTH 987 || d * d <= thresholdSquared * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) { 988 // below thresh, draw line by adding endpoint 989 pushToVector(outputVertices, p2x, p2y); 990 } else { 991 float p1c1x = (p1x + c1x) * 0.5f; 992 float p1c1y = (p1y + c1y) * 0.5f; 993 float p2c2x = (p2x + c2x) * 0.5f; 994 float p2c2y = (p2y + c2y) * 0.5f; 995 996 float c1c2x = (c1x + c2x) * 0.5f; 997 float c1c2y = (c1y + c2y) * 0.5f; 998 999 float p1c1c2x = (p1c1x + c1c2x) * 0.5f; 1000 float p1c1c2y = (p1c1y + c1c2y) * 0.5f; 1001 1002 float p2c1c2x = (p2c2x + c1c2x) * 0.5f; 1003 float p2c1c2y = (p2c2y + c1c2y) * 0.5f; 1004 1005 float mx = (p1c1c2x + p2c1c2x) * 0.5f; 1006 float my = (p1c1c2y + p2c1c2y) * 0.5f; 1007 1008 recursiveCubicBezierVertices( 1009 p1x, p1y, p1c1x, p1c1y, 1010 mx, my, p1c1c2x, p1c1c2y, 1011 sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices, depth + 1); 1012 recursiveCubicBezierVertices( 1013 mx, my, p2c1c2x, p2c1c2y, 1014 p2x, p2y, p2c2x, p2c2y, 1015 sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices, depth + 1); 1016 } 1017 } 1018 1019 void PathTessellator::recursiveQuadraticBezierVertices( 1020 float ax, float ay, 1021 float bx, float by, 1022 float cx, float cy, 1023 float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared, 1024 Vector<Vertex>& outputVertices, int depth) { 1025 float dx = bx - ax; 1026 float dy = by - ay; 1027 float d = (cx - bx) * dy - (cy - by) * dx; 1028 1029 // multiplying by sqrInvScaleY/X equivalent to multiplying in dimensional scale factors 1030 if (depth >= MAX_DEPTH 1031 || d * d <= thresholdSquared * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) { 1032 // below thresh, draw line by adding endpoint 1033 pushToVector(outputVertices, bx, by); 1034 } else { 1035 float acx = (ax + cx) * 0.5f; 1036 float bcx = (bx + cx) * 0.5f; 1037 float acy = (ay + cy) * 0.5f; 1038 float bcy = (by + cy) * 0.5f; 1039 1040 // midpoint 1041 float mx = (acx + bcx) * 0.5f; 1042 float my = (acy + bcy) * 0.5f; 1043 1044 recursiveQuadraticBezierVertices(ax, ay, mx, my, acx, acy, 1045 sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices, depth + 1); 1046 recursiveQuadraticBezierVertices(mx, my, bx, by, bcx, bcy, 1047 sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices, depth + 1); 1048 } 1049 } 1050 1051 }; // namespace uirenderer 1052 }; // namespace android 1053