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 "PathRenderer" 18 #define LOG_NDEBUG 1 19 #define ATRACE_TAG ATRACE_TAG_GRAPHICS 20 21 #define VERTEX_DEBUG 0 22 23 #include <SkPath.h> 24 #include <SkPaint.h> 25 26 #include <stdlib.h> 27 #include <stdint.h> 28 #include <sys/types.h> 29 30 #include <utils/Log.h> 31 #include <utils/Trace.h> 32 33 #include "PathRenderer.h" 34 #include "Matrix.h" 35 #include "Vector.h" 36 #include "Vertex.h" 37 38 namespace android { 39 namespace uirenderer { 40 41 #define THRESHOLD 0.5f 42 43 SkRect PathRenderer::computePathBounds(const SkPath& path, const SkPaint* paint) { 44 SkRect bounds = path.getBounds(); 45 if (paint->getStyle() != SkPaint::kFill_Style) { 46 float outset = paint->getStrokeWidth() * 0.5f; 47 bounds.outset(outset, outset); 48 } 49 return bounds; 50 } 51 52 void computeInverseScales(const mat4 *transform, float &inverseScaleX, float& inverseScaleY) { 53 if (CC_UNLIKELY(!transform->isPureTranslate())) { 54 float m00 = transform->data[Matrix4::kScaleX]; 55 float m01 = transform->data[Matrix4::kSkewY]; 56 float m10 = transform->data[Matrix4::kSkewX]; 57 float m11 = transform->data[Matrix4::kScaleY]; 58 float scaleX = sqrt(m00 * m00 + m01 * m01); 59 float scaleY = sqrt(m10 * m10 + m11 * m11); 60 inverseScaleX = (scaleX != 0) ? (1.0f / scaleX) : 1.0f; 61 inverseScaleY = (scaleY != 0) ? (1.0f / scaleY) : 1.0f; 62 } else { 63 inverseScaleX = 1.0f; 64 inverseScaleY = 1.0f; 65 } 66 } 67 68 inline void copyVertex(Vertex* destPtr, const Vertex* srcPtr) { 69 Vertex::set(destPtr, srcPtr->position[0], srcPtr->position[1]); 70 } 71 72 inline void copyAlphaVertex(AlphaVertex* destPtr, const AlphaVertex* srcPtr) { 73 AlphaVertex::set(destPtr, srcPtr->position[0], srcPtr->position[1], srcPtr->alpha); 74 } 75 76 /** 77 * Produces a pseudo-normal for a vertex, given the normals of the two incoming lines. If the offset 78 * from each vertex in a perimeter is calculated, the resultant lines connecting the offset vertices 79 * will be offset by 1.0 80 * 81 * Note that we can't add and normalize the two vectors, that would result in a rectangle having an 82 * offset of (sqrt(2)/2, sqrt(2)/2) at each corner, instead of (1, 1) 83 * 84 * NOTE: assumes angles between normals 90 degrees or less 85 */ 86 inline vec2 totalOffsetFromNormals(const vec2& normalA, const vec2& normalB) { 87 return (normalA + normalB) / (1 + fabs(normalA.dot(normalB))); 88 } 89 90 inline void scaleOffsetForStrokeWidth(vec2& offset, float halfStrokeWidth, 91 float inverseScaleX, float inverseScaleY) { 92 if (halfStrokeWidth == 0.0f) { 93 // hairline - compensate for scale 94 offset.x *= 0.5f * inverseScaleX; 95 offset.y *= 0.5f * inverseScaleY; 96 } else { 97 offset *= halfStrokeWidth; 98 } 99 } 100 101 void getFillVerticesFromPerimeter(const Vector<Vertex>& perimeter, VertexBuffer& vertexBuffer) { 102 Vertex* buffer = vertexBuffer.alloc<Vertex>(perimeter.size()); 103 104 int currentIndex = 0; 105 // zig zag between all previous points on the inside of the hull to create a 106 // triangle strip that fills the hull 107 int srcAindex = 0; 108 int srcBindex = perimeter.size() - 1; 109 while (srcAindex <= srcBindex) { 110 copyVertex(&buffer[currentIndex++], &perimeter[srcAindex]); 111 if (srcAindex == srcBindex) break; 112 copyVertex(&buffer[currentIndex++], &perimeter[srcBindex]); 113 srcAindex++; 114 srcBindex--; 115 } 116 } 117 118 void getStrokeVerticesFromPerimeter(const Vector<Vertex>& perimeter, float halfStrokeWidth, 119 VertexBuffer& vertexBuffer, float inverseScaleX, float inverseScaleY) { 120 Vertex* buffer = vertexBuffer.alloc<Vertex>(perimeter.size() * 2 + 2); 121 122 int currentIndex = 0; 123 const Vertex* last = &(perimeter[perimeter.size() - 1]); 124 const Vertex* current = &(perimeter[0]); 125 vec2 lastNormal(current->position[1] - last->position[1], 126 last->position[0] - current->position[0]); 127 lastNormal.normalize(); 128 for (unsigned int i = 0; i < perimeter.size(); i++) { 129 const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]); 130 vec2 nextNormal(next->position[1] - current->position[1], 131 current->position[0] - next->position[0]); 132 nextNormal.normalize(); 133 134 vec2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal); 135 scaleOffsetForStrokeWidth(totalOffset, halfStrokeWidth, inverseScaleX, inverseScaleY); 136 137 Vertex::set(&buffer[currentIndex++], 138 current->position[0] + totalOffset.x, 139 current->position[1] + totalOffset.y); 140 141 Vertex::set(&buffer[currentIndex++], 142 current->position[0] - totalOffset.x, 143 current->position[1] - totalOffset.y); 144 145 last = current; 146 current = next; 147 lastNormal = nextNormal; 148 } 149 150 // wrap around to beginning 151 copyVertex(&buffer[currentIndex++], &buffer[0]); 152 copyVertex(&buffer[currentIndex++], &buffer[1]); 153 } 154 155 void getStrokeVerticesFromUnclosedVertices(const Vector<Vertex>& vertices, float halfStrokeWidth, 156 VertexBuffer& vertexBuffer, float inverseScaleX, float inverseScaleY) { 157 Vertex* buffer = vertexBuffer.alloc<Vertex>(vertices.size() * 2); 158 159 int currentIndex = 0; 160 const Vertex* current = &(vertices[0]); 161 vec2 lastNormal; 162 for (unsigned int i = 0; i < vertices.size() - 1; i++) { 163 const Vertex* next = &(vertices[i + 1]); 164 vec2 nextNormal(next->position[1] - current->position[1], 165 current->position[0] - next->position[0]); 166 nextNormal.normalize(); 167 168 vec2 totalOffset; 169 if (i == 0) { 170 totalOffset = nextNormal; 171 } else { 172 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal); 173 } 174 scaleOffsetForStrokeWidth(totalOffset, halfStrokeWidth, inverseScaleX, inverseScaleY); 175 176 Vertex::set(&buffer[currentIndex++], 177 current->position[0] + totalOffset.x, 178 current->position[1] + totalOffset.y); 179 180 Vertex::set(&buffer[currentIndex++], 181 current->position[0] - totalOffset.x, 182 current->position[1] - totalOffset.y); 183 184 current = next; 185 lastNormal = nextNormal; 186 } 187 188 vec2 totalOffset = lastNormal; 189 scaleOffsetForStrokeWidth(totalOffset, halfStrokeWidth, inverseScaleX, inverseScaleY); 190 191 Vertex::set(&buffer[currentIndex++], 192 current->position[0] + totalOffset.x, 193 current->position[1] + totalOffset.y); 194 Vertex::set(&buffer[currentIndex++], 195 current->position[0] - totalOffset.x, 196 current->position[1] - totalOffset.y); 197 #if VERTEX_DEBUG 198 for (unsigned int i = 0; i < vertexBuffer.getSize(); i++) { 199 ALOGD("point at %f %f", buffer[i].position[0], buffer[i].position[1]); 200 } 201 #endif 202 } 203 204 void getFillVerticesFromPerimeterAA(const Vector<Vertex>& perimeter, VertexBuffer& vertexBuffer, 205 float inverseScaleX, float inverseScaleY) { 206 AlphaVertex* buffer = vertexBuffer.alloc<AlphaVertex>(perimeter.size() * 3 + 2); 207 208 // generate alpha points - fill Alpha vertex gaps in between each point with 209 // alpha 0 vertex, offset by a scaled normal. 210 int currentIndex = 0; 211 const Vertex* last = &(perimeter[perimeter.size() - 1]); 212 const Vertex* current = &(perimeter[0]); 213 vec2 lastNormal(current->position[1] - last->position[1], 214 last->position[0] - current->position[0]); 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 vec2 nextNormal(next->position[1] - current->position[1], 219 current->position[0] - next->position[0]); 220 nextNormal.normalize(); 221 222 // AA point offset from original point is that point's normal, such that each side is offset 223 // by .5 pixels 224 vec2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal); 225 totalOffset.x *= 0.5f * inverseScaleX; 226 totalOffset.y *= 0.5f * inverseScaleY; 227 228 AlphaVertex::set(&buffer[currentIndex++], 229 current->position[0] + totalOffset.x, 230 current->position[1] + totalOffset.y, 231 0.0f); 232 AlphaVertex::set(&buffer[currentIndex++], 233 current->position[0] - totalOffset.x, 234 current->position[1] - totalOffset.y, 235 1.0f); 236 237 last = current; 238 current = next; 239 lastNormal = nextNormal; 240 } 241 242 // wrap around to beginning 243 copyAlphaVertex(&buffer[currentIndex++], &buffer[0]); 244 copyAlphaVertex(&buffer[currentIndex++], &buffer[1]); 245 246 // zig zag between all previous points on the inside of the hull to create a 247 // triangle strip that fills the hull, repeating the first inner point to 248 // create degenerate tris to start inside path 249 int srcAindex = 0; 250 int srcBindex = perimeter.size() - 1; 251 while (srcAindex <= srcBindex) { 252 copyAlphaVertex(&buffer[currentIndex++], &buffer[srcAindex * 2 + 1]); 253 if (srcAindex == srcBindex) break; 254 copyAlphaVertex(&buffer[currentIndex++], &buffer[srcBindex * 2 + 1]); 255 srcAindex++; 256 srcBindex--; 257 } 258 259 #if VERTEX_DEBUG 260 for (unsigned int i = 0; i < vertexBuffer.getSize(); i++) { 261 ALOGD("point at %f %f, alpha %f", buffer[i].position[0], buffer[i].position[1], buffer[i].alpha); 262 } 263 #endif 264 } 265 266 267 void getStrokeVerticesFromUnclosedVerticesAA(const Vector<Vertex>& vertices, float halfStrokeWidth, 268 VertexBuffer& vertexBuffer, float inverseScaleX, float inverseScaleY) { 269 AlphaVertex* buffer = vertexBuffer.alloc<AlphaVertex>(6 * vertices.size() + 2); 270 271 // avoid lines smaller than hairline since they break triangle based sampling. instead reducing 272 // alpha value (TODO: support different X/Y scale) 273 float maxAlpha = 1.0f; 274 if (halfStrokeWidth != 0 && inverseScaleX == inverseScaleY && 275 halfStrokeWidth * inverseScaleX < 0.5f) { 276 maxAlpha *= (2 * halfStrokeWidth) / inverseScaleX; 277 halfStrokeWidth = 0.0f; 278 } 279 280 // there is no outer/inner here, using them for consistency with below approach 281 int offset = 2 * (vertices.size() - 2); 282 int currentAAOuterIndex = 2; 283 int currentAAInnerIndex = 2 * offset + 5; // reversed 284 int currentStrokeIndex = currentAAInnerIndex + 7; 285 286 const Vertex* last = &(vertices[0]); 287 const Vertex* current = &(vertices[1]); 288 vec2 lastNormal(current->position[1] - last->position[1], 289 last->position[0] - current->position[0]); 290 lastNormal.normalize(); 291 292 { 293 // start cap 294 vec2 totalOffset = lastNormal; 295 vec2 AAOffset = totalOffset; 296 AAOffset.x *= 0.5f * inverseScaleX; 297 AAOffset.y *= 0.5f * inverseScaleY; 298 299 vec2 innerOffset = totalOffset; 300 scaleOffsetForStrokeWidth(innerOffset, halfStrokeWidth, inverseScaleX, inverseScaleY); 301 vec2 outerOffset = innerOffset + AAOffset; 302 innerOffset -= AAOffset; 303 304 // TODO: support square cap by changing this offset to incorporate halfStrokeWidth 305 vec2 capAAOffset(AAOffset.y, -AAOffset.x); 306 AlphaVertex::set(&buffer[0], 307 last->position[0] + outerOffset.x + capAAOffset.x, 308 last->position[1] + outerOffset.y + capAAOffset.y, 309 0.0f); 310 AlphaVertex::set(&buffer[1], 311 last->position[0] + innerOffset.x - capAAOffset.x, 312 last->position[1] + innerOffset.y - capAAOffset.y, 313 maxAlpha); 314 315 AlphaVertex::set(&buffer[2 * offset + 6], 316 last->position[0] - outerOffset.x + capAAOffset.x, 317 last->position[1] - outerOffset.y + capAAOffset.y, 318 0.0f); 319 AlphaVertex::set(&buffer[2 * offset + 7], 320 last->position[0] - innerOffset.x - capAAOffset.x, 321 last->position[1] - innerOffset.y - capAAOffset.y, 322 maxAlpha); 323 copyAlphaVertex(&buffer[2 * offset + 8], &buffer[0]); 324 copyAlphaVertex(&buffer[2 * offset + 9], &buffer[1]); 325 copyAlphaVertex(&buffer[2 * offset + 10], &buffer[1]); // degenerate tris (the only two!) 326 copyAlphaVertex(&buffer[2 * offset + 11], &buffer[2 * offset + 7]); 327 } 328 329 for (unsigned int i = 1; i < vertices.size() - 1; i++) { 330 const Vertex* next = &(vertices[i + 1]); 331 vec2 nextNormal(next->position[1] - current->position[1], 332 current->position[0] - next->position[0]); 333 nextNormal.normalize(); 334 335 vec2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal); 336 vec2 AAOffset = totalOffset; 337 AAOffset.x *= 0.5f * inverseScaleX; 338 AAOffset.y *= 0.5f * inverseScaleY; 339 340 vec2 innerOffset = totalOffset; 341 scaleOffsetForStrokeWidth(innerOffset, halfStrokeWidth, inverseScaleX, inverseScaleY); 342 vec2 outerOffset = innerOffset + AAOffset; 343 innerOffset -= AAOffset; 344 345 AlphaVertex::set(&buffer[currentAAOuterIndex++], 346 current->position[0] + outerOffset.x, 347 current->position[1] + outerOffset.y, 348 0.0f); 349 AlphaVertex::set(&buffer[currentAAOuterIndex++], 350 current->position[0] + innerOffset.x, 351 current->position[1] + innerOffset.y, 352 maxAlpha); 353 354 AlphaVertex::set(&buffer[currentStrokeIndex++], 355 current->position[0] + innerOffset.x, 356 current->position[1] + innerOffset.y, 357 maxAlpha); 358 AlphaVertex::set(&buffer[currentStrokeIndex++], 359 current->position[0] - innerOffset.x, 360 current->position[1] - innerOffset.y, 361 maxAlpha); 362 363 AlphaVertex::set(&buffer[currentAAInnerIndex--], 364 current->position[0] - innerOffset.x, 365 current->position[1] - innerOffset.y, 366 maxAlpha); 367 AlphaVertex::set(&buffer[currentAAInnerIndex--], 368 current->position[0] - outerOffset.x, 369 current->position[1] - outerOffset.y, 370 0.0f); 371 372 last = current; 373 current = next; 374 lastNormal = nextNormal; 375 } 376 377 { 378 // end cap 379 vec2 totalOffset = lastNormal; 380 vec2 AAOffset = totalOffset; 381 AAOffset.x *= 0.5f * inverseScaleX; 382 AAOffset.y *= 0.5f * inverseScaleY; 383 384 vec2 innerOffset = totalOffset; 385 scaleOffsetForStrokeWidth(innerOffset, halfStrokeWidth, inverseScaleX, inverseScaleY); 386 vec2 outerOffset = innerOffset + AAOffset; 387 innerOffset -= AAOffset; 388 389 // TODO: support square cap by changing this offset to incorporate halfStrokeWidth 390 vec2 capAAOffset(-AAOffset.y, AAOffset.x); 391 392 AlphaVertex::set(&buffer[offset + 2], 393 current->position[0] + outerOffset.x + capAAOffset.x, 394 current->position[1] + outerOffset.y + capAAOffset.y, 395 0.0f); 396 AlphaVertex::set(&buffer[offset + 3], 397 current->position[0] + innerOffset.x - capAAOffset.x, 398 current->position[1] + innerOffset.y - capAAOffset.y, 399 maxAlpha); 400 401 AlphaVertex::set(&buffer[offset + 4], 402 current->position[0] - outerOffset.x + capAAOffset.x, 403 current->position[1] - outerOffset.y + capAAOffset.y, 404 0.0f); 405 AlphaVertex::set(&buffer[offset + 5], 406 current->position[0] - innerOffset.x - capAAOffset.x, 407 current->position[1] - innerOffset.y - capAAOffset.y, 408 maxAlpha); 409 410 copyAlphaVertex(&buffer[vertexBuffer.getSize() - 2], &buffer[offset + 3]); 411 copyAlphaVertex(&buffer[vertexBuffer.getSize() - 1], &buffer[offset + 5]); 412 } 413 414 #if VERTEX_DEBUG 415 for (unsigned int i = 0; i < vertexBuffer.getSize(); i++) { 416 ALOGD("point at %f %f, alpha %f", buffer[i].position[0], buffer[i].position[1], buffer[i].alpha); 417 } 418 #endif 419 } 420 421 422 void getStrokeVerticesFromPerimeterAA(const Vector<Vertex>& perimeter, float halfStrokeWidth, 423 VertexBuffer& vertexBuffer, float inverseScaleX, float inverseScaleY) { 424 AlphaVertex* buffer = vertexBuffer.alloc<AlphaVertex>(6 * perimeter.size() + 8); 425 426 // avoid lines smaller than hairline since they break triangle based sampling. instead reducing 427 // alpha value (TODO: support different X/Y scale) 428 float maxAlpha = 1.0f; 429 if (halfStrokeWidth != 0 && inverseScaleX == inverseScaleY && 430 halfStrokeWidth * inverseScaleX < 0.5f) { 431 maxAlpha *= (2 * halfStrokeWidth) / inverseScaleX; 432 halfStrokeWidth = 0.0f; 433 } 434 435 int offset = 2 * perimeter.size() + 3; 436 int currentAAOuterIndex = 0; 437 int currentStrokeIndex = offset; 438 int currentAAInnerIndex = offset * 2; 439 440 const Vertex* last = &(perimeter[perimeter.size() - 1]); 441 const Vertex* current = &(perimeter[0]); 442 vec2 lastNormal(current->position[1] - last->position[1], 443 last->position[0] - current->position[0]); 444 lastNormal.normalize(); 445 for (unsigned int i = 0; i < perimeter.size(); i++) { 446 const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]); 447 vec2 nextNormal(next->position[1] - current->position[1], 448 current->position[0] - next->position[0]); 449 nextNormal.normalize(); 450 451 vec2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal); 452 vec2 AAOffset = totalOffset; 453 AAOffset.x *= 0.5f * inverseScaleX; 454 AAOffset.y *= 0.5f * inverseScaleY; 455 456 vec2 innerOffset = totalOffset; 457 scaleOffsetForStrokeWidth(innerOffset, halfStrokeWidth, inverseScaleX, inverseScaleY); 458 vec2 outerOffset = innerOffset + AAOffset; 459 innerOffset -= AAOffset; 460 461 AlphaVertex::set(&buffer[currentAAOuterIndex++], 462 current->position[0] + outerOffset.x, 463 current->position[1] + outerOffset.y, 464 0.0f); 465 AlphaVertex::set(&buffer[currentAAOuterIndex++], 466 current->position[0] + innerOffset.x, 467 current->position[1] + innerOffset.y, 468 maxAlpha); 469 470 AlphaVertex::set(&buffer[currentStrokeIndex++], 471 current->position[0] + innerOffset.x, 472 current->position[1] + innerOffset.y, 473 maxAlpha); 474 AlphaVertex::set(&buffer[currentStrokeIndex++], 475 current->position[0] - innerOffset.x, 476 current->position[1] - innerOffset.y, 477 maxAlpha); 478 479 AlphaVertex::set(&buffer[currentAAInnerIndex++], 480 current->position[0] - innerOffset.x, 481 current->position[1] - innerOffset.y, 482 maxAlpha); 483 AlphaVertex::set(&buffer[currentAAInnerIndex++], 484 current->position[0] - outerOffset.x, 485 current->position[1] - outerOffset.y, 486 0.0f); 487 488 last = current; 489 current = next; 490 lastNormal = nextNormal; 491 } 492 493 // wrap each strip around to beginning, creating degenerate tris to bridge strips 494 copyAlphaVertex(&buffer[currentAAOuterIndex++], &buffer[0]); 495 copyAlphaVertex(&buffer[currentAAOuterIndex++], &buffer[1]); 496 copyAlphaVertex(&buffer[currentAAOuterIndex++], &buffer[1]); 497 498 copyAlphaVertex(&buffer[currentStrokeIndex++], &buffer[offset]); 499 copyAlphaVertex(&buffer[currentStrokeIndex++], &buffer[offset + 1]); 500 copyAlphaVertex(&buffer[currentStrokeIndex++], &buffer[offset + 1]); 501 502 copyAlphaVertex(&buffer[currentAAInnerIndex++], &buffer[2 * offset]); 503 copyAlphaVertex(&buffer[currentAAInnerIndex++], &buffer[2 * offset + 1]); 504 // don't need to create last degenerate tri 505 506 #if VERTEX_DEBUG 507 for (unsigned int i = 0; i < vertexBuffer.getSize(); i++) { 508 ALOGD("point at %f %f, alpha %f", buffer[i].position[0], buffer[i].position[1], buffer[i].alpha); 509 } 510 #endif 511 } 512 513 void PathRenderer::convexPathVertices(const SkPath &path, const SkPaint* paint, 514 const mat4 *transform, VertexBuffer& vertexBuffer) { 515 ATRACE_CALL(); 516 517 SkPaint::Style style = paint->getStyle(); 518 bool isAA = paint->isAntiAlias(); 519 520 float inverseScaleX, inverseScaleY; 521 computeInverseScales(transform, inverseScaleX, inverseScaleY); 522 523 Vector<Vertex> tempVertices; 524 float threshInvScaleX = inverseScaleX; 525 float threshInvScaleY = inverseScaleY; 526 if (style == SkPaint::kStroke_Style) { 527 // alter the bezier recursion threshold values we calculate in order to compensate for 528 // expansion done after the path vertices are found 529 SkRect bounds = path.getBounds(); 530 if (!bounds.isEmpty()) { 531 threshInvScaleX *= bounds.width() / (bounds.width() + paint->getStrokeWidth()); 532 threshInvScaleY *= bounds.height() / (bounds.height() + paint->getStrokeWidth()); 533 } 534 } 535 536 // force close if we're filling the path, since fill path expects closed perimeter. 537 bool forceClose = style != SkPaint::kStroke_Style; 538 bool wasClosed = convexPathPerimeterVertices(path, forceClose, threshInvScaleX * threshInvScaleX, 539 threshInvScaleY * threshInvScaleY, tempVertices); 540 541 if (!tempVertices.size()) { 542 // path was empty, return without allocating vertex buffer 543 return; 544 } 545 546 #if VERTEX_DEBUG 547 for (unsigned int i = 0; i < tempVertices.size(); i++) { 548 ALOGD("orig path: point at %f %f", tempVertices[i].position[0], tempVertices[i].position[1]); 549 } 550 #endif 551 552 if (style == SkPaint::kStroke_Style) { 553 float halfStrokeWidth = paint->getStrokeWidth() * 0.5f; 554 if (!isAA) { 555 if (wasClosed) { 556 getStrokeVerticesFromPerimeter(tempVertices, halfStrokeWidth, vertexBuffer, 557 inverseScaleX, inverseScaleY); 558 } else { 559 getStrokeVerticesFromUnclosedVertices(tempVertices, halfStrokeWidth, vertexBuffer, 560 inverseScaleX, inverseScaleY); 561 } 562 563 } else { 564 if (wasClosed) { 565 getStrokeVerticesFromPerimeterAA(tempVertices, halfStrokeWidth, vertexBuffer, 566 inverseScaleX, inverseScaleY); 567 } else { 568 getStrokeVerticesFromUnclosedVerticesAA(tempVertices, halfStrokeWidth, vertexBuffer, 569 inverseScaleX, inverseScaleY); 570 } 571 } 572 } else { 573 // For kStrokeAndFill style, the path should be adjusted externally, as it will be treated as a fill here. 574 if (!isAA) { 575 getFillVerticesFromPerimeter(tempVertices, vertexBuffer); 576 } else { 577 getFillVerticesFromPerimeterAA(tempVertices, vertexBuffer, inverseScaleX, inverseScaleY); 578 } 579 } 580 } 581 582 583 void pushToVector(Vector<Vertex>& vertices, float x, float y) { 584 // TODO: make this not yuck 585 vertices.push(); 586 Vertex* newVertex = &(vertices.editArray()[vertices.size() - 1]); 587 Vertex::set(newVertex, x, y); 588 } 589 590 bool PathRenderer::convexPathPerimeterVertices(const SkPath& path, bool forceClose, 591 float sqrInvScaleX, float sqrInvScaleY, Vector<Vertex>& outputVertices) { 592 ATRACE_CALL(); 593 594 // TODO: to support joins other than sharp miter, join vertices should be labelled in the 595 // perimeter, or resolved into more vertices. Reconsider forceClose-ing in that case. 596 SkPath::Iter iter(path, forceClose); 597 SkPoint pts[4]; 598 SkPath::Verb v; 599 Vertex* newVertex = 0; 600 while (SkPath::kDone_Verb != (v = iter.next(pts))) { 601 switch (v) { 602 case SkPath::kMove_Verb: 603 pushToVector(outputVertices, pts[0].x(), pts[0].y()); 604 ALOGV("Move to pos %f %f", pts[0].x(), pts[0].y()); 605 break; 606 case SkPath::kClose_Verb: 607 ALOGV("Close at pos %f %f", pts[0].x(), pts[0].y()); 608 break; 609 case SkPath::kLine_Verb: 610 ALOGV("kLine_Verb %f %f -> %f %f", 611 pts[0].x(), pts[0].y(), 612 pts[1].x(), pts[1].y()); 613 614 pushToVector(outputVertices, pts[1].x(), pts[1].y()); 615 break; 616 case SkPath::kQuad_Verb: 617 ALOGV("kQuad_Verb"); 618 recursiveQuadraticBezierVertices( 619 pts[0].x(), pts[0].y(), 620 pts[2].x(), pts[2].y(), 621 pts[1].x(), pts[1].y(), 622 sqrInvScaleX, sqrInvScaleY, outputVertices); 623 break; 624 case SkPath::kCubic_Verb: 625 ALOGV("kCubic_Verb"); 626 recursiveCubicBezierVertices( 627 pts[0].x(), pts[0].y(), 628 pts[1].x(), pts[1].y(), 629 pts[3].x(), pts[3].y(), 630 pts[2].x(), pts[2].y(), 631 sqrInvScaleX, sqrInvScaleY, outputVertices); 632 break; 633 default: 634 break; 635 } 636 } 637 638 int size = outputVertices.size(); 639 if (size >= 2 && outputVertices[0].position[0] == outputVertices[size - 1].position[0] && 640 outputVertices[0].position[1] == outputVertices[size - 1].position[1]) { 641 outputVertices.pop(); 642 return true; 643 } 644 return false; 645 } 646 647 void PathRenderer::recursiveCubicBezierVertices( 648 float p1x, float p1y, float c1x, float c1y, 649 float p2x, float p2y, float c2x, float c2y, 650 float sqrInvScaleX, float sqrInvScaleY, Vector<Vertex>& outputVertices) { 651 float dx = p2x - p1x; 652 float dy = p2y - p1y; 653 float d1 = fabs((c1x - p2x) * dy - (c1y - p2y) * dx); 654 float d2 = fabs((c2x - p2x) * dy - (c2y - p2y) * dx); 655 float d = d1 + d2; 656 657 // multiplying by sqrInvScaleY/X equivalent to multiplying in dimensional scale factors 658 659 if (d * d < THRESHOLD * THRESHOLD * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) { 660 // below thresh, draw line by adding endpoint 661 pushToVector(outputVertices, p2x, p2y); 662 } else { 663 float p1c1x = (p1x + c1x) * 0.5f; 664 float p1c1y = (p1y + c1y) * 0.5f; 665 float p2c2x = (p2x + c2x) * 0.5f; 666 float p2c2y = (p2y + c2y) * 0.5f; 667 668 float c1c2x = (c1x + c2x) * 0.5f; 669 float c1c2y = (c1y + c2y) * 0.5f; 670 671 float p1c1c2x = (p1c1x + c1c2x) * 0.5f; 672 float p1c1c2y = (p1c1y + c1c2y) * 0.5f; 673 674 float p2c1c2x = (p2c2x + c1c2x) * 0.5f; 675 float p2c1c2y = (p2c2y + c1c2y) * 0.5f; 676 677 float mx = (p1c1c2x + p2c1c2x) * 0.5f; 678 float my = (p1c1c2y + p2c1c2y) * 0.5f; 679 680 recursiveCubicBezierVertices( 681 p1x, p1y, p1c1x, p1c1y, 682 mx, my, p1c1c2x, p1c1c2y, 683 sqrInvScaleX, sqrInvScaleY, outputVertices); 684 recursiveCubicBezierVertices( 685 mx, my, p2c1c2x, p2c1c2y, 686 p2x, p2y, p2c2x, p2c2y, 687 sqrInvScaleX, sqrInvScaleY, outputVertices); 688 } 689 } 690 691 void PathRenderer::recursiveQuadraticBezierVertices( 692 float ax, float ay, 693 float bx, float by, 694 float cx, float cy, 695 float sqrInvScaleX, float sqrInvScaleY, Vector<Vertex>& outputVertices) { 696 float dx = bx - ax; 697 float dy = by - ay; 698 float d = (cx - bx) * dy - (cy - by) * dx; 699 700 if (d * d < THRESHOLD * THRESHOLD * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) { 701 // below thresh, draw line by adding endpoint 702 pushToVector(outputVertices, bx, by); 703 } else { 704 float acx = (ax + cx) * 0.5f; 705 float bcx = (bx + cx) * 0.5f; 706 float acy = (ay + cy) * 0.5f; 707 float bcy = (by + cy) * 0.5f; 708 709 // midpoint 710 float mx = (acx + bcx) * 0.5f; 711 float my = (acy + bcy) * 0.5f; 712 713 recursiveQuadraticBezierVertices(ax, ay, mx, my, acx, acy, 714 sqrInvScaleX, sqrInvScaleY, outputVertices); 715 recursiveQuadraticBezierVertices(mx, my, bx, by, bcx, bcy, 716 sqrInvScaleX, sqrInvScaleY, outputVertices); 717 } 718 } 719 720 }; // namespace uirenderer 721 }; // namespace android 722