Home | History | Annotate | Download | only in hwui
      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  */
     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  */
     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:
    100     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 
    133     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      */
    147     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      */
    155     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      */
    172     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 
    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  */
    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 
    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  */
    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  */
    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  */
    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  */
    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 
    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 
    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>
    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 
    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 
    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 
    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:
    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     }
    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 
    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.
    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 
   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 
   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