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