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