165cd612face362d054a85d0f7e5881c59cd523beChris Craik/*
265cd612face362d054a85d0f7e5881c59cd523beChris Craik * Copyright (C) 2012 The Android Open Source Project
365cd612face362d054a85d0f7e5881c59cd523beChris Craik *
465cd612face362d054a85d0f7e5881c59cd523beChris Craik * Licensed under the Apache License, Version 2.0 (the "License");
565cd612face362d054a85d0f7e5881c59cd523beChris Craik * you may not use this file except in compliance with the License.
665cd612face362d054a85d0f7e5881c59cd523beChris Craik * You may obtain a copy of the License at
765cd612face362d054a85d0f7e5881c59cd523beChris Craik *
865cd612face362d054a85d0f7e5881c59cd523beChris Craik *      http://www.apache.org/licenses/LICENSE-2.0
965cd612face362d054a85d0f7e5881c59cd523beChris Craik *
1065cd612face362d054a85d0f7e5881c59cd523beChris Craik * Unless required by applicable law or agreed to in writing, software
1165cd612face362d054a85d0f7e5881c59cd523beChris Craik * distributed under the License is distributed on an "AS IS" BASIS,
1265cd612face362d054a85d0f7e5881c59cd523beChris Craik * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1365cd612face362d054a85d0f7e5881c59cd523beChris Craik * See the License for the specific language governing permissions and
1465cd612face362d054a85d0f7e5881c59cd523beChris Craik * limitations under the License.
1565cd612face362d054a85d0f7e5881c59cd523beChris Craik */
1665cd612face362d054a85d0f7e5881c59cd523beChris Craik
1765cd612face362d054a85d0f7e5881c59cd523beChris Craik#define LOG_TAG "PathTessellator"
1865cd612face362d054a85d0f7e5881c59cd523beChris Craik#define LOG_NDEBUG 1
1965cd612face362d054a85d0f7e5881c59cd523beChris Craik#define ATRACE_TAG ATRACE_TAG_GRAPHICS
2065cd612face362d054a85d0f7e5881c59cd523beChris Craik
2165cd612face362d054a85d0f7e5881c59cd523beChris Craik#define VERTEX_DEBUG 0
2265cd612face362d054a85d0f7e5881c59cd523beChris Craik
2365cd612face362d054a85d0f7e5881c59cd523beChris Craik#if VERTEX_DEBUG
2465cd612face362d054a85d0f7e5881c59cd523beChris Craik#define DEBUG_DUMP_ALPHA_BUFFER() \
2565cd612face362d054a85d0f7e5881c59cd523beChris Craik    for (unsigned int i = 0; i < vertexBuffer.getSize(); i++) { \
2665cd612face362d054a85d0f7e5881c59cd523beChris Craik        ALOGD("point %d at %f %f, alpha %f", \
2765cd612face362d054a85d0f7e5881c59cd523beChris Craik        i, buffer[i].position[0], buffer[i].position[1], buffer[i].alpha); \
2865cd612face362d054a85d0f7e5881c59cd523beChris Craik    }
2965cd612face362d054a85d0f7e5881c59cd523beChris Craik#define DEBUG_DUMP_BUFFER() \
3065cd612face362d054a85d0f7e5881c59cd523beChris Craik    for (unsigned int i = 0; i < vertexBuffer.getSize(); i++) { \
3165cd612face362d054a85d0f7e5881c59cd523beChris Craik        ALOGD("point %d at %f %f", i, buffer[i].position[0], buffer[i].position[1]); \
3265cd612face362d054a85d0f7e5881c59cd523beChris Craik    }
3365cd612face362d054a85d0f7e5881c59cd523beChris Craik#else
3465cd612face362d054a85d0f7e5881c59cd523beChris Craik#define DEBUG_DUMP_ALPHA_BUFFER()
3565cd612face362d054a85d0f7e5881c59cd523beChris Craik#define DEBUG_DUMP_BUFFER()
3665cd612face362d054a85d0f7e5881c59cd523beChris Craik#endif
3765cd612face362d054a85d0f7e5881c59cd523beChris Craik
3865cd612face362d054a85d0f7e5881c59cd523beChris Craik#include <SkPath.h>
3965cd612face362d054a85d0f7e5881c59cd523beChris Craik#include <SkPaint.h>
4065cd612face362d054a85d0f7e5881c59cd523beChris Craik
4165cd612face362d054a85d0f7e5881c59cd523beChris Craik#include <stdlib.h>
4265cd612face362d054a85d0f7e5881c59cd523beChris Craik#include <stdint.h>
4365cd612face362d054a85d0f7e5881c59cd523beChris Craik#include <sys/types.h>
4465cd612face362d054a85d0f7e5881c59cd523beChris Craik
4565cd612face362d054a85d0f7e5881c59cd523beChris Craik#include <utils/Log.h>
4665cd612face362d054a85d0f7e5881c59cd523beChris Craik#include <utils/Trace.h>
4765cd612face362d054a85d0f7e5881c59cd523beChris Craik
4865cd612face362d054a85d0f7e5881c59cd523beChris Craik#include "PathTessellator.h"
4965cd612face362d054a85d0f7e5881c59cd523beChris Craik#include "Matrix.h"
5065cd612face362d054a85d0f7e5881c59cd523beChris Craik#include "Vector.h"
5165cd612face362d054a85d0f7e5881c59cd523beChris Craik#include "Vertex.h"
5265cd612face362d054a85d0f7e5881c59cd523beChris Craik
5365cd612face362d054a85d0f7e5881c59cd523beChris Craiknamespace android {
5465cd612face362d054a85d0f7e5881c59cd523beChris Craiknamespace uirenderer {
5565cd612face362d054a85d0f7e5881c59cd523beChris Craik
5665cd612face362d054a85d0f7e5881c59cd523beChris Craik#define THRESHOLD 0.5f
5765cd612face362d054a85d0f7e5881c59cd523beChris Craik#define ROUND_CAP_THRESH 0.25f
5865cd612face362d054a85d0f7e5881c59cd523beChris Craik#define PI 3.1415926535897932f
5965cd612face362d054a85d0f7e5881c59cd523beChris Craik
6065cd612face362d054a85d0f7e5881c59cd523beChris Craikvoid PathTessellator::expandBoundsForStroke(SkRect& bounds, const SkPaint* paint,
6165cd612face362d054a85d0f7e5881c59cd523beChris Craik        bool forceExpand) {
6265cd612face362d054a85d0f7e5881c59cd523beChris Craik    if (forceExpand || paint->getStyle() != SkPaint::kFill_Style) {
6365cd612face362d054a85d0f7e5881c59cd523beChris Craik        float outset = paint->getStrokeWidth() * 0.5f;
64e7c69c6fe3eac1fb01126ede550e5dc32979804aChris Craik        if (outset == 0) outset = 0.5f; // account for hairline
6565cd612face362d054a85d0f7e5881c59cd523beChris Craik        bounds.outset(outset, outset);
6665cd612face362d054a85d0f7e5881c59cd523beChris Craik    }
6765cd612face362d054a85d0f7e5881c59cd523beChris Craik}
6865cd612face362d054a85d0f7e5881c59cd523beChris Craik
696d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craikinline static void copyVertex(Vertex* destPtr, const Vertex* srcPtr) {
7065cd612face362d054a85d0f7e5881c59cd523beChris Craik    Vertex::set(destPtr, srcPtr->position[0], srcPtr->position[1]);
7165cd612face362d054a85d0f7e5881c59cd523beChris Craik}
7265cd612face362d054a85d0f7e5881c59cd523beChris Craik
736d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craikinline static void copyAlphaVertex(AlphaVertex* destPtr, const AlphaVertex* srcPtr) {
7465cd612face362d054a85d0f7e5881c59cd523beChris Craik    AlphaVertex::set(destPtr, srcPtr->position[0], srcPtr->position[1], srcPtr->alpha);
7565cd612face362d054a85d0f7e5881c59cd523beChris Craik}
7665cd612face362d054a85d0f7e5881c59cd523beChris Craik
7765cd612face362d054a85d0f7e5881c59cd523beChris Craik/**
7865cd612face362d054a85d0f7e5881c59cd523beChris Craik * Produces a pseudo-normal for a vertex, given the normals of the two incoming lines. If the offset
7965cd612face362d054a85d0f7e5881c59cd523beChris Craik * from each vertex in a perimeter is calculated, the resultant lines connecting the offset vertices
8065cd612face362d054a85d0f7e5881c59cd523beChris Craik * will be offset by 1.0
8165cd612face362d054a85d0f7e5881c59cd523beChris Craik *
8265cd612face362d054a85d0f7e5881c59cd523beChris Craik * Note that we can't add and normalize the two vectors, that would result in a rectangle having an
8365cd612face362d054a85d0f7e5881c59cd523beChris Craik * offset of (sqrt(2)/2, sqrt(2)/2) at each corner, instead of (1, 1)
8465cd612face362d054a85d0f7e5881c59cd523beChris Craik *
8565cd612face362d054a85d0f7e5881c59cd523beChris Craik * NOTE: assumes angles between normals 90 degrees or less
8665cd612face362d054a85d0f7e5881c59cd523beChris Craik */
876d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craikinline static vec2 totalOffsetFromNormals(const vec2& normalA, const vec2& normalB) {
8865cd612face362d054a85d0f7e5881c59cd523beChris Craik    return (normalA + normalB) / (1 + fabs(normalA.dot(normalB)));
8965cd612face362d054a85d0f7e5881c59cd523beChris Craik}
9065cd612face362d054a85d0f7e5881c59cd523beChris Craik
9165cd612face362d054a85d0f7e5881c59cd523beChris Craik/**
9265cd612face362d054a85d0f7e5881c59cd523beChris Craik * Structure used for storing useful information about the SkPaint and scale used for tessellating
9365cd612face362d054a85d0f7e5881c59cd523beChris Craik */
9465cd612face362d054a85d0f7e5881c59cd523beChris Craikstruct PaintInfo {
9565cd612face362d054a85d0f7e5881c59cd523beChris Craikpublic:
9665cd612face362d054a85d0f7e5881c59cd523beChris Craik    PaintInfo(const SkPaint* paint, const mat4 *transform) :
9765cd612face362d054a85d0f7e5881c59cd523beChris Craik            style(paint->getStyle()), cap(paint->getStrokeCap()), isAA(paint->isAntiAlias()),
9865cd612face362d054a85d0f7e5881c59cd523beChris Craik            inverseScaleX(1.0f), inverseScaleY(1.0f),
9965cd612face362d054a85d0f7e5881c59cd523beChris Craik            halfStrokeWidth(paint->getStrokeWidth() * 0.5f), maxAlpha(1.0f) {
10065cd612face362d054a85d0f7e5881c59cd523beChris Craik        // compute inverse scales
10165cd612face362d054a85d0f7e5881c59cd523beChris Craik        if (CC_UNLIKELY(!transform->isPureTranslate())) {
10265cd612face362d054a85d0f7e5881c59cd523beChris Craik            float m00 = transform->data[Matrix4::kScaleX];
10365cd612face362d054a85d0f7e5881c59cd523beChris Craik            float m01 = transform->data[Matrix4::kSkewY];
10465cd612face362d054a85d0f7e5881c59cd523beChris Craik            float m10 = transform->data[Matrix4::kSkewX];
10565cd612face362d054a85d0f7e5881c59cd523beChris Craik            float m11 = transform->data[Matrix4::kScaleY];
10665cd612face362d054a85d0f7e5881c59cd523beChris Craik            float scaleX = sqrt(m00 * m00 + m01 * m01);
10765cd612face362d054a85d0f7e5881c59cd523beChris Craik            float scaleY = sqrt(m10 * m10 + m11 * m11);
10865cd612face362d054a85d0f7e5881c59cd523beChris Craik            inverseScaleX = (scaleX != 0) ? (1.0f / scaleX) : 1.0f;
10965cd612face362d054a85d0f7e5881c59cd523beChris Craik            inverseScaleY = (scaleY != 0) ? (1.0f / scaleY) : 1.0f;
11065cd612face362d054a85d0f7e5881c59cd523beChris Craik        }
11165cd612face362d054a85d0f7e5881c59cd523beChris Craik
11265cd612face362d054a85d0f7e5881c59cd523beChris Craik        if (isAA && halfStrokeWidth != 0 && inverseScaleX == inverseScaleY &&
11319a390bff348cd379caba1265faec77fcff29200Chris Craik                2 * halfStrokeWidth < inverseScaleX) {
11465cd612face362d054a85d0f7e5881c59cd523beChris Craik            maxAlpha *= (2 * halfStrokeWidth) / inverseScaleX;
11565cd612face362d054a85d0f7e5881c59cd523beChris Craik            halfStrokeWidth = 0.0f;
11665cd612face362d054a85d0f7e5881c59cd523beChris Craik        }
11765cd612face362d054a85d0f7e5881c59cd523beChris Craik    }
11865cd612face362d054a85d0f7e5881c59cd523beChris Craik
11965cd612face362d054a85d0f7e5881c59cd523beChris Craik    SkPaint::Style style;
12065cd612face362d054a85d0f7e5881c59cd523beChris Craik    SkPaint::Cap cap;
12165cd612face362d054a85d0f7e5881c59cd523beChris Craik    bool isAA;
12265cd612face362d054a85d0f7e5881c59cd523beChris Craik    float inverseScaleX;
12365cd612face362d054a85d0f7e5881c59cd523beChris Craik    float inverseScaleY;
12465cd612face362d054a85d0f7e5881c59cd523beChris Craik    float halfStrokeWidth;
12565cd612face362d054a85d0f7e5881c59cd523beChris Craik    float maxAlpha;
12665cd612face362d054a85d0f7e5881c59cd523beChris Craik
12765cd612face362d054a85d0f7e5881c59cd523beChris Craik    inline void scaleOffsetForStrokeWidth(vec2& offset) const {
12865cd612face362d054a85d0f7e5881c59cd523beChris Craik        if (halfStrokeWidth == 0.0f) {
12965cd612face362d054a85d0f7e5881c59cd523beChris Craik            // hairline - compensate for scale
13065cd612face362d054a85d0f7e5881c59cd523beChris Craik            offset.x *= 0.5f * inverseScaleX;
13165cd612face362d054a85d0f7e5881c59cd523beChris Craik            offset.y *= 0.5f * inverseScaleY;
13265cd612face362d054a85d0f7e5881c59cd523beChris Craik        } else {
13365cd612face362d054a85d0f7e5881c59cd523beChris Craik            offset *= halfStrokeWidth;
13465cd612face362d054a85d0f7e5881c59cd523beChris Craik        }
13565cd612face362d054a85d0f7e5881c59cd523beChris Craik    }
13665cd612face362d054a85d0f7e5881c59cd523beChris Craik
13765cd612face362d054a85d0f7e5881c59cd523beChris Craik    /**
13865cd612face362d054a85d0f7e5881c59cd523beChris Craik     * NOTE: the input will not always be a normal, especially for sharp edges - it should be the
13965cd612face362d054a85d0f7e5881c59cd523beChris Craik     * result of totalOffsetFromNormals (see documentation there)
14065cd612face362d054a85d0f7e5881c59cd523beChris Craik     */
14165cd612face362d054a85d0f7e5881c59cd523beChris Craik    inline vec2 deriveAAOffset(const vec2& offset) const {
14265cd612face362d054a85d0f7e5881c59cd523beChris Craik        return vec2(offset.x * 0.5f * inverseScaleX,
14365cd612face362d054a85d0f7e5881c59cd523beChris Craik            offset.y * 0.5f * inverseScaleY);
14465cd612face362d054a85d0f7e5881c59cd523beChris Craik    }
14565cd612face362d054a85d0f7e5881c59cd523beChris Craik
14665cd612face362d054a85d0f7e5881c59cd523beChris Craik    /**
14765cd612face362d054a85d0f7e5881c59cd523beChris Craik     * Returns the number of cap divisions beyond the minimum 2 (kButt_Cap/kSquareCap will return 0)
14865cd612face362d054a85d0f7e5881c59cd523beChris Craik     * Should only be used when stroking and drawing caps
14965cd612face362d054a85d0f7e5881c59cd523beChris Craik     */
15065cd612face362d054a85d0f7e5881c59cd523beChris Craik    inline int capExtraDivisions() const {
15165cd612face362d054a85d0f7e5881c59cd523beChris Craik        if (cap == SkPaint::kRound_Cap) {
15265cd612face362d054a85d0f7e5881c59cd523beChris Craik            if (halfStrokeWidth == 0.0f) return 2;
15365cd612face362d054a85d0f7e5881c59cd523beChris Craik
15465cd612face362d054a85d0f7e5881c59cd523beChris Craik            // ROUND_CAP_THRESH is the maximum error for polygonal approximation of the round cap
15565cd612face362d054a85d0f7e5881c59cd523beChris Craik            const float errConst = (-ROUND_CAP_THRESH / halfStrokeWidth + 1);
15665cd612face362d054a85d0f7e5881c59cd523beChris Craik            const float targetCosVal = 2 * errConst * errConst - 1;
15765cd612face362d054a85d0f7e5881c59cd523beChris Craik            int neededDivisions = (int)(ceilf(PI / acos(targetCosVal)/2)) * 2;
15865cd612face362d054a85d0f7e5881c59cd523beChris Craik            return neededDivisions;
15965cd612face362d054a85d0f7e5881c59cd523beChris Craik        }
16065cd612face362d054a85d0f7e5881c59cd523beChris Craik        return 0;
16165cd612face362d054a85d0f7e5881c59cd523beChris Craik    }
16265cd612face362d054a85d0f7e5881c59cd523beChris Craik};
16365cd612face362d054a85d0f7e5881c59cd523beChris Craik
16465cd612face362d054a85d0f7e5881c59cd523beChris Craikvoid getFillVerticesFromPerimeter(const Vector<Vertex>& perimeter, VertexBuffer& vertexBuffer) {
16565cd612face362d054a85d0f7e5881c59cd523beChris Craik    Vertex* buffer = vertexBuffer.alloc<Vertex>(perimeter.size());
16665cd612face362d054a85d0f7e5881c59cd523beChris Craik
16765cd612face362d054a85d0f7e5881c59cd523beChris Craik    int currentIndex = 0;
16865cd612face362d054a85d0f7e5881c59cd523beChris Craik    // zig zag between all previous points on the inside of the hull to create a
16965cd612face362d054a85d0f7e5881c59cd523beChris Craik    // triangle strip that fills the hull
17065cd612face362d054a85d0f7e5881c59cd523beChris Craik    int srcAindex = 0;
17165cd612face362d054a85d0f7e5881c59cd523beChris Craik    int srcBindex = perimeter.size() - 1;
17265cd612face362d054a85d0f7e5881c59cd523beChris Craik    while (srcAindex <= srcBindex) {
17365cd612face362d054a85d0f7e5881c59cd523beChris Craik        copyVertex(&buffer[currentIndex++], &perimeter[srcAindex]);
17465cd612face362d054a85d0f7e5881c59cd523beChris Craik        if (srcAindex == srcBindex) break;
17565cd612face362d054a85d0f7e5881c59cd523beChris Craik        copyVertex(&buffer[currentIndex++], &perimeter[srcBindex]);
17665cd612face362d054a85d0f7e5881c59cd523beChris Craik        srcAindex++;
17765cd612face362d054a85d0f7e5881c59cd523beChris Craik        srcBindex--;
17865cd612face362d054a85d0f7e5881c59cd523beChris Craik    }
17965cd612face362d054a85d0f7e5881c59cd523beChris Craik}
18065cd612face362d054a85d0f7e5881c59cd523beChris Craik
18165cd612face362d054a85d0f7e5881c59cd523beChris Craik/*
18265cd612face362d054a85d0f7e5881c59cd523beChris Craik * Fills a vertexBuffer with non-alpha vertices, zig-zagging at each perimeter point to create a
18365cd612face362d054a85d0f7e5881c59cd523beChris Craik * tri-strip as wide as the stroke.
18465cd612face362d054a85d0f7e5881c59cd523beChris Craik *
18565cd612face362d054a85d0f7e5881c59cd523beChris Craik * Uses an additional 2 vertices at the end to wrap around, closing the tri-strip
18665cd612face362d054a85d0f7e5881c59cd523beChris Craik * (for a total of perimeter.size() * 2 + 2 vertices)
18765cd612face362d054a85d0f7e5881c59cd523beChris Craik */
18865cd612face362d054a85d0f7e5881c59cd523beChris Craikvoid getStrokeVerticesFromPerimeter(const PaintInfo& paintInfo, const Vector<Vertex>& perimeter,
18965cd612face362d054a85d0f7e5881c59cd523beChris Craik        VertexBuffer& vertexBuffer) {
19065cd612face362d054a85d0f7e5881c59cd523beChris Craik    Vertex* buffer = vertexBuffer.alloc<Vertex>(perimeter.size() * 2 + 2);
19165cd612face362d054a85d0f7e5881c59cd523beChris Craik
19265cd612face362d054a85d0f7e5881c59cd523beChris Craik    int currentIndex = 0;
19365cd612face362d054a85d0f7e5881c59cd523beChris Craik    const Vertex* last = &(perimeter[perimeter.size() - 1]);
19465cd612face362d054a85d0f7e5881c59cd523beChris Craik    const Vertex* current = &(perimeter[0]);
19565cd612face362d054a85d0f7e5881c59cd523beChris Craik    vec2 lastNormal(current->position[1] - last->position[1],
19665cd612face362d054a85d0f7e5881c59cd523beChris Craik            last->position[0] - current->position[0]);
19765cd612face362d054a85d0f7e5881c59cd523beChris Craik    lastNormal.normalize();
19865cd612face362d054a85d0f7e5881c59cd523beChris Craik    for (unsigned int i = 0; i < perimeter.size(); i++) {
19965cd612face362d054a85d0f7e5881c59cd523beChris Craik        const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]);
20065cd612face362d054a85d0f7e5881c59cd523beChris Craik        vec2 nextNormal(next->position[1] - current->position[1],
20165cd612face362d054a85d0f7e5881c59cd523beChris Craik                current->position[0] - next->position[0]);
20265cd612face362d054a85d0f7e5881c59cd523beChris Craik        nextNormal.normalize();
20365cd612face362d054a85d0f7e5881c59cd523beChris Craik
20465cd612face362d054a85d0f7e5881c59cd523beChris Craik        vec2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
20565cd612face362d054a85d0f7e5881c59cd523beChris Craik        paintInfo.scaleOffsetForStrokeWidth(totalOffset);
20665cd612face362d054a85d0f7e5881c59cd523beChris Craik
20765cd612face362d054a85d0f7e5881c59cd523beChris Craik        Vertex::set(&buffer[currentIndex++],
20865cd612face362d054a85d0f7e5881c59cd523beChris Craik                current->position[0] + totalOffset.x,
20965cd612face362d054a85d0f7e5881c59cd523beChris Craik                current->position[1] + totalOffset.y);
21065cd612face362d054a85d0f7e5881c59cd523beChris Craik
21165cd612face362d054a85d0f7e5881c59cd523beChris Craik        Vertex::set(&buffer[currentIndex++],
21265cd612face362d054a85d0f7e5881c59cd523beChris Craik                current->position[0] - totalOffset.x,
21365cd612face362d054a85d0f7e5881c59cd523beChris Craik                current->position[1] - totalOffset.y);
21465cd612face362d054a85d0f7e5881c59cd523beChris Craik
21565cd612face362d054a85d0f7e5881c59cd523beChris Craik        last = current;
21665cd612face362d054a85d0f7e5881c59cd523beChris Craik        current = next;
21765cd612face362d054a85d0f7e5881c59cd523beChris Craik        lastNormal = nextNormal;
21865cd612face362d054a85d0f7e5881c59cd523beChris Craik    }
21965cd612face362d054a85d0f7e5881c59cd523beChris Craik
22065cd612face362d054a85d0f7e5881c59cd523beChris Craik    // wrap around to beginning
22165cd612face362d054a85d0f7e5881c59cd523beChris Craik    copyVertex(&buffer[currentIndex++], &buffer[0]);
22265cd612face362d054a85d0f7e5881c59cd523beChris Craik    copyVertex(&buffer[currentIndex++], &buffer[1]);
22365cd612face362d054a85d0f7e5881c59cd523beChris Craik
22465cd612face362d054a85d0f7e5881c59cd523beChris Craik    DEBUG_DUMP_BUFFER();
22565cd612face362d054a85d0f7e5881c59cd523beChris Craik}
22665cd612face362d054a85d0f7e5881c59cd523beChris Craik
2276d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craikstatic inline void storeBeginEnd(const PaintInfo& paintInfo, const Vertex& center,
2286d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik        const vec2& normal, Vertex* buffer, int& currentIndex, bool begin) {
2296d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik    vec2 strokeOffset = normal;
2306d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik    paintInfo.scaleOffsetForStrokeWidth(strokeOffset);
2316d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik
2326d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik    vec2 referencePoint(center.position[0], center.position[1]);
2336d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik    if (paintInfo.cap == SkPaint::kSquare_Cap) {
2346d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik        referencePoint += vec2(-strokeOffset.y, strokeOffset.x) * (begin ? -1 : 1);
2356d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik    }
2366d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik
2376d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik    Vertex::set(&buffer[currentIndex++], referencePoint + strokeOffset);
2386d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik    Vertex::set(&buffer[currentIndex++], referencePoint - strokeOffset);
2396d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik}
2406d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik
24165cd612face362d054a85d0f7e5881c59cd523beChris Craik/**
24265cd612face362d054a85d0f7e5881c59cd523beChris Craik * Fills a vertexBuffer with non-alpha vertices similar to getStrokeVerticesFromPerimeter, except:
24365cd612face362d054a85d0f7e5881c59cd523beChris Craik *
24465cd612face362d054a85d0f7e5881c59cd523beChris Craik * 1 - Doesn't need to wrap around, since the input vertices are unclosed
24565cd612face362d054a85d0f7e5881c59cd523beChris Craik *
24665cd612face362d054a85d0f7e5881c59cd523beChris Craik * 2 - can zig-zag across 'extra' vertices at either end, to create round caps
24765cd612face362d054a85d0f7e5881c59cd523beChris Craik */
24865cd612face362d054a85d0f7e5881c59cd523beChris Craikvoid getStrokeVerticesFromUnclosedVertices(const PaintInfo& paintInfo,
24965cd612face362d054a85d0f7e5881c59cd523beChris Craik        const Vector<Vertex>& vertices, VertexBuffer& vertexBuffer) {
25065cd612face362d054a85d0f7e5881c59cd523beChris Craik    const int extra = paintInfo.capExtraDivisions();
25165cd612face362d054a85d0f7e5881c59cd523beChris Craik    const int allocSize = (vertices.size() + extra) * 2;
25265cd612face362d054a85d0f7e5881c59cd523beChris Craik    Vertex* buffer = vertexBuffer.alloc<Vertex>(allocSize);
25365cd612face362d054a85d0f7e5881c59cd523beChris Craik
2546d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik    const int lastIndex = vertices.size() - 1;
25565cd612face362d054a85d0f7e5881c59cd523beChris Craik    if (extra > 0) {
25665cd612face362d054a85d0f7e5881c59cd523beChris Craik        // tessellate both round caps
25765cd612face362d054a85d0f7e5881c59cd523beChris Craik        float beginTheta = atan2(
2586d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik                    - (vertices[0].position[0] - vertices[1].position[0]),
2596d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik                    vertices[0].position[1] - vertices[1].position[1]);
26065cd612face362d054a85d0f7e5881c59cd523beChris Craik        float endTheta = atan2(
2616d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik                    - (vertices[lastIndex].position[0] - vertices[lastIndex - 1].position[0]),
2626d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik                    vertices[lastIndex].position[1] - vertices[lastIndex - 1].position[1]);
26365cd612face362d054a85d0f7e5881c59cd523beChris Craik        const float dTheta = PI / (extra + 1);
26465cd612face362d054a85d0f7e5881c59cd523beChris Craik        const float radialScale = 2.0f / (1 + cos(dTheta));
26565cd612face362d054a85d0f7e5881c59cd523beChris Craik
26665cd612face362d054a85d0f7e5881c59cd523beChris Craik        int capOffset;
26765cd612face362d054a85d0f7e5881c59cd523beChris Craik        for (int i = 0; i < extra; i++) {
26865cd612face362d054a85d0f7e5881c59cd523beChris Craik            if (i < extra / 2) {
26965cd612face362d054a85d0f7e5881c59cd523beChris Craik                capOffset = extra - 2 * i - 1;
27065cd612face362d054a85d0f7e5881c59cd523beChris Craik            } else {
27165cd612face362d054a85d0f7e5881c59cd523beChris Craik                capOffset = 2 * i - extra;
27265cd612face362d054a85d0f7e5881c59cd523beChris Craik            }
27365cd612face362d054a85d0f7e5881c59cd523beChris Craik
27465cd612face362d054a85d0f7e5881c59cd523beChris Craik            beginTheta += dTheta;
27565cd612face362d054a85d0f7e5881c59cd523beChris Craik            vec2 beginRadialOffset(cos(beginTheta), sin(beginTheta));
27665cd612face362d054a85d0f7e5881c59cd523beChris Craik            paintInfo.scaleOffsetForStrokeWidth(beginRadialOffset);
27765cd612face362d054a85d0f7e5881c59cd523beChris Craik            Vertex::set(&buffer[capOffset],
27865cd612face362d054a85d0f7e5881c59cd523beChris Craik                    vertices[0].position[0] + beginRadialOffset.x,
27965cd612face362d054a85d0f7e5881c59cd523beChris Craik                    vertices[0].position[1] + beginRadialOffset.y);
28065cd612face362d054a85d0f7e5881c59cd523beChris Craik
28165cd612face362d054a85d0f7e5881c59cd523beChris Craik            endTheta += dTheta;
28265cd612face362d054a85d0f7e5881c59cd523beChris Craik            vec2 endRadialOffset(cos(endTheta), sin(endTheta));
28365cd612face362d054a85d0f7e5881c59cd523beChris Craik            paintInfo.scaleOffsetForStrokeWidth(endRadialOffset);
28465cd612face362d054a85d0f7e5881c59cd523beChris Craik            Vertex::set(&buffer[allocSize - 1 - capOffset],
2856d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik                    vertices[lastIndex].position[0] + endRadialOffset.x,
2866d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik                    vertices[lastIndex].position[1] + endRadialOffset.y);
28765cd612face362d054a85d0f7e5881c59cd523beChris Craik        }
28865cd612face362d054a85d0f7e5881c59cd523beChris Craik    }
28965cd612face362d054a85d0f7e5881c59cd523beChris Craik
29065cd612face362d054a85d0f7e5881c59cd523beChris Craik    int currentIndex = extra;
2916d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik    const Vertex* last = &(vertices[0]);
2926d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik    const Vertex* current = &(vertices[1]);
2936d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik    vec2 lastNormal(current->position[1] - last->position[1],
2946d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik                last->position[0] - current->position[0]);
2956d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik    lastNormal.normalize();
2966d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik
2976d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik    storeBeginEnd(paintInfo, vertices[0], lastNormal, buffer, currentIndex, true);
2986d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik
2996d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik    for (unsigned int i = 1; i < vertices.size() - 1; i++) {
30065cd612face362d054a85d0f7e5881c59cd523beChris Craik        const Vertex* next = &(vertices[i + 1]);
30165cd612face362d054a85d0f7e5881c59cd523beChris Craik        vec2 nextNormal(next->position[1] - current->position[1],
30265cd612face362d054a85d0f7e5881c59cd523beChris Craik                current->position[0] - next->position[0]);
30365cd612face362d054a85d0f7e5881c59cd523beChris Craik        nextNormal.normalize();
30465cd612face362d054a85d0f7e5881c59cd523beChris Craik
3056d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik        vec2 strokeOffset  = totalOffsetFromNormals(lastNormal, nextNormal);
3066d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik        paintInfo.scaleOffsetForStrokeWidth(strokeOffset);
30765cd612face362d054a85d0f7e5881c59cd523beChris Craik
3086d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik        vec2 center(current->position[0], current->position[1]);
3096d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik        Vertex::set(&buffer[currentIndex++], center + strokeOffset);
3106d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik        Vertex::set(&buffer[currentIndex++], center - strokeOffset);
31165cd612face362d054a85d0f7e5881c59cd523beChris Craik
31265cd612face362d054a85d0f7e5881c59cd523beChris Craik        current = next;
31365cd612face362d054a85d0f7e5881c59cd523beChris Craik        lastNormal = nextNormal;
31465cd612face362d054a85d0f7e5881c59cd523beChris Craik    }
31565cd612face362d054a85d0f7e5881c59cd523beChris Craik
3166d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik    storeBeginEnd(paintInfo, vertices[lastIndex], lastNormal, buffer, currentIndex, false);
31765cd612face362d054a85d0f7e5881c59cd523beChris Craik
31865cd612face362d054a85d0f7e5881c59cd523beChris Craik    DEBUG_DUMP_BUFFER();
31965cd612face362d054a85d0f7e5881c59cd523beChris Craik}
32065cd612face362d054a85d0f7e5881c59cd523beChris Craik
32165cd612face362d054a85d0f7e5881c59cd523beChris Craik/**
32265cd612face362d054a85d0f7e5881c59cd523beChris Craik * Populates a vertexBuffer with AlphaVertices to create an anti-aliased fill shape tessellation
3236d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik *
32465cd612face362d054a85d0f7e5881c59cd523beChris Craik * 1 - create the AA perimeter of unit width, by zig-zagging at each point around the perimeter of
32565cd612face362d054a85d0f7e5881c59cd523beChris Craik * the shape (using 2 * perimeter.size() vertices)
32665cd612face362d054a85d0f7e5881c59cd523beChris Craik *
32765cd612face362d054a85d0f7e5881c59cd523beChris Craik * 2 - wrap around to the beginning to complete the perimeter (2 vertices)
32865cd612face362d054a85d0f7e5881c59cd523beChris Craik *
32965cd612face362d054a85d0f7e5881c59cd523beChris Craik * 3 - zig zag back and forth inside the shape to fill it (using perimeter.size() vertices)
33065cd612face362d054a85d0f7e5881c59cd523beChris Craik */
33165cd612face362d054a85d0f7e5881c59cd523beChris Craikvoid getFillVerticesFromPerimeterAA(const PaintInfo& paintInfo, const Vector<Vertex>& perimeter,
33265cd612face362d054a85d0f7e5881c59cd523beChris Craik        VertexBuffer& vertexBuffer) {
33365cd612face362d054a85d0f7e5881c59cd523beChris Craik    AlphaVertex* buffer = vertexBuffer.alloc<AlphaVertex>(perimeter.size() * 3 + 2);
33465cd612face362d054a85d0f7e5881c59cd523beChris Craik
33565cd612face362d054a85d0f7e5881c59cd523beChris Craik    // generate alpha points - fill Alpha vertex gaps in between each point with
33665cd612face362d054a85d0f7e5881c59cd523beChris Craik    // alpha 0 vertex, offset by a scaled normal.
33765cd612face362d054a85d0f7e5881c59cd523beChris Craik    int currentIndex = 0;
33865cd612face362d054a85d0f7e5881c59cd523beChris Craik    const Vertex* last = &(perimeter[perimeter.size() - 1]);
33965cd612face362d054a85d0f7e5881c59cd523beChris Craik    const Vertex* current = &(perimeter[0]);
34065cd612face362d054a85d0f7e5881c59cd523beChris Craik    vec2 lastNormal(current->position[1] - last->position[1],
34165cd612face362d054a85d0f7e5881c59cd523beChris Craik            last->position[0] - current->position[0]);
34265cd612face362d054a85d0f7e5881c59cd523beChris Craik    lastNormal.normalize();
34365cd612face362d054a85d0f7e5881c59cd523beChris Craik    for (unsigned int i = 0; i < perimeter.size(); i++) {
34465cd612face362d054a85d0f7e5881c59cd523beChris Craik        const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]);
34565cd612face362d054a85d0f7e5881c59cd523beChris Craik        vec2 nextNormal(next->position[1] - current->position[1],
34665cd612face362d054a85d0f7e5881c59cd523beChris Craik                current->position[0] - next->position[0]);
34765cd612face362d054a85d0f7e5881c59cd523beChris Craik        nextNormal.normalize();
34865cd612face362d054a85d0f7e5881c59cd523beChris Craik
34965cd612face362d054a85d0f7e5881c59cd523beChris Craik        // AA point offset from original point is that point's normal, such that each side is offset
35065cd612face362d054a85d0f7e5881c59cd523beChris Craik        // by .5 pixels
35165cd612face362d054a85d0f7e5881c59cd523beChris Craik        vec2 totalOffset = paintInfo.deriveAAOffset(totalOffsetFromNormals(lastNormal, nextNormal));
35265cd612face362d054a85d0f7e5881c59cd523beChris Craik
35365cd612face362d054a85d0f7e5881c59cd523beChris Craik        AlphaVertex::set(&buffer[currentIndex++],
35465cd612face362d054a85d0f7e5881c59cd523beChris Craik                current->position[0] + totalOffset.x,
35565cd612face362d054a85d0f7e5881c59cd523beChris Craik                current->position[1] + totalOffset.y,
35665cd612face362d054a85d0f7e5881c59cd523beChris Craik                0.0f);
35765cd612face362d054a85d0f7e5881c59cd523beChris Craik        AlphaVertex::set(&buffer[currentIndex++],
35865cd612face362d054a85d0f7e5881c59cd523beChris Craik                current->position[0] - totalOffset.x,
35965cd612face362d054a85d0f7e5881c59cd523beChris Craik                current->position[1] - totalOffset.y,
36065cd612face362d054a85d0f7e5881c59cd523beChris Craik                1.0f);
36165cd612face362d054a85d0f7e5881c59cd523beChris Craik
36265cd612face362d054a85d0f7e5881c59cd523beChris Craik        last = current;
36365cd612face362d054a85d0f7e5881c59cd523beChris Craik        current = next;
36465cd612face362d054a85d0f7e5881c59cd523beChris Craik        lastNormal = nextNormal;
36565cd612face362d054a85d0f7e5881c59cd523beChris Craik    }
36665cd612face362d054a85d0f7e5881c59cd523beChris Craik
36765cd612face362d054a85d0f7e5881c59cd523beChris Craik    // wrap around to beginning
36865cd612face362d054a85d0f7e5881c59cd523beChris Craik    copyAlphaVertex(&buffer[currentIndex++], &buffer[0]);
36965cd612face362d054a85d0f7e5881c59cd523beChris Craik    copyAlphaVertex(&buffer[currentIndex++], &buffer[1]);
37065cd612face362d054a85d0f7e5881c59cd523beChris Craik
37165cd612face362d054a85d0f7e5881c59cd523beChris Craik    // zig zag between all previous points on the inside of the hull to create a
37265cd612face362d054a85d0f7e5881c59cd523beChris Craik    // triangle strip that fills the hull, repeating the first inner point to
37365cd612face362d054a85d0f7e5881c59cd523beChris Craik    // create degenerate tris to start inside path
37465cd612face362d054a85d0f7e5881c59cd523beChris Craik    int srcAindex = 0;
37565cd612face362d054a85d0f7e5881c59cd523beChris Craik    int srcBindex = perimeter.size() - 1;
37665cd612face362d054a85d0f7e5881c59cd523beChris Craik    while (srcAindex <= srcBindex) {
37765cd612face362d054a85d0f7e5881c59cd523beChris Craik        copyAlphaVertex(&buffer[currentIndex++], &buffer[srcAindex * 2 + 1]);
37865cd612face362d054a85d0f7e5881c59cd523beChris Craik        if (srcAindex == srcBindex) break;
37965cd612face362d054a85d0f7e5881c59cd523beChris Craik        copyAlphaVertex(&buffer[currentIndex++], &buffer[srcBindex * 2 + 1]);
38065cd612face362d054a85d0f7e5881c59cd523beChris Craik        srcAindex++;
38165cd612face362d054a85d0f7e5881c59cd523beChris Craik        srcBindex--;
38265cd612face362d054a85d0f7e5881c59cd523beChris Craik    }
38365cd612face362d054a85d0f7e5881c59cd523beChris Craik
38465cd612face362d054a85d0f7e5881c59cd523beChris Craik    DEBUG_DUMP_BUFFER();
38565cd612face362d054a85d0f7e5881c59cd523beChris Craik}
38665cd612face362d054a85d0f7e5881c59cd523beChris Craik
38765cd612face362d054a85d0f7e5881c59cd523beChris Craik/**
38865cd612face362d054a85d0f7e5881c59cd523beChris Craik * Stores geometry for a single, AA-perimeter (potentially rounded) cap
38965cd612face362d054a85d0f7e5881c59cd523beChris Craik *
39065cd612face362d054a85d0f7e5881c59cd523beChris Craik * For explanation of constants and general methodoloyg, see comments for
39165cd612face362d054a85d0f7e5881c59cd523beChris Craik * getStrokeVerticesFromUnclosedVerticesAA() below.
39265cd612face362d054a85d0f7e5881c59cd523beChris Craik */
3936d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craikinline static void storeCapAA(const PaintInfo& paintInfo, const Vector<Vertex>& vertices,
39465cd612face362d054a85d0f7e5881c59cd523beChris Craik        AlphaVertex* buffer, bool isFirst, vec2 normal, int offset) {
39565cd612face362d054a85d0f7e5881c59cd523beChris Craik    const int extra = paintInfo.capExtraDivisions();
39665cd612face362d054a85d0f7e5881c59cd523beChris Craik    const int extraOffset = (extra + 1) / 2;
39765cd612face362d054a85d0f7e5881c59cd523beChris Craik    const int capIndex = isFirst
39865cd612face362d054a85d0f7e5881c59cd523beChris Craik            ? 2 * offset + 6 + 2 * (extra + extraOffset)
39965cd612face362d054a85d0f7e5881c59cd523beChris Craik            : offset + 2 + 2 * extraOffset;
40065cd612face362d054a85d0f7e5881c59cd523beChris Craik    if (isFirst) normal *= -1;
40165cd612face362d054a85d0f7e5881c59cd523beChris Craik
40265cd612face362d054a85d0f7e5881c59cd523beChris Craik    // TODO: this normal should be scaled by radialScale if extra != 0, see totalOffsetFromNormals()
40365cd612face362d054a85d0f7e5881c59cd523beChris Craik    vec2 AAOffset = paintInfo.deriveAAOffset(normal);
40465cd612face362d054a85d0f7e5881c59cd523beChris Craik
40565cd612face362d054a85d0f7e5881c59cd523beChris Craik    vec2 strokeOffset = normal;
40665cd612face362d054a85d0f7e5881c59cd523beChris Craik    paintInfo.scaleOffsetForStrokeWidth(strokeOffset);
40765cd612face362d054a85d0f7e5881c59cd523beChris Craik    vec2 outerOffset = strokeOffset + AAOffset;
40865cd612face362d054a85d0f7e5881c59cd523beChris Craik    vec2 innerOffset = strokeOffset - AAOffset;
40965cd612face362d054a85d0f7e5881c59cd523beChris Craik
41065cd612face362d054a85d0f7e5881c59cd523beChris Craik    vec2 capAAOffset;
41165cd612face362d054a85d0f7e5881c59cd523beChris Craik    if (paintInfo.cap != SkPaint::kRound_Cap) {
41265cd612face362d054a85d0f7e5881c59cd523beChris Craik        // if the cap is square or butt, the inside primary cap vertices will be inset in two
41365cd612face362d054a85d0f7e5881c59cd523beChris Craik        // directions - both normal to the stroke, and parallel to it.
41465cd612face362d054a85d0f7e5881c59cd523beChris Craik        capAAOffset = vec2(-AAOffset.y, AAOffset.x);
41565cd612face362d054a85d0f7e5881c59cd523beChris Craik    }
41665cd612face362d054a85d0f7e5881c59cd523beChris Craik
41765cd612face362d054a85d0f7e5881c59cd523beChris Craik    // determine referencePoint, the center point for the 4 primary cap vertices
41865cd612face362d054a85d0f7e5881c59cd523beChris Craik    const Vertex* point = isFirst ? vertices.begin() : (vertices.end() - 1);
41965cd612face362d054a85d0f7e5881c59cd523beChris Craik    vec2 referencePoint(point->position[0], point->position[1]);
42065cd612face362d054a85d0f7e5881c59cd523beChris Craik    if (paintInfo.cap == SkPaint::kSquare_Cap) {
42165cd612face362d054a85d0f7e5881c59cd523beChris Craik        // To account for square cap, move the primary cap vertices (that create the AA edge) by the
42265cd612face362d054a85d0f7e5881c59cd523beChris Craik        // stroke offset vector (rotated to be parallel to the stroke)
42365cd612face362d054a85d0f7e5881c59cd523beChris Craik        referencePoint += vec2(-strokeOffset.y, strokeOffset.x);
42465cd612face362d054a85d0f7e5881c59cd523beChris Craik    }
42565cd612face362d054a85d0f7e5881c59cd523beChris Craik
42665cd612face362d054a85d0f7e5881c59cd523beChris Craik    AlphaVertex::set(&buffer[capIndex + 0],
42765cd612face362d054a85d0f7e5881c59cd523beChris Craik            referencePoint.x + outerOffset.x + capAAOffset.x,
42865cd612face362d054a85d0f7e5881c59cd523beChris Craik            referencePoint.y + outerOffset.y + capAAOffset.y,
42965cd612face362d054a85d0f7e5881c59cd523beChris Craik            0.0f);
43065cd612face362d054a85d0f7e5881c59cd523beChris Craik    AlphaVertex::set(&buffer[capIndex + 1],
43165cd612face362d054a85d0f7e5881c59cd523beChris Craik            referencePoint.x + innerOffset.x - capAAOffset.x,
43265cd612face362d054a85d0f7e5881c59cd523beChris Craik            referencePoint.y + innerOffset.y - capAAOffset.y,
43365cd612face362d054a85d0f7e5881c59cd523beChris Craik            paintInfo.maxAlpha);
43465cd612face362d054a85d0f7e5881c59cd523beChris Craik
43565cd612face362d054a85d0f7e5881c59cd523beChris Craik    bool isRound = paintInfo.cap == SkPaint::kRound_Cap;
43665cd612face362d054a85d0f7e5881c59cd523beChris Craik
43765cd612face362d054a85d0f7e5881c59cd523beChris Craik    const int postCapIndex = (isRound && isFirst) ? (2 * extraOffset - 2) : capIndex + (2 * extra);
43865cd612face362d054a85d0f7e5881c59cd523beChris Craik    AlphaVertex::set(&buffer[postCapIndex + 2],
43965cd612face362d054a85d0f7e5881c59cd523beChris Craik            referencePoint.x - outerOffset.x + capAAOffset.x,
44065cd612face362d054a85d0f7e5881c59cd523beChris Craik            referencePoint.y - outerOffset.y + capAAOffset.y,
44165cd612face362d054a85d0f7e5881c59cd523beChris Craik            0.0f);
44265cd612face362d054a85d0f7e5881c59cd523beChris Craik    AlphaVertex::set(&buffer[postCapIndex + 3],
44365cd612face362d054a85d0f7e5881c59cd523beChris Craik            referencePoint.x - innerOffset.x - capAAOffset.x,
44465cd612face362d054a85d0f7e5881c59cd523beChris Craik            referencePoint.y - innerOffset.y - capAAOffset.y,
44565cd612face362d054a85d0f7e5881c59cd523beChris Craik            paintInfo.maxAlpha);
44665cd612face362d054a85d0f7e5881c59cd523beChris Craik
44765cd612face362d054a85d0f7e5881c59cd523beChris Craik    if (isRound) {
44865cd612face362d054a85d0f7e5881c59cd523beChris Craik        const float dTheta = PI / (extra + 1);
44965cd612face362d054a85d0f7e5881c59cd523beChris Craik        const float radialScale = 2.0f / (1 + cos(dTheta));
45065cd612face362d054a85d0f7e5881c59cd523beChris Craik        float theta = atan2(normal.y, normal.x);
45165cd612face362d054a85d0f7e5881c59cd523beChris Craik        int capPerimIndex = capIndex + 2;
45265cd612face362d054a85d0f7e5881c59cd523beChris Craik
45365cd612face362d054a85d0f7e5881c59cd523beChris Craik        for (int i = 0; i < extra; i++) {
45465cd612face362d054a85d0f7e5881c59cd523beChris Craik            theta += dTheta;
45565cd612face362d054a85d0f7e5881c59cd523beChris Craik
45665cd612face362d054a85d0f7e5881c59cd523beChris Craik            vec2 radialOffset(cos(theta), sin(theta));
45765cd612face362d054a85d0f7e5881c59cd523beChris Craik
45865cd612face362d054a85d0f7e5881c59cd523beChris Craik            // scale to compensate for pinching at sharp angles, see totalOffsetFromNormals()
45965cd612face362d054a85d0f7e5881c59cd523beChris Craik            radialOffset *= radialScale;
46065cd612face362d054a85d0f7e5881c59cd523beChris Craik
46165cd612face362d054a85d0f7e5881c59cd523beChris Craik            AAOffset = paintInfo.deriveAAOffset(radialOffset);
46265cd612face362d054a85d0f7e5881c59cd523beChris Craik            paintInfo.scaleOffsetForStrokeWidth(radialOffset);
46365cd612face362d054a85d0f7e5881c59cd523beChris Craik            AlphaVertex::set(&buffer[capPerimIndex++],
46465cd612face362d054a85d0f7e5881c59cd523beChris Craik                    referencePoint.x + radialOffset.x + AAOffset.x,
46565cd612face362d054a85d0f7e5881c59cd523beChris Craik                    referencePoint.y + radialOffset.y + AAOffset.y,
46665cd612face362d054a85d0f7e5881c59cd523beChris Craik                    0.0f);
46765cd612face362d054a85d0f7e5881c59cd523beChris Craik            AlphaVertex::set(&buffer[capPerimIndex++],
46865cd612face362d054a85d0f7e5881c59cd523beChris Craik                    referencePoint.x + radialOffset.x - AAOffset.x,
46965cd612face362d054a85d0f7e5881c59cd523beChris Craik                    referencePoint.y + radialOffset.y - AAOffset.y,
47065cd612face362d054a85d0f7e5881c59cd523beChris Craik                    paintInfo.maxAlpha);
47165cd612face362d054a85d0f7e5881c59cd523beChris Craik
47265cd612face362d054a85d0f7e5881c59cd523beChris Craik            if (isFirst && i == extra - extraOffset) {
47365cd612face362d054a85d0f7e5881c59cd523beChris Craik                //copy most recent two points to first two points
47465cd612face362d054a85d0f7e5881c59cd523beChris Craik                copyAlphaVertex(&buffer[0], &buffer[capPerimIndex - 2]);
47565cd612face362d054a85d0f7e5881c59cd523beChris Craik                copyAlphaVertex(&buffer[1], &buffer[capPerimIndex - 1]);
47665cd612face362d054a85d0f7e5881c59cd523beChris Craik
47765cd612face362d054a85d0f7e5881c59cd523beChris Craik                capPerimIndex = 2; // start writing the rest of the round cap at index 2
47865cd612face362d054a85d0f7e5881c59cd523beChris Craik            }
47965cd612face362d054a85d0f7e5881c59cd523beChris Craik        }
48065cd612face362d054a85d0f7e5881c59cd523beChris Craik
48165cd612face362d054a85d0f7e5881c59cd523beChris Craik        if (isFirst) {
48265cd612face362d054a85d0f7e5881c59cd523beChris Craik            const int startCapFillIndex = capIndex + 2 * (extra - extraOffset) + 4;
48365cd612face362d054a85d0f7e5881c59cd523beChris Craik            int capFillIndex = startCapFillIndex;
48465cd612face362d054a85d0f7e5881c59cd523beChris Craik            for (int i = 0; i < extra + 2; i += 2) {
48565cd612face362d054a85d0f7e5881c59cd523beChris Craik                copyAlphaVertex(&buffer[capFillIndex++], &buffer[1 + i]);
48665cd612face362d054a85d0f7e5881c59cd523beChris Craik                // TODO: to support odd numbers of divisions, break here on the last iteration
48765cd612face362d054a85d0f7e5881c59cd523beChris Craik                copyAlphaVertex(&buffer[capFillIndex++], &buffer[startCapFillIndex - 3 - i]);
48865cd612face362d054a85d0f7e5881c59cd523beChris Craik            }
48965cd612face362d054a85d0f7e5881c59cd523beChris Craik        } else {
49065cd612face362d054a85d0f7e5881c59cd523beChris Craik            int capFillIndex = 6 * vertices.size() + 2 + 6 * extra - (extra + 2);
49165cd612face362d054a85d0f7e5881c59cd523beChris Craik            for (int i = 0; i < extra + 2; i += 2) {
49265cd612face362d054a85d0f7e5881c59cd523beChris Craik                copyAlphaVertex(&buffer[capFillIndex++], &buffer[capIndex + 1 + i]);
49365cd612face362d054a85d0f7e5881c59cd523beChris Craik                // TODO: to support odd numbers of divisions, break here on the last iteration
49465cd612face362d054a85d0f7e5881c59cd523beChris Craik                copyAlphaVertex(&buffer[capFillIndex++], &buffer[capIndex + 3 + 2 * extra - i]);
49565cd612face362d054a85d0f7e5881c59cd523beChris Craik            }
49665cd612face362d054a85d0f7e5881c59cd523beChris Craik        }
49765cd612face362d054a85d0f7e5881c59cd523beChris Craik        return;
49865cd612face362d054a85d0f7e5881c59cd523beChris Craik    }
49965cd612face362d054a85d0f7e5881c59cd523beChris Craik    if (isFirst) {
50065cd612face362d054a85d0f7e5881c59cd523beChris Craik        copyAlphaVertex(&buffer[0], &buffer[postCapIndex + 2]);
50165cd612face362d054a85d0f7e5881c59cd523beChris Craik        copyAlphaVertex(&buffer[1], &buffer[postCapIndex + 3]);
50265cd612face362d054a85d0f7e5881c59cd523beChris Craik        copyAlphaVertex(&buffer[postCapIndex + 4], &buffer[1]); // degenerate tris (the only two!)
50365cd612face362d054a85d0f7e5881c59cd523beChris Craik        copyAlphaVertex(&buffer[postCapIndex + 5], &buffer[postCapIndex + 1]);
50465cd612face362d054a85d0f7e5881c59cd523beChris Craik    } else {
50565cd612face362d054a85d0f7e5881c59cd523beChris Craik        copyAlphaVertex(&buffer[6 * vertices.size()], &buffer[postCapIndex + 1]);
50665cd612face362d054a85d0f7e5881c59cd523beChris Craik        copyAlphaVertex(&buffer[6 * vertices.size() + 1], &buffer[postCapIndex + 3]);
50765cd612face362d054a85d0f7e5881c59cd523beChris Craik    }
50865cd612face362d054a85d0f7e5881c59cd523beChris Craik}
50965cd612face362d054a85d0f7e5881c59cd523beChris Craik
51065cd612face362d054a85d0f7e5881c59cd523beChris Craik/*
51165cd612face362d054a85d0f7e5881c59cd523beChris Craikthe geometry for an aa, capped stroke consists of the following:
51265cd612face362d054a85d0f7e5881c59cd523beChris Craik
51365cd612face362d054a85d0f7e5881c59cd523beChris Craik       # vertices       |    function
51465cd612face362d054a85d0f7e5881c59cd523beChris Craik----------------------------------------------------------------------
51565cd612face362d054a85d0f7e5881c59cd523beChris Craika) 2                    | Start AA perimeter
51665cd612face362d054a85d0f7e5881c59cd523beChris Craikb) 2, 2 * roundDivOff   | First half of begin cap's perimeter
51765cd612face362d054a85d0f7e5881c59cd523beChris Craik                        |
51865cd612face362d054a85d0f7e5881c59cd523beChris Craik   2 * middlePts        | 'Outer' or 'Top' AA perimeter half (between caps)
51965cd612face362d054a85d0f7e5881c59cd523beChris Craik                        |
52065cd612face362d054a85d0f7e5881c59cd523beChris Craika) 4                    | End cap's
52165cd612face362d054a85d0f7e5881c59cd523beChris Craikb) 2, 2 * roundDivs, 2  |    AA perimeter
52265cd612face362d054a85d0f7e5881c59cd523beChris Craik                        |
52365cd612face362d054a85d0f7e5881c59cd523beChris Craik   2 * middlePts        | 'Inner' or 'bottom' AA perimeter half
52465cd612face362d054a85d0f7e5881c59cd523beChris Craik                        |
52565cd612face362d054a85d0f7e5881c59cd523beChris Craika) 6                    | Begin cap's perimeter
52665cd612face362d054a85d0f7e5881c59cd523beChris Craikb) 2, 2*(rD - rDO + 1), | Last half of begin cap's perimeter
52765cd612face362d054a85d0f7e5881c59cd523beChris Craik       roundDivs, 2     |
52865cd612face362d054a85d0f7e5881c59cd523beChris Craik                        |
52965cd612face362d054a85d0f7e5881c59cd523beChris Craik   2 * middlePts        | Stroke's full opacity center strip
53065cd612face362d054a85d0f7e5881c59cd523beChris Craik                        |
53165cd612face362d054a85d0f7e5881c59cd523beChris Craika) 2                    | end stroke
53265cd612face362d054a85d0f7e5881c59cd523beChris Craikb) 2, roundDivs         |    (and end cap fill, for round)
53365cd612face362d054a85d0f7e5881c59cd523beChris Craik
53465cd612face362d054a85d0f7e5881c59cd523beChris CraikNotes:
53565cd612face362d054a85d0f7e5881c59cd523beChris Craik* rows starting with 'a)' denote the Butt or Square cap vertex use, 'b)' denote Round
53665cd612face362d054a85d0f7e5881c59cd523beChris Craik
53765cd612face362d054a85d0f7e5881c59cd523beChris Craik* 'middlePts' is (number of points in the unclosed input vertex list, minus 2) times two
53865cd612face362d054a85d0f7e5881c59cd523beChris Craik
53965cd612face362d054a85d0f7e5881c59cd523beChris Craik* 'roundDivs' or 'rD' is the number of extra vertices (beyond the minimum of 2) that define the
54065cd612face362d054a85d0f7e5881c59cd523beChris Craik        round cap's shape, and is at least two. This will increase with cap size to sufficiently
54165cd612face362d054a85d0f7e5881c59cd523beChris Craik        define the cap's level of tessellation.
54265cd612face362d054a85d0f7e5881c59cd523beChris Craik
54365cd612face362d054a85d0f7e5881c59cd523beChris Craik* 'roundDivOffset' or 'rDO' is the point about halfway along the start cap's round perimeter, where
54465cd612face362d054a85d0f7e5881c59cd523beChris Craik        the stream of vertices for the AA perimeter starts. By starting and ending the perimeter at
54565cd612face362d054a85d0f7e5881c59cd523beChris Craik        this offset, the fill of the stroke is drawn from this point with minimal extra vertices.
54665cd612face362d054a85d0f7e5881c59cd523beChris Craik
54765cd612face362d054a85d0f7e5881c59cd523beChris CraikThis means the outer perimeter starts at:
54865cd612face362d054a85d0f7e5881c59cd523beChris Craik    outerIndex = (2) OR (2 + 2 * roundDivOff)
54965cd612face362d054a85d0f7e5881c59cd523beChris Craikthe inner perimeter (since it is filled in reverse) starts at:
55065cd612face362d054a85d0f7e5881c59cd523beChris Craik    innerIndex = outerIndex + (4 * middlePts) + ((4) OR (4 + 2 * roundDivs)) - 1
55165cd612face362d054a85d0f7e5881c59cd523beChris Craikthe stroke starts at:
55265cd612face362d054a85d0f7e5881c59cd523beChris Craik    strokeIndex = innerIndex + 1 + ((6) OR (6 + 3 * roundDivs - 2 * roundDivOffset))
55365cd612face362d054a85d0f7e5881c59cd523beChris Craik
55465cd612face362d054a85d0f7e5881c59cd523beChris CraikThe total needed allocated space is either:
55565cd612face362d054a85d0f7e5881c59cd523beChris Craik    2 + 4 + 6 + 2 + 3 * (2 * middlePts) = 14 + 6 * middlePts = 2 + 6 * pts
55665cd612face362d054a85d0f7e5881c59cd523beChris Craikor, for rounded caps:
55765cd612face362d054a85d0f7e5881c59cd523beChris Craik    (2 + 2 * rDO) + (4 + 2 * rD) + (2 * (rD - rDO + 1)
55865cd612face362d054a85d0f7e5881c59cd523beChris Craik            + roundDivs + 4) + (2 + roundDivs) + 3 * (2 * middlePts)
55965cd612face362d054a85d0f7e5881c59cd523beChris Craik    = 14 + 6 * middlePts + 6 * roundDivs
56065cd612face362d054a85d0f7e5881c59cd523beChris Craik    = 2 + 6 * pts + 6 * roundDivs
56165cd612face362d054a85d0f7e5881c59cd523beChris Craik */
56265cd612face362d054a85d0f7e5881c59cd523beChris Craikvoid getStrokeVerticesFromUnclosedVerticesAA(const PaintInfo& paintInfo,
56365cd612face362d054a85d0f7e5881c59cd523beChris Craik        const Vector<Vertex>& vertices, VertexBuffer& vertexBuffer) {
56465cd612face362d054a85d0f7e5881c59cd523beChris Craik
56565cd612face362d054a85d0f7e5881c59cd523beChris Craik    const int extra = paintInfo.capExtraDivisions();
56665cd612face362d054a85d0f7e5881c59cd523beChris Craik    const int allocSize = 6 * vertices.size() + 2 + 6 * extra;
56765cd612face362d054a85d0f7e5881c59cd523beChris Craik
56865cd612face362d054a85d0f7e5881c59cd523beChris Craik    AlphaVertex* buffer = vertexBuffer.alloc<AlphaVertex>(allocSize);
56965cd612face362d054a85d0f7e5881c59cd523beChris Craik
57065cd612face362d054a85d0f7e5881c59cd523beChris Craik    const int extraOffset = (extra + 1) / 2;
57165cd612face362d054a85d0f7e5881c59cd523beChris Craik    int offset = 2 * (vertices.size() - 2);
57265cd612face362d054a85d0f7e5881c59cd523beChris Craik    // there is no outer/inner here, using them for consistency with below approach
57365cd612face362d054a85d0f7e5881c59cd523beChris Craik    int currentAAOuterIndex = 2 + 2 * extraOffset;
57465cd612face362d054a85d0f7e5881c59cd523beChris Craik    int currentAAInnerIndex = currentAAOuterIndex + (2 * offset) + 3 + (2 * extra);
57565cd612face362d054a85d0f7e5881c59cd523beChris Craik    int currentStrokeIndex = currentAAInnerIndex + 7 + (3 * extra - 2 * extraOffset);
57665cd612face362d054a85d0f7e5881c59cd523beChris Craik
57765cd612face362d054a85d0f7e5881c59cd523beChris Craik    const Vertex* last = &(vertices[0]);
57865cd612face362d054a85d0f7e5881c59cd523beChris Craik    const Vertex* current = &(vertices[1]);
57965cd612face362d054a85d0f7e5881c59cd523beChris Craik    vec2 lastNormal(current->position[1] - last->position[1],
58065cd612face362d054a85d0f7e5881c59cd523beChris Craik            last->position[0] - current->position[0]);
58165cd612face362d054a85d0f7e5881c59cd523beChris Craik    lastNormal.normalize();
58265cd612face362d054a85d0f7e5881c59cd523beChris Craik
58365cd612face362d054a85d0f7e5881c59cd523beChris Craik    // TODO: use normal from bezier traversal for cap, instead of from vertices
58465cd612face362d054a85d0f7e5881c59cd523beChris Craik    storeCapAA(paintInfo, vertices, buffer, true, lastNormal, offset);
58565cd612face362d054a85d0f7e5881c59cd523beChris Craik
58665cd612face362d054a85d0f7e5881c59cd523beChris Craik    for (unsigned int i = 1; i < vertices.size() - 1; i++) {
58765cd612face362d054a85d0f7e5881c59cd523beChris Craik        const Vertex* next = &(vertices[i + 1]);
58865cd612face362d054a85d0f7e5881c59cd523beChris Craik        vec2 nextNormal(next->position[1] - current->position[1],
58965cd612face362d054a85d0f7e5881c59cd523beChris Craik                current->position[0] - next->position[0]);
59065cd612face362d054a85d0f7e5881c59cd523beChris Craik        nextNormal.normalize();
59165cd612face362d054a85d0f7e5881c59cd523beChris Craik
59265cd612face362d054a85d0f7e5881c59cd523beChris Craik        vec2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
59365cd612face362d054a85d0f7e5881c59cd523beChris Craik        vec2 AAOffset = paintInfo.deriveAAOffset(totalOffset);
59465cd612face362d054a85d0f7e5881c59cd523beChris Craik
59565cd612face362d054a85d0f7e5881c59cd523beChris Craik        vec2 innerOffset = totalOffset;
59665cd612face362d054a85d0f7e5881c59cd523beChris Craik        paintInfo.scaleOffsetForStrokeWidth(innerOffset);
59765cd612face362d054a85d0f7e5881c59cd523beChris Craik        vec2 outerOffset = innerOffset + AAOffset;
59865cd612face362d054a85d0f7e5881c59cd523beChris Craik        innerOffset -= AAOffset;
59965cd612face362d054a85d0f7e5881c59cd523beChris Craik
60065cd612face362d054a85d0f7e5881c59cd523beChris Craik        AlphaVertex::set(&buffer[currentAAOuterIndex++],
60165cd612face362d054a85d0f7e5881c59cd523beChris Craik                current->position[0] + outerOffset.x,
60265cd612face362d054a85d0f7e5881c59cd523beChris Craik                current->position[1] + outerOffset.y,
60365cd612face362d054a85d0f7e5881c59cd523beChris Craik                0.0f);
60465cd612face362d054a85d0f7e5881c59cd523beChris Craik        AlphaVertex::set(&buffer[currentAAOuterIndex++],
60565cd612face362d054a85d0f7e5881c59cd523beChris Craik                current->position[0] + innerOffset.x,
60665cd612face362d054a85d0f7e5881c59cd523beChris Craik                current->position[1] + innerOffset.y,
60765cd612face362d054a85d0f7e5881c59cd523beChris Craik                paintInfo.maxAlpha);
60865cd612face362d054a85d0f7e5881c59cd523beChris Craik
60965cd612face362d054a85d0f7e5881c59cd523beChris Craik        AlphaVertex::set(&buffer[currentStrokeIndex++],
61065cd612face362d054a85d0f7e5881c59cd523beChris Craik                current->position[0] + innerOffset.x,
61165cd612face362d054a85d0f7e5881c59cd523beChris Craik                current->position[1] + innerOffset.y,
61265cd612face362d054a85d0f7e5881c59cd523beChris Craik                paintInfo.maxAlpha);
61365cd612face362d054a85d0f7e5881c59cd523beChris Craik        AlphaVertex::set(&buffer[currentStrokeIndex++],
61465cd612face362d054a85d0f7e5881c59cd523beChris Craik                current->position[0] - innerOffset.x,
61565cd612face362d054a85d0f7e5881c59cd523beChris Craik                current->position[1] - innerOffset.y,
61665cd612face362d054a85d0f7e5881c59cd523beChris Craik                paintInfo.maxAlpha);
61765cd612face362d054a85d0f7e5881c59cd523beChris Craik
61865cd612face362d054a85d0f7e5881c59cd523beChris Craik        AlphaVertex::set(&buffer[currentAAInnerIndex--],
61965cd612face362d054a85d0f7e5881c59cd523beChris Craik                current->position[0] - innerOffset.x,
62065cd612face362d054a85d0f7e5881c59cd523beChris Craik                current->position[1] - innerOffset.y,
62165cd612face362d054a85d0f7e5881c59cd523beChris Craik                paintInfo.maxAlpha);
62265cd612face362d054a85d0f7e5881c59cd523beChris Craik        AlphaVertex::set(&buffer[currentAAInnerIndex--],
62365cd612face362d054a85d0f7e5881c59cd523beChris Craik                current->position[0] - outerOffset.x,
62465cd612face362d054a85d0f7e5881c59cd523beChris Craik                current->position[1] - outerOffset.y,
62565cd612face362d054a85d0f7e5881c59cd523beChris Craik                0.0f);
62665cd612face362d054a85d0f7e5881c59cd523beChris Craik
62765cd612face362d054a85d0f7e5881c59cd523beChris Craik        current = next;
62865cd612face362d054a85d0f7e5881c59cd523beChris Craik        lastNormal = nextNormal;
62965cd612face362d054a85d0f7e5881c59cd523beChris Craik    }
63065cd612face362d054a85d0f7e5881c59cd523beChris Craik
63165cd612face362d054a85d0f7e5881c59cd523beChris Craik    // TODO: use normal from bezier traversal for cap, instead of from vertices
63265cd612face362d054a85d0f7e5881c59cd523beChris Craik    storeCapAA(paintInfo, vertices, buffer, false, lastNormal, offset);
63365cd612face362d054a85d0f7e5881c59cd523beChris Craik
63465cd612face362d054a85d0f7e5881c59cd523beChris Craik    DEBUG_DUMP_ALPHA_BUFFER();
63565cd612face362d054a85d0f7e5881c59cd523beChris Craik}
63665cd612face362d054a85d0f7e5881c59cd523beChris Craik
63765cd612face362d054a85d0f7e5881c59cd523beChris Craik
63865cd612face362d054a85d0f7e5881c59cd523beChris Craikvoid getStrokeVerticesFromPerimeterAA(const PaintInfo& paintInfo, const Vector<Vertex>& perimeter,
63965cd612face362d054a85d0f7e5881c59cd523beChris Craik        VertexBuffer& vertexBuffer) {
64065cd612face362d054a85d0f7e5881c59cd523beChris Craik    AlphaVertex* buffer = vertexBuffer.alloc<AlphaVertex>(6 * perimeter.size() + 8);
64165cd612face362d054a85d0f7e5881c59cd523beChris Craik
64265cd612face362d054a85d0f7e5881c59cd523beChris Craik    int offset = 2 * perimeter.size() + 3;
64365cd612face362d054a85d0f7e5881c59cd523beChris Craik    int currentAAOuterIndex = 0;
64465cd612face362d054a85d0f7e5881c59cd523beChris Craik    int currentStrokeIndex = offset;
64565cd612face362d054a85d0f7e5881c59cd523beChris Craik    int currentAAInnerIndex = offset * 2;
64665cd612face362d054a85d0f7e5881c59cd523beChris Craik
64765cd612face362d054a85d0f7e5881c59cd523beChris Craik    const Vertex* last = &(perimeter[perimeter.size() - 1]);
64865cd612face362d054a85d0f7e5881c59cd523beChris Craik    const Vertex* current = &(perimeter[0]);
64965cd612face362d054a85d0f7e5881c59cd523beChris Craik    vec2 lastNormal(current->position[1] - last->position[1],
65065cd612face362d054a85d0f7e5881c59cd523beChris Craik            last->position[0] - current->position[0]);
65165cd612face362d054a85d0f7e5881c59cd523beChris Craik    lastNormal.normalize();
65265cd612face362d054a85d0f7e5881c59cd523beChris Craik    for (unsigned int i = 0; i < perimeter.size(); i++) {
65365cd612face362d054a85d0f7e5881c59cd523beChris Craik        const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]);
65465cd612face362d054a85d0f7e5881c59cd523beChris Craik        vec2 nextNormal(next->position[1] - current->position[1],
65565cd612face362d054a85d0f7e5881c59cd523beChris Craik                current->position[0] - next->position[0]);
65665cd612face362d054a85d0f7e5881c59cd523beChris Craik        nextNormal.normalize();
65765cd612face362d054a85d0f7e5881c59cd523beChris Craik
65865cd612face362d054a85d0f7e5881c59cd523beChris Craik        vec2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
65965cd612face362d054a85d0f7e5881c59cd523beChris Craik        vec2 AAOffset = paintInfo.deriveAAOffset(totalOffset);
66065cd612face362d054a85d0f7e5881c59cd523beChris Craik
66165cd612face362d054a85d0f7e5881c59cd523beChris Craik        vec2 innerOffset = totalOffset;
66265cd612face362d054a85d0f7e5881c59cd523beChris Craik        paintInfo.scaleOffsetForStrokeWidth(innerOffset);
66365cd612face362d054a85d0f7e5881c59cd523beChris Craik        vec2 outerOffset = innerOffset + AAOffset;
66465cd612face362d054a85d0f7e5881c59cd523beChris Craik        innerOffset -= AAOffset;
66565cd612face362d054a85d0f7e5881c59cd523beChris Craik
66665cd612face362d054a85d0f7e5881c59cd523beChris Craik        AlphaVertex::set(&buffer[currentAAOuterIndex++],
66765cd612face362d054a85d0f7e5881c59cd523beChris Craik                current->position[0] + outerOffset.x,
66865cd612face362d054a85d0f7e5881c59cd523beChris Craik                current->position[1] + outerOffset.y,
66965cd612face362d054a85d0f7e5881c59cd523beChris Craik                0.0f);
67065cd612face362d054a85d0f7e5881c59cd523beChris Craik        AlphaVertex::set(&buffer[currentAAOuterIndex++],
67165cd612face362d054a85d0f7e5881c59cd523beChris Craik                current->position[0] + innerOffset.x,
67265cd612face362d054a85d0f7e5881c59cd523beChris Craik                current->position[1] + innerOffset.y,
67365cd612face362d054a85d0f7e5881c59cd523beChris Craik                paintInfo.maxAlpha);
67465cd612face362d054a85d0f7e5881c59cd523beChris Craik
67565cd612face362d054a85d0f7e5881c59cd523beChris Craik        AlphaVertex::set(&buffer[currentStrokeIndex++],
67665cd612face362d054a85d0f7e5881c59cd523beChris Craik                current->position[0] + innerOffset.x,
67765cd612face362d054a85d0f7e5881c59cd523beChris Craik                current->position[1] + innerOffset.y,
67865cd612face362d054a85d0f7e5881c59cd523beChris Craik                paintInfo.maxAlpha);
67965cd612face362d054a85d0f7e5881c59cd523beChris Craik        AlphaVertex::set(&buffer[currentStrokeIndex++],
68065cd612face362d054a85d0f7e5881c59cd523beChris Craik                current->position[0] - innerOffset.x,
68165cd612face362d054a85d0f7e5881c59cd523beChris Craik                current->position[1] - innerOffset.y,
68265cd612face362d054a85d0f7e5881c59cd523beChris Craik                paintInfo.maxAlpha);
68365cd612face362d054a85d0f7e5881c59cd523beChris Craik
68465cd612face362d054a85d0f7e5881c59cd523beChris Craik        AlphaVertex::set(&buffer[currentAAInnerIndex++],
68565cd612face362d054a85d0f7e5881c59cd523beChris Craik                current->position[0] - innerOffset.x,
68665cd612face362d054a85d0f7e5881c59cd523beChris Craik                current->position[1] - innerOffset.y,
68765cd612face362d054a85d0f7e5881c59cd523beChris Craik                paintInfo.maxAlpha);
68865cd612face362d054a85d0f7e5881c59cd523beChris Craik        AlphaVertex::set(&buffer[currentAAInnerIndex++],
68965cd612face362d054a85d0f7e5881c59cd523beChris Craik                current->position[0] - outerOffset.x,
69065cd612face362d054a85d0f7e5881c59cd523beChris Craik                current->position[1] - outerOffset.y,
69165cd612face362d054a85d0f7e5881c59cd523beChris Craik                0.0f);
69265cd612face362d054a85d0f7e5881c59cd523beChris Craik
69365cd612face362d054a85d0f7e5881c59cd523beChris Craik        last = current;
69465cd612face362d054a85d0f7e5881c59cd523beChris Craik        current = next;
69565cd612face362d054a85d0f7e5881c59cd523beChris Craik        lastNormal = nextNormal;
69665cd612face362d054a85d0f7e5881c59cd523beChris Craik    }
69765cd612face362d054a85d0f7e5881c59cd523beChris Craik
69865cd612face362d054a85d0f7e5881c59cd523beChris Craik    // wrap each strip around to beginning, creating degenerate tris to bridge strips
69965cd612face362d054a85d0f7e5881c59cd523beChris Craik    copyAlphaVertex(&buffer[currentAAOuterIndex++], &buffer[0]);
70065cd612face362d054a85d0f7e5881c59cd523beChris Craik    copyAlphaVertex(&buffer[currentAAOuterIndex++], &buffer[1]);
70165cd612face362d054a85d0f7e5881c59cd523beChris Craik    copyAlphaVertex(&buffer[currentAAOuterIndex++], &buffer[1]);
70265cd612face362d054a85d0f7e5881c59cd523beChris Craik
70365cd612face362d054a85d0f7e5881c59cd523beChris Craik    copyAlphaVertex(&buffer[currentStrokeIndex++], &buffer[offset]);
70465cd612face362d054a85d0f7e5881c59cd523beChris Craik    copyAlphaVertex(&buffer[currentStrokeIndex++], &buffer[offset + 1]);
70565cd612face362d054a85d0f7e5881c59cd523beChris Craik    copyAlphaVertex(&buffer[currentStrokeIndex++], &buffer[offset + 1]);
70665cd612face362d054a85d0f7e5881c59cd523beChris Craik
70765cd612face362d054a85d0f7e5881c59cd523beChris Craik    copyAlphaVertex(&buffer[currentAAInnerIndex++], &buffer[2 * offset]);
70865cd612face362d054a85d0f7e5881c59cd523beChris Craik    copyAlphaVertex(&buffer[currentAAInnerIndex++], &buffer[2 * offset + 1]);
70965cd612face362d054a85d0f7e5881c59cd523beChris Craik    // don't need to create last degenerate tri
71065cd612face362d054a85d0f7e5881c59cd523beChris Craik
71165cd612face362d054a85d0f7e5881c59cd523beChris Craik    DEBUG_DUMP_ALPHA_BUFFER();
71265cd612face362d054a85d0f7e5881c59cd523beChris Craik}
71365cd612face362d054a85d0f7e5881c59cd523beChris Craik
71465cd612face362d054a85d0f7e5881c59cd523beChris Craikvoid PathTessellator::tessellatePath(const SkPath &path, const SkPaint* paint,
71565cd612face362d054a85d0f7e5881c59cd523beChris Craik        const mat4 *transform, VertexBuffer& vertexBuffer) {
71665cd612face362d054a85d0f7e5881c59cd523beChris Craik    ATRACE_CALL();
71765cd612face362d054a85d0f7e5881c59cd523beChris Craik
71865cd612face362d054a85d0f7e5881c59cd523beChris Craik    const PaintInfo paintInfo(paint, transform);
71965cd612face362d054a85d0f7e5881c59cd523beChris Craik
72065cd612face362d054a85d0f7e5881c59cd523beChris Craik    Vector<Vertex> tempVertices;
72165cd612face362d054a85d0f7e5881c59cd523beChris Craik    float threshInvScaleX = paintInfo.inverseScaleX;
72265cd612face362d054a85d0f7e5881c59cd523beChris Craik    float threshInvScaleY = paintInfo.inverseScaleY;
72365cd612face362d054a85d0f7e5881c59cd523beChris Craik    if (paintInfo.style == SkPaint::kStroke_Style) {
72465cd612face362d054a85d0f7e5881c59cd523beChris Craik        // alter the bezier recursion threshold values we calculate in order to compensate for
72565cd612face362d054a85d0f7e5881c59cd523beChris Craik        // expansion done after the path vertices are found
72665cd612face362d054a85d0f7e5881c59cd523beChris Craik        SkRect bounds = path.getBounds();
72765cd612face362d054a85d0f7e5881c59cd523beChris Craik        if (!bounds.isEmpty()) {
72865cd612face362d054a85d0f7e5881c59cd523beChris Craik            threshInvScaleX *= bounds.width() / (bounds.width() + paint->getStrokeWidth());
72965cd612face362d054a85d0f7e5881c59cd523beChris Craik            threshInvScaleY *= bounds.height() / (bounds.height() + paint->getStrokeWidth());
73065cd612face362d054a85d0f7e5881c59cd523beChris Craik        }
73165cd612face362d054a85d0f7e5881c59cd523beChris Craik    }
73265cd612face362d054a85d0f7e5881c59cd523beChris Craik
73365cd612face362d054a85d0f7e5881c59cd523beChris Craik    // force close if we're filling the path, since fill path expects closed perimeter.
73465cd612face362d054a85d0f7e5881c59cd523beChris Craik    bool forceClose = paintInfo.style != SkPaint::kStroke_Style;
73565cd612face362d054a85d0f7e5881c59cd523beChris Craik    bool wasClosed = approximatePathOutlineVertices(path, forceClose,
73665cd612face362d054a85d0f7e5881c59cd523beChris Craik            threshInvScaleX * threshInvScaleX, threshInvScaleY * threshInvScaleY, tempVertices);
73765cd612face362d054a85d0f7e5881c59cd523beChris Craik
73865cd612face362d054a85d0f7e5881c59cd523beChris Craik    if (!tempVertices.size()) {
73965cd612face362d054a85d0f7e5881c59cd523beChris Craik        // path was empty, return without allocating vertex buffer
74065cd612face362d054a85d0f7e5881c59cd523beChris Craik        return;
74165cd612face362d054a85d0f7e5881c59cd523beChris Craik    }
74265cd612face362d054a85d0f7e5881c59cd523beChris Craik
74365cd612face362d054a85d0f7e5881c59cd523beChris Craik#if VERTEX_DEBUG
74465cd612face362d054a85d0f7e5881c59cd523beChris Craik    for (unsigned int i = 0; i < tempVertices.size(); i++) {
74565cd612face362d054a85d0f7e5881c59cd523beChris Craik        ALOGD("orig path: point at %f %f",
74665cd612face362d054a85d0f7e5881c59cd523beChris Craik                tempVertices[i].position[0], tempVertices[i].position[1]);
74765cd612face362d054a85d0f7e5881c59cd523beChris Craik    }
74865cd612face362d054a85d0f7e5881c59cd523beChris Craik#endif
74965cd612face362d054a85d0f7e5881c59cd523beChris Craik
75065cd612face362d054a85d0f7e5881c59cd523beChris Craik    if (paintInfo.style == SkPaint::kStroke_Style) {
75165cd612face362d054a85d0f7e5881c59cd523beChris Craik        if (!paintInfo.isAA) {
75265cd612face362d054a85d0f7e5881c59cd523beChris Craik            if (wasClosed) {
75365cd612face362d054a85d0f7e5881c59cd523beChris Craik                getStrokeVerticesFromPerimeter(paintInfo, tempVertices, vertexBuffer);
75465cd612face362d054a85d0f7e5881c59cd523beChris Craik            } else {
75565cd612face362d054a85d0f7e5881c59cd523beChris Craik                getStrokeVerticesFromUnclosedVertices(paintInfo, tempVertices, vertexBuffer);
75665cd612face362d054a85d0f7e5881c59cd523beChris Craik            }
75765cd612face362d054a85d0f7e5881c59cd523beChris Craik
75865cd612face362d054a85d0f7e5881c59cd523beChris Craik        } else {
75965cd612face362d054a85d0f7e5881c59cd523beChris Craik            if (wasClosed) {
76065cd612face362d054a85d0f7e5881c59cd523beChris Craik                getStrokeVerticesFromPerimeterAA(paintInfo, tempVertices, vertexBuffer);
76165cd612face362d054a85d0f7e5881c59cd523beChris Craik            } else {
76265cd612face362d054a85d0f7e5881c59cd523beChris Craik                getStrokeVerticesFromUnclosedVerticesAA(paintInfo, tempVertices, vertexBuffer);
76365cd612face362d054a85d0f7e5881c59cd523beChris Craik            }
76465cd612face362d054a85d0f7e5881c59cd523beChris Craik        }
76565cd612face362d054a85d0f7e5881c59cd523beChris Craik    } else {
76665cd612face362d054a85d0f7e5881c59cd523beChris Craik        // For kStrokeAndFill style, the path should be adjusted externally.
76765cd612face362d054a85d0f7e5881c59cd523beChris Craik        // It will be treated as a fill here.
76865cd612face362d054a85d0f7e5881c59cd523beChris Craik        if (!paintInfo.isAA) {
76965cd612face362d054a85d0f7e5881c59cd523beChris Craik            getFillVerticesFromPerimeter(tempVertices, vertexBuffer);
77065cd612face362d054a85d0f7e5881c59cd523beChris Craik        } else {
77165cd612face362d054a85d0f7e5881c59cd523beChris Craik            getFillVerticesFromPerimeterAA(paintInfo, tempVertices, vertexBuffer);
77265cd612face362d054a85d0f7e5881c59cd523beChris Craik        }
77365cd612face362d054a85d0f7e5881c59cd523beChris Craik    }
77465cd612face362d054a85d0f7e5881c59cd523beChris Craik}
77565cd612face362d054a85d0f7e5881c59cd523beChris Craik
7766d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craikstatic void expandRectToCoverVertex(SkRect& rect, float x, float y) {
7776d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik    rect.fLeft = fminf(rect.fLeft, x);
7786d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik    rect.fTop = fminf(rect.fTop, y);
7796d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik    rect.fRight = fmaxf(rect.fRight, x);
7806d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik    rect.fBottom = fmaxf(rect.fBottom, y);
7816d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik}
78265cd612face362d054a85d0f7e5881c59cd523beChris Craikstatic void expandRectToCoverVertex(SkRect& rect, const Vertex& vertex) {
7836d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik    expandRectToCoverVertex(rect, vertex.position[0], vertex.position[1]);
7846d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik}
7856d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik
7866d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craiktemplate <class TYPE>
7876d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craikstatic void instanceVertices(VertexBuffer& srcBuffer, VertexBuffer& dstBuffer,
7886d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik        const float* points, int count, SkRect& bounds) {
7896d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik    bounds.set(points[0], points[1], points[0], points[1]);
7906d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik
7916d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik    int numPoints = count / 2;
7926d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik    int verticesPerPoint = srcBuffer.getVertexCount();
7936d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik    dstBuffer.alloc<TYPE>(numPoints * verticesPerPoint + (numPoints - 1) * 2);
7946d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik
7956d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik    for (int i = 0; i < count; i += 2) {
7966d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik        expandRectToCoverVertex(bounds, points[i + 0], points[i + 1]);
7976d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik        dstBuffer.copyInto<TYPE>(srcBuffer, points[i + 0], points[i + 1]);
7986d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik    }
7996d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik    dstBuffer.createDegenerateSeparators<TYPE>(verticesPerPoint);
8006d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik}
8016d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik
8026d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craikvoid PathTessellator::tessellatePoints(const float* points, int count, SkPaint* paint,
8036d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik        const mat4* transform, SkRect& bounds, VertexBuffer& vertexBuffer) {
8046d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik    const PaintInfo paintInfo(paint, transform);
8056d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik
8066d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik    // determine point shape
8076d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik    SkPath path;
8086d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik    float radius = paintInfo.halfStrokeWidth;
8096d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik    if (radius == 0.0f) radius = 0.25f;
8106d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik
8116d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik    if (paintInfo.cap == SkPaint::kRound_Cap) {
8126d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik        path.addCircle(0, 0, radius);
8136d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik    } else {
8146d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik        path.addRect(-radius, -radius, radius, radius);
8156d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik    }
8166d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik
8176d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik    // calculate outline
8186d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik    Vector<Vertex> outlineVertices;
8196d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik    approximatePathOutlineVertices(path, true,
8206d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik            paintInfo.inverseScaleX * paintInfo.inverseScaleX,
8216d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik            paintInfo.inverseScaleY * paintInfo.inverseScaleY, outlineVertices);
8226d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik
8236d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik    if (!outlineVertices.size()) return;
8246d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik
8256d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik    // tessellate, then duplicate outline across points
8266d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik    int numPoints = count / 2;
8276d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik    VertexBuffer tempBuffer;
8286d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik    if (!paintInfo.isAA) {
8296d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik        getFillVerticesFromPerimeter(outlineVertices, tempBuffer);
8306d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik        instanceVertices<Vertex>(tempBuffer, vertexBuffer, points, count, bounds);
8316d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik    } else {
8326d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik        getFillVerticesFromPerimeterAA(paintInfo, outlineVertices, tempBuffer);
8336d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik        instanceVertices<AlphaVertex>(tempBuffer, vertexBuffer, points, count, bounds);
8346d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik    }
8356d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik
8366d29c8d5218cac0fb35f3b7c253f2bdebd44f15aChris Craik    expandBoundsForStroke(bounds, paint, true); // force-expand bounds to incorporate stroke
83765cd612face362d054a85d0f7e5881c59cd523beChris Craik}
83865cd612face362d054a85d0f7e5881c59cd523beChris Craik
83965cd612face362d054a85d0f7e5881c59cd523beChris Craikvoid PathTessellator::tessellateLines(const float* points, int count, SkPaint* paint,
84065cd612face362d054a85d0f7e5881c59cd523beChris Craik        const mat4* transform, SkRect& bounds, VertexBuffer& vertexBuffer) {
84165cd612face362d054a85d0f7e5881c59cd523beChris Craik    ATRACE_CALL();
84265cd612face362d054a85d0f7e5881c59cd523beChris Craik    const PaintInfo paintInfo(paint, transform);
84365cd612face362d054a85d0f7e5881c59cd523beChris Craik
84465cd612face362d054a85d0f7e5881c59cd523beChris Craik    const int extra = paintInfo.capExtraDivisions();
84565cd612face362d054a85d0f7e5881c59cd523beChris Craik    int numLines = count / 4;
84665cd612face362d054a85d0f7e5881c59cd523beChris Craik    int lineAllocSize;
84765cd612face362d054a85d0f7e5881c59cd523beChris Craik    // pre-allocate space for lines in the buffer, and degenerate tris in between
84865cd612face362d054a85d0f7e5881c59cd523beChris Craik    if (paintInfo.isAA) {
84965cd612face362d054a85d0f7e5881c59cd523beChris Craik        lineAllocSize = 6 * (2) + 2 + 6 * extra;
85065cd612face362d054a85d0f7e5881c59cd523beChris Craik        vertexBuffer.alloc<AlphaVertex>(numLines * lineAllocSize + (numLines - 1) * 2);
85165cd612face362d054a85d0f7e5881c59cd523beChris Craik    } else {
85265cd612face362d054a85d0f7e5881c59cd523beChris Craik        lineAllocSize = 2 * ((2) + extra);
85365cd612face362d054a85d0f7e5881c59cd523beChris Craik        vertexBuffer.alloc<Vertex>(numLines * lineAllocSize + (numLines - 1) * 2);
85465cd612face362d054a85d0f7e5881c59cd523beChris Craik    }
85565cd612face362d054a85d0f7e5881c59cd523beChris Craik
85665cd612face362d054a85d0f7e5881c59cd523beChris Craik    Vector<Vertex> tempVertices;
85765cd612face362d054a85d0f7e5881c59cd523beChris Craik    tempVertices.push();
85865cd612face362d054a85d0f7e5881c59cd523beChris Craik    tempVertices.push();
85965cd612face362d054a85d0f7e5881c59cd523beChris Craik    Vertex* tempVerticesData = tempVertices.editArray();
86065cd612face362d054a85d0f7e5881c59cd523beChris Craik    bounds.set(points[0], points[1], points[0], points[1]);
86165cd612face362d054a85d0f7e5881c59cd523beChris Craik    for (int i = 0; i < count; i += 4) {
86265cd612face362d054a85d0f7e5881c59cd523beChris Craik        Vertex::set(&(tempVerticesData[0]), points[i + 0], points[i + 1]);
86365cd612face362d054a85d0f7e5881c59cd523beChris Craik        Vertex::set(&(tempVerticesData[1]), points[i + 2], points[i + 3]);
86465cd612face362d054a85d0f7e5881c59cd523beChris Craik
86565cd612face362d054a85d0f7e5881c59cd523beChris Craik        if (paintInfo.isAA) {
86665cd612face362d054a85d0f7e5881c59cd523beChris Craik            getStrokeVerticesFromUnclosedVerticesAA(paintInfo, tempVertices, vertexBuffer);
86765cd612face362d054a85d0f7e5881c59cd523beChris Craik        } else {
86865cd612face362d054a85d0f7e5881c59cd523beChris Craik            getStrokeVerticesFromUnclosedVertices(paintInfo, tempVertices, vertexBuffer);
86965cd612face362d054a85d0f7e5881c59cd523beChris Craik        }
87065cd612face362d054a85d0f7e5881c59cd523beChris Craik
87165cd612face362d054a85d0f7e5881c59cd523beChris Craik        // calculate bounds
87265cd612face362d054a85d0f7e5881c59cd523beChris Craik        expandRectToCoverVertex(bounds, tempVerticesData[0]);
87365cd612face362d054a85d0f7e5881c59cd523beChris Craik        expandRectToCoverVertex(bounds, tempVerticesData[1]);
87465cd612face362d054a85d0f7e5881c59cd523beChris Craik    }
87565cd612face362d054a85d0f7e5881c59cd523beChris Craik
87665cd612face362d054a85d0f7e5881c59cd523beChris Craik    expandBoundsForStroke(bounds, paint, true); // force-expand bounds to incorporate stroke
87765cd612face362d054a85d0f7e5881c59cd523beChris Craik
87865cd612face362d054a85d0f7e5881c59cd523beChris Craik    // since multiple objects tessellated into buffer, separate them with degen tris
87965cd612face362d054a85d0f7e5881c59cd523beChris Craik    if (paintInfo.isAA) {
88065cd612face362d054a85d0f7e5881c59cd523beChris Craik        vertexBuffer.createDegenerateSeparators<AlphaVertex>(lineAllocSize);
88165cd612face362d054a85d0f7e5881c59cd523beChris Craik    } else {
88265cd612face362d054a85d0f7e5881c59cd523beChris Craik        vertexBuffer.createDegenerateSeparators<Vertex>(lineAllocSize);
88365cd612face362d054a85d0f7e5881c59cd523beChris Craik    }
88465cd612face362d054a85d0f7e5881c59cd523beChris Craik}
88565cd612face362d054a85d0f7e5881c59cd523beChris Craik
88665cd612face362d054a85d0f7e5881c59cd523beChris Craik///////////////////////////////////////////////////////////////////////////////
88765cd612face362d054a85d0f7e5881c59cd523beChris Craik// Simple path line approximation
88865cd612face362d054a85d0f7e5881c59cd523beChris Craik///////////////////////////////////////////////////////////////////////////////
88965cd612face362d054a85d0f7e5881c59cd523beChris Craik
89065cd612face362d054a85d0f7e5881c59cd523beChris Craikvoid pushToVector(Vector<Vertex>& vertices, float x, float y) {
89165cd612face362d054a85d0f7e5881c59cd523beChris Craik    // TODO: make this not yuck
89265cd612face362d054a85d0f7e5881c59cd523beChris Craik    vertices.push();
89365cd612face362d054a85d0f7e5881c59cd523beChris Craik    Vertex* newVertex = &(vertices.editArray()[vertices.size() - 1]);
89465cd612face362d054a85d0f7e5881c59cd523beChris Craik    Vertex::set(newVertex, x, y);
89565cd612face362d054a85d0f7e5881c59cd523beChris Craik}
89665cd612face362d054a85d0f7e5881c59cd523beChris Craik
89765cd612face362d054a85d0f7e5881c59cd523beChris Craikbool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool forceClose,
89865cd612face362d054a85d0f7e5881c59cd523beChris Craik        float sqrInvScaleX, float sqrInvScaleY, Vector<Vertex>& outputVertices) {
89965cd612face362d054a85d0f7e5881c59cd523beChris Craik    ATRACE_CALL();
90065cd612face362d054a85d0f7e5881c59cd523beChris Craik
90165cd612face362d054a85d0f7e5881c59cd523beChris Craik    // TODO: to support joins other than sharp miter, join vertices should be labelled in the
90265cd612face362d054a85d0f7e5881c59cd523beChris Craik    // perimeter, or resolved into more vertices. Reconsider forceClose-ing in that case.
90365cd612face362d054a85d0f7e5881c59cd523beChris Craik    SkPath::Iter iter(path, forceClose);
90465cd612face362d054a85d0f7e5881c59cd523beChris Craik    SkPoint pts[4];
90565cd612face362d054a85d0f7e5881c59cd523beChris Craik    SkPath::Verb v;
90665cd612face362d054a85d0f7e5881c59cd523beChris Craik    while (SkPath::kDone_Verb != (v = iter.next(pts))) {
90765cd612face362d054a85d0f7e5881c59cd523beChris Craik            switch (v) {
90865cd612face362d054a85d0f7e5881c59cd523beChris Craik            case SkPath::kMove_Verb:
90965cd612face362d054a85d0f7e5881c59cd523beChris Craik                pushToVector(outputVertices, pts[0].x(), pts[0].y());
91065cd612face362d054a85d0f7e5881c59cd523beChris Craik                ALOGV("Move to pos %f %f", pts[0].x(), pts[0].y());
91165cd612face362d054a85d0f7e5881c59cd523beChris Craik                break;
91265cd612face362d054a85d0f7e5881c59cd523beChris Craik            case SkPath::kClose_Verb:
91365cd612face362d054a85d0f7e5881c59cd523beChris Craik                ALOGV("Close at pos %f %f", pts[0].x(), pts[0].y());
91465cd612face362d054a85d0f7e5881c59cd523beChris Craik                break;
91565cd612face362d054a85d0f7e5881c59cd523beChris Craik            case SkPath::kLine_Verb:
91665cd612face362d054a85d0f7e5881c59cd523beChris Craik                ALOGV("kLine_Verb %f %f -> %f %f", pts[0].x(), pts[0].y(), pts[1].x(), pts[1].y());
91765cd612face362d054a85d0f7e5881c59cd523beChris Craik                pushToVector(outputVertices, pts[1].x(), pts[1].y());
91865cd612face362d054a85d0f7e5881c59cd523beChris Craik                break;
91965cd612face362d054a85d0f7e5881c59cd523beChris Craik            case SkPath::kQuad_Verb:
92065cd612face362d054a85d0f7e5881c59cd523beChris Craik                ALOGV("kQuad_Verb");
92165cd612face362d054a85d0f7e5881c59cd523beChris Craik                recursiveQuadraticBezierVertices(
92265cd612face362d054a85d0f7e5881c59cd523beChris Craik                        pts[0].x(), pts[0].y(),
92365cd612face362d054a85d0f7e5881c59cd523beChris Craik                        pts[2].x(), pts[2].y(),
92465cd612face362d054a85d0f7e5881c59cd523beChris Craik                        pts[1].x(), pts[1].y(),
92565cd612face362d054a85d0f7e5881c59cd523beChris Craik                        sqrInvScaleX, sqrInvScaleY, outputVertices);
92665cd612face362d054a85d0f7e5881c59cd523beChris Craik                break;
92765cd612face362d054a85d0f7e5881c59cd523beChris Craik            case SkPath::kCubic_Verb:
92865cd612face362d054a85d0f7e5881c59cd523beChris Craik                ALOGV("kCubic_Verb");
92965cd612face362d054a85d0f7e5881c59cd523beChris Craik                recursiveCubicBezierVertices(
93065cd612face362d054a85d0f7e5881c59cd523beChris Craik                        pts[0].x(), pts[0].y(),
93165cd612face362d054a85d0f7e5881c59cd523beChris Craik                        pts[1].x(), pts[1].y(),
93265cd612face362d054a85d0f7e5881c59cd523beChris Craik                        pts[3].x(), pts[3].y(),
93365cd612face362d054a85d0f7e5881c59cd523beChris Craik                        pts[2].x(), pts[2].y(),
93465cd612face362d054a85d0f7e5881c59cd523beChris Craik                        sqrInvScaleX, sqrInvScaleY, outputVertices);
93565cd612face362d054a85d0f7e5881c59cd523beChris Craik                break;
93665cd612face362d054a85d0f7e5881c59cd523beChris Craik            default:
93765cd612face362d054a85d0f7e5881c59cd523beChris Craik                break;
93865cd612face362d054a85d0f7e5881c59cd523beChris Craik            }
93965cd612face362d054a85d0f7e5881c59cd523beChris Craik    }
94065cd612face362d054a85d0f7e5881c59cd523beChris Craik
94165cd612face362d054a85d0f7e5881c59cd523beChris Craik    int size = outputVertices.size();
94265cd612face362d054a85d0f7e5881c59cd523beChris Craik    if (size >= 2 && outputVertices[0].position[0] == outputVertices[size - 1].position[0] &&
94365cd612face362d054a85d0f7e5881c59cd523beChris Craik            outputVertices[0].position[1] == outputVertices[size - 1].position[1]) {
94465cd612face362d054a85d0f7e5881c59cd523beChris Craik        outputVertices.pop();
94565cd612face362d054a85d0f7e5881c59cd523beChris Craik        return true;
94665cd612face362d054a85d0f7e5881c59cd523beChris Craik    }
94765cd612face362d054a85d0f7e5881c59cd523beChris Craik    return false;
94865cd612face362d054a85d0f7e5881c59cd523beChris Craik}
94965cd612face362d054a85d0f7e5881c59cd523beChris Craik
95065cd612face362d054a85d0f7e5881c59cd523beChris Craik///////////////////////////////////////////////////////////////////////////////
95165cd612face362d054a85d0f7e5881c59cd523beChris Craik// Bezier approximation
95265cd612face362d054a85d0f7e5881c59cd523beChris Craik///////////////////////////////////////////////////////////////////////////////
95365cd612face362d054a85d0f7e5881c59cd523beChris Craik
95465cd612face362d054a85d0f7e5881c59cd523beChris Craikvoid PathTessellator::recursiveCubicBezierVertices(
95565cd612face362d054a85d0f7e5881c59cd523beChris Craik        float p1x, float p1y, float c1x, float c1y,
95665cd612face362d054a85d0f7e5881c59cd523beChris Craik        float p2x, float p2y, float c2x, float c2y,
95765cd612face362d054a85d0f7e5881c59cd523beChris Craik        float sqrInvScaleX, float sqrInvScaleY, Vector<Vertex>& outputVertices) {
95865cd612face362d054a85d0f7e5881c59cd523beChris Craik    float dx = p2x - p1x;
95965cd612face362d054a85d0f7e5881c59cd523beChris Craik    float dy = p2y - p1y;
96065cd612face362d054a85d0f7e5881c59cd523beChris Craik    float d1 = fabs((c1x - p2x) * dy - (c1y - p2y) * dx);
96165cd612face362d054a85d0f7e5881c59cd523beChris Craik    float d2 = fabs((c2x - p2x) * dy - (c2y - p2y) * dx);
96265cd612face362d054a85d0f7e5881c59cd523beChris Craik    float d = d1 + d2;
96365cd612face362d054a85d0f7e5881c59cd523beChris Craik
96465cd612face362d054a85d0f7e5881c59cd523beChris Craik    // multiplying by sqrInvScaleY/X equivalent to multiplying in dimensional scale factors
96565cd612face362d054a85d0f7e5881c59cd523beChris Craik
96665cd612face362d054a85d0f7e5881c59cd523beChris Craik    if (d * d < THRESHOLD * THRESHOLD * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) {
96765cd612face362d054a85d0f7e5881c59cd523beChris Craik        // below thresh, draw line by adding endpoint
96865cd612face362d054a85d0f7e5881c59cd523beChris Craik        pushToVector(outputVertices, p2x, p2y);
96965cd612face362d054a85d0f7e5881c59cd523beChris Craik    } else {
97065cd612face362d054a85d0f7e5881c59cd523beChris Craik        float p1c1x = (p1x + c1x) * 0.5f;
97165cd612face362d054a85d0f7e5881c59cd523beChris Craik        float p1c1y = (p1y + c1y) * 0.5f;
97265cd612face362d054a85d0f7e5881c59cd523beChris Craik        float p2c2x = (p2x + c2x) * 0.5f;
97365cd612face362d054a85d0f7e5881c59cd523beChris Craik        float p2c2y = (p2y + c2y) * 0.5f;
97465cd612face362d054a85d0f7e5881c59cd523beChris Craik
97565cd612face362d054a85d0f7e5881c59cd523beChris Craik        float c1c2x = (c1x + c2x) * 0.5f;
97665cd612face362d054a85d0f7e5881c59cd523beChris Craik        float c1c2y = (c1y + c2y) * 0.5f;
97765cd612face362d054a85d0f7e5881c59cd523beChris Craik
97865cd612face362d054a85d0f7e5881c59cd523beChris Craik        float p1c1c2x = (p1c1x + c1c2x) * 0.5f;
97965cd612face362d054a85d0f7e5881c59cd523beChris Craik        float p1c1c2y = (p1c1y + c1c2y) * 0.5f;
98065cd612face362d054a85d0f7e5881c59cd523beChris Craik
98165cd612face362d054a85d0f7e5881c59cd523beChris Craik        float p2c1c2x = (p2c2x + c1c2x) * 0.5f;
98265cd612face362d054a85d0f7e5881c59cd523beChris Craik        float p2c1c2y = (p2c2y + c1c2y) * 0.5f;
98365cd612face362d054a85d0f7e5881c59cd523beChris Craik
98465cd612face362d054a85d0f7e5881c59cd523beChris Craik        float mx = (p1c1c2x + p2c1c2x) * 0.5f;
98565cd612face362d054a85d0f7e5881c59cd523beChris Craik        float my = (p1c1c2y + p2c1c2y) * 0.5f;
98665cd612face362d054a85d0f7e5881c59cd523beChris Craik
98765cd612face362d054a85d0f7e5881c59cd523beChris Craik        recursiveCubicBezierVertices(
98865cd612face362d054a85d0f7e5881c59cd523beChris Craik                p1x, p1y, p1c1x, p1c1y,
98965cd612face362d054a85d0f7e5881c59cd523beChris Craik                mx, my, p1c1c2x, p1c1c2y,
99065cd612face362d054a85d0f7e5881c59cd523beChris Craik                sqrInvScaleX, sqrInvScaleY, outputVertices);
99165cd612face362d054a85d0f7e5881c59cd523beChris Craik        recursiveCubicBezierVertices(
99265cd612face362d054a85d0f7e5881c59cd523beChris Craik                mx, my, p2c1c2x, p2c1c2y,
99365cd612face362d054a85d0f7e5881c59cd523beChris Craik                p2x, p2y, p2c2x, p2c2y,
99465cd612face362d054a85d0f7e5881c59cd523beChris Craik                sqrInvScaleX, sqrInvScaleY, outputVertices);
99565cd612face362d054a85d0f7e5881c59cd523beChris Craik    }
99665cd612face362d054a85d0f7e5881c59cd523beChris Craik}
99765cd612face362d054a85d0f7e5881c59cd523beChris Craik
99865cd612face362d054a85d0f7e5881c59cd523beChris Craikvoid PathTessellator::recursiveQuadraticBezierVertices(
99965cd612face362d054a85d0f7e5881c59cd523beChris Craik        float ax, float ay,
100065cd612face362d054a85d0f7e5881c59cd523beChris Craik        float bx, float by,
100165cd612face362d054a85d0f7e5881c59cd523beChris Craik        float cx, float cy,
100265cd612face362d054a85d0f7e5881c59cd523beChris Craik        float sqrInvScaleX, float sqrInvScaleY, Vector<Vertex>& outputVertices) {
100365cd612face362d054a85d0f7e5881c59cd523beChris Craik    float dx = bx - ax;
100465cd612face362d054a85d0f7e5881c59cd523beChris Craik    float dy = by - ay;
100565cd612face362d054a85d0f7e5881c59cd523beChris Craik    float d = (cx - bx) * dy - (cy - by) * dx;
100665cd612face362d054a85d0f7e5881c59cd523beChris Craik
100765cd612face362d054a85d0f7e5881c59cd523beChris Craik    if (d * d < THRESHOLD * THRESHOLD * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) {
100865cd612face362d054a85d0f7e5881c59cd523beChris Craik        // below thresh, draw line by adding endpoint
100965cd612face362d054a85d0f7e5881c59cd523beChris Craik        pushToVector(outputVertices, bx, by);
101065cd612face362d054a85d0f7e5881c59cd523beChris Craik    } else {
101165cd612face362d054a85d0f7e5881c59cd523beChris Craik        float acx = (ax + cx) * 0.5f;
101265cd612face362d054a85d0f7e5881c59cd523beChris Craik        float bcx = (bx + cx) * 0.5f;
101365cd612face362d054a85d0f7e5881c59cd523beChris Craik        float acy = (ay + cy) * 0.5f;
101465cd612face362d054a85d0f7e5881c59cd523beChris Craik        float bcy = (by + cy) * 0.5f;
101565cd612face362d054a85d0f7e5881c59cd523beChris Craik
101665cd612face362d054a85d0f7e5881c59cd523beChris Craik        // midpoint
101765cd612face362d054a85d0f7e5881c59cd523beChris Craik        float mx = (acx + bcx) * 0.5f;
101865cd612face362d054a85d0f7e5881c59cd523beChris Craik        float my = (acy + bcy) * 0.5f;
101965cd612face362d054a85d0f7e5881c59cd523beChris Craik
102065cd612face362d054a85d0f7e5881c59cd523beChris Craik        recursiveQuadraticBezierVertices(ax, ay, mx, my, acx, acy,
102165cd612face362d054a85d0f7e5881c59cd523beChris Craik                sqrInvScaleX, sqrInvScaleY, outputVertices);
102265cd612face362d054a85d0f7e5881c59cd523beChris Craik        recursiveQuadraticBezierVertices(mx, my, bx, by, bcx, bcy,
102365cd612face362d054a85d0f7e5881c59cd523beChris Craik                sqrInvScaleX, sqrInvScaleY, outputVertices);
102465cd612face362d054a85d0f7e5881c59cd523beChris Craik    }
102565cd612face362d054a85d0f7e5881c59cd523beChris Craik}
102665cd612face362d054a85d0f7e5881c59cd523beChris Craik
102765cd612face362d054a85d0f7e5881c59cd523beChris Craik}; // namespace uirenderer
102865cd612face362d054a85d0f7e5881c59cd523beChris Craik}; // namespace android
1029