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