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
computePathBounds(const SkPath & path,const SkPaint * paint)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
computeInverseScales(const mat4 * transform,float & inverseScaleX,float & inverseScaleY)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
copyVertex(Vertex * destPtr,const Vertex * srcPtr)68 inline void copyVertex(Vertex* destPtr, const Vertex* srcPtr) {
69 Vertex::set(destPtr, srcPtr->position[0], srcPtr->position[1]);
70 }
71
copyAlphaVertex(AlphaVertex * destPtr,const AlphaVertex * srcPtr)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 */
totalOffsetFromNormals(const vec2 & normalA,const vec2 & normalB)86 inline vec2 totalOffsetFromNormals(const vec2& normalA, const vec2& normalB) {
87 return (normalA + normalB) / (1 + fabs(normalA.dot(normalB)));
88 }
89
scaleOffsetForStrokeWidth(vec2 & offset,float halfStrokeWidth,float inverseScaleX,float inverseScaleY)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
getFillVerticesFromPerimeter(const Vector<Vertex> & perimeter,VertexBuffer & vertexBuffer)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
getStrokeVerticesFromPerimeter(const Vector<Vertex> & perimeter,float halfStrokeWidth,VertexBuffer & vertexBuffer,float inverseScaleX,float inverseScaleY)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
getStrokeVerticesFromUnclosedVertices(const Vector<Vertex> & vertices,float halfStrokeWidth,VertexBuffer & vertexBuffer,float inverseScaleX,float inverseScaleY)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
getFillVerticesFromPerimeterAA(const Vector<Vertex> & perimeter,VertexBuffer & vertexBuffer,float inverseScaleX,float inverseScaleY)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
getStrokeVerticesFromUnclosedVerticesAA(const Vector<Vertex> & vertices,float halfStrokeWidth,VertexBuffer & vertexBuffer,float inverseScaleX,float inverseScaleY)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
getStrokeVerticesFromPerimeterAA(const Vector<Vertex> & perimeter,float halfStrokeWidth,VertexBuffer & vertexBuffer,float inverseScaleX,float inverseScaleY)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
convexPathVertices(const SkPath & path,const SkPaint * paint,const mat4 * transform,VertexBuffer & vertexBuffer)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
pushToVector(Vector<Vertex> & vertices,float x,float y)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
convexPathPerimeterVertices(const SkPath & path,bool forceClose,float sqrInvScaleX,float sqrInvScaleY,Vector<Vertex> & outputVertices)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
recursiveCubicBezierVertices(float p1x,float p1y,float c1x,float c1y,float p2x,float p2y,float c2x,float c2y,float sqrInvScaleX,float sqrInvScaleY,Vector<Vertex> & outputVertices)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
recursiveQuadraticBezierVertices(float ax,float ay,float bx,float by,float cx,float cy,float sqrInvScaleX,float sqrInvScaleY,Vector<Vertex> & outputVertices)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