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