1710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik/*
2710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik * Copyright (C) 2012 The Android Open Source Project
3710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik *
4710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik * Licensed under the Apache License, Version 2.0 (the "License");
5710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik * you may not use this file except in compliance with the License.
6710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik * You may obtain a copy of the License at
7710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik *
8710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik *      http://www.apache.org/licenses/LICENSE-2.0
9710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik *
10710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik * Unless required by applicable law or agreed to in writing, software
11710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik * distributed under the License is distributed on an "AS IS" BASIS,
12710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik * See the License for the specific language governing permissions and
14710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik * limitations under the License.
15710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik */
16710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik
17710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik#define LOG_TAG "PathRenderer"
18710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik#define LOG_NDEBUG 1
19710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik#define ATRACE_TAG ATRACE_TAG_GRAPHICS
20710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik
21710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik#define VERTEX_DEBUG 0
22710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik
23710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik#include <SkPath.h>
24cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik#include <SkPaint.h>
25710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik
26710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik#include <stdlib.h>
27710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik#include <stdint.h>
28710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik#include <sys/types.h>
29710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik
30710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik#include <utils/Log.h>
31710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik#include <utils/Trace.h>
32710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik
33710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik#include "PathRenderer.h"
34710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik#include "Matrix.h"
35710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik#include "Vector.h"
36710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik#include "Vertex.h"
37710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik
38710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craiknamespace android {
39710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craiknamespace uirenderer {
40710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik
41710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik#define THRESHOLD 0.5f
42710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik
43cb4d6009576cf08195dc23f341a3f4939c0878bbChris CraikSkRect PathRenderer::computePathBounds(const SkPath& path, const SkPaint* paint) {
44cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    SkRect bounds = path.getBounds();
45cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    if (paint->getStyle() != SkPaint::kFill_Style) {
46cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik        float outset = paint->getStrokeWidth() * 0.5f;
47cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik        bounds.outset(outset, outset);
48cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    }
49cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    return bounds;
50cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik}
51cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik
52cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craikvoid computeInverseScales(const mat4 *transform, float &inverseScaleX, float& inverseScaleY) {
53710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik    if (CC_UNLIKELY(!transform->isPureTranslate())) {
54710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik        float m00 = transform->data[Matrix4::kScaleX];
55710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik        float m01 = transform->data[Matrix4::kSkewY];
56710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik        float m10 = transform->data[Matrix4::kSkewX];
57710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik        float m11 = transform->data[Matrix4::kScaleY];
58710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik        float scaleX = sqrt(m00 * m00 + m01 * m01);
59710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik        float scaleY = sqrt(m10 * m10 + m11 * m11);
6016b897c488a740e004bfce7d50b0d7602277fc0bChris Craik        inverseScaleX = (scaleX != 0) ? (1.0f / scaleX) : 1.0f;
6116b897c488a740e004bfce7d50b0d7602277fc0bChris Craik        inverseScaleY = (scaleY != 0) ? (1.0f / scaleY) : 1.0f;
62cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    } else {
63cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik        inverseScaleX = 1.0f;
64cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik        inverseScaleY = 1.0f;
65710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik    }
66710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik}
67710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik
6816b897c488a740e004bfce7d50b0d7602277fc0bChris Craikinline void copyVertex(Vertex* destPtr, const Vertex* srcPtr) {
69cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    Vertex::set(destPtr, srcPtr->position[0], srcPtr->position[1]);
70cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik}
71710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik
7216b897c488a740e004bfce7d50b0d7602277fc0bChris Craikinline void copyAlphaVertex(AlphaVertex* destPtr, const AlphaVertex* srcPtr) {
73cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    AlphaVertex::set(destPtr, srcPtr->position[0], srcPtr->position[1], srcPtr->alpha);
74cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik}
75710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik
7616b897c488a740e004bfce7d50b0d7602277fc0bChris Craik/**
7716b897c488a740e004bfce7d50b0d7602277fc0bChris Craik * Produces a pseudo-normal for a vertex, given the normals of the two incoming lines. If the offset
7816b897c488a740e004bfce7d50b0d7602277fc0bChris Craik * from each vertex in a perimeter is calculated, the resultant lines connecting the offset vertices
7916b897c488a740e004bfce7d50b0d7602277fc0bChris Craik * will be offset by 1.0
8016b897c488a740e004bfce7d50b0d7602277fc0bChris Craik *
8116b897c488a740e004bfce7d50b0d7602277fc0bChris Craik * Note that we can't add and normalize the two vectors, that would result in a rectangle having an
8216b897c488a740e004bfce7d50b0d7602277fc0bChris Craik * offset of (sqrt(2)/2, sqrt(2)/2) at each corner, instead of (1, 1)
83780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik *
84780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik * NOTE: assumes angles between normals 90 degrees or less
8516b897c488a740e004bfce7d50b0d7602277fc0bChris Craik */
8616b897c488a740e004bfce7d50b0d7602277fc0bChris Craikinline vec2 totalOffsetFromNormals(const vec2& normalA, const vec2& normalB) {
8716b897c488a740e004bfce7d50b0d7602277fc0bChris Craik    return (normalA + normalB) / (1 + fabs(normalA.dot(normalB)));
8816b897c488a740e004bfce7d50b0d7602277fc0bChris Craik}
8916b897c488a740e004bfce7d50b0d7602277fc0bChris Craik
90780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craikinline void scaleOffsetForStrokeWidth(vec2& offset, float halfStrokeWidth,
91780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        float inverseScaleX, float inverseScaleY) {
92780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik    if (halfStrokeWidth == 0.0f) {
93780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        // hairline - compensate for scale
94780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        offset.x *= 0.5f * inverseScaleX;
95780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        offset.y *= 0.5f * inverseScaleY;
96780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik    } else {
97780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        offset *= halfStrokeWidth;
98780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik    }
99780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik}
100780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik
101cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craikvoid getFillVerticesFromPerimeter(const Vector<Vertex>& perimeter, VertexBuffer& vertexBuffer) {
102cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    Vertex* buffer = vertexBuffer.alloc<Vertex>(perimeter.size());
103cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik
104cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    int currentIndex = 0;
105cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    // zig zag between all previous points on the inside of the hull to create a
106cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    // triangle strip that fills the hull
107cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    int srcAindex = 0;
108cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    int srcBindex = perimeter.size() - 1;
109cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    while (srcAindex <= srcBindex) {
110cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik        copyVertex(&buffer[currentIndex++], &perimeter[srcAindex]);
111cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik        if (srcAindex == srcBindex) break;
112cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik        copyVertex(&buffer[currentIndex++], &perimeter[srcBindex]);
113cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik        srcAindex++;
114cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik        srcBindex--;
115710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik    }
116cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik}
117cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik
118cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craikvoid getStrokeVerticesFromPerimeter(const Vector<Vertex>& perimeter, float halfStrokeWidth,
119cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik        VertexBuffer& vertexBuffer, float inverseScaleX, float inverseScaleY) {
120cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    Vertex* buffer = vertexBuffer.alloc<Vertex>(perimeter.size() * 2 + 2);
121cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik
122710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik    int currentIndex = 0;
123cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    const Vertex* last = &(perimeter[perimeter.size() - 1]);
124cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    const Vertex* current = &(perimeter[0]);
125cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    vec2 lastNormal(current->position[1] - last->position[1],
126cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik            last->position[0] - current->position[0]);
127cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    lastNormal.normalize();
128cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    for (unsigned int i = 0; i < perimeter.size(); i++) {
129cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik        const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]);
130cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik        vec2 nextNormal(next->position[1] - current->position[1],
131cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik                current->position[0] - next->position[0]);
132cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik        nextNormal.normalize();
133cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik
13416b897c488a740e004bfce7d50b0d7602277fc0bChris Craik        vec2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
135780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        scaleOffsetForStrokeWidth(totalOffset, halfStrokeWidth, inverseScaleX, inverseScaleY);
136cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik
137cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik        Vertex::set(&buffer[currentIndex++],
138cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik                current->position[0] + totalOffset.x,
139cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik                current->position[1] + totalOffset.y);
140cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik
141cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik        Vertex::set(&buffer[currentIndex++],
142cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik                current->position[0] - totalOffset.x,
143cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik                current->position[1] - totalOffset.y);
144cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik
145cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik        last = current;
146cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik        current = next;
147cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik        lastNormal = nextNormal;
148710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik    }
149710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik
150cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    // wrap around to beginning
151cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    copyVertex(&buffer[currentIndex++], &buffer[0]);
152cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    copyVertex(&buffer[currentIndex++], &buffer[1]);
153cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik}
154710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik
155780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craikvoid getStrokeVerticesFromUnclosedVertices(const Vector<Vertex>& vertices, float halfStrokeWidth,
156780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        VertexBuffer& vertexBuffer, float inverseScaleX, float inverseScaleY) {
157780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik    Vertex* buffer = vertexBuffer.alloc<Vertex>(vertices.size() * 2);
158780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik
159780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik    int currentIndex = 0;
160780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik    const Vertex* current = &(vertices[0]);
161780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik    vec2 lastNormal;
162780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik    for (unsigned int i = 0; i < vertices.size() - 1; i++) {
163780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        const Vertex* next = &(vertices[i + 1]);
164780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        vec2 nextNormal(next->position[1] - current->position[1],
165780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik                current->position[0] - next->position[0]);
166780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        nextNormal.normalize();
167780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik
168780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        vec2 totalOffset;
169780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        if (i == 0) {
170780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik            totalOffset = nextNormal;
171780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        } else {
172780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik            totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
173780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        }
174780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        scaleOffsetForStrokeWidth(totalOffset, halfStrokeWidth, inverseScaleX, inverseScaleY);
175780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik
176780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        Vertex::set(&buffer[currentIndex++],
177780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik                current->position[0] + totalOffset.x,
178780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik                current->position[1] + totalOffset.y);
179780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik
180780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        Vertex::set(&buffer[currentIndex++],
181780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik                current->position[0] - totalOffset.x,
182780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik                current->position[1] - totalOffset.y);
183780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik
184780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        current = next;
185780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        lastNormal = nextNormal;
186780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik    }
187780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik
188780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik    vec2 totalOffset = lastNormal;
189780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik    scaleOffsetForStrokeWidth(totalOffset, halfStrokeWidth, inverseScaleX, inverseScaleY);
190780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik
191780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik    Vertex::set(&buffer[currentIndex++],
192780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik            current->position[0] + totalOffset.x,
193780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik            current->position[1] + totalOffset.y);
194780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik    Vertex::set(&buffer[currentIndex++],
195780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik            current->position[0] - totalOffset.x,
196780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik            current->position[1] - totalOffset.y);
197780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik#if VERTEX_DEBUG
198780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik    for (unsigned int i = 0; i < vertexBuffer.getSize(); i++) {
199780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        ALOGD("point at %f %f", buffer[i].position[0], buffer[i].position[1]);
200780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik    }
201780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik#endif
202780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik}
203780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik
204cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craikvoid getFillVerticesFromPerimeterAA(const Vector<Vertex>& perimeter, VertexBuffer& vertexBuffer,
205cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik         float inverseScaleX, float inverseScaleY) {
206cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    AlphaVertex* buffer = vertexBuffer.alloc<AlphaVertex>(perimeter.size() * 3 + 2);
207710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik
208cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    // generate alpha points - fill Alpha vertex gaps in between each point with
209cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    // alpha 0 vertex, offset by a scaled normal.
210cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    int currentIndex = 0;
211cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    const Vertex* last = &(perimeter[perimeter.size() - 1]);
212cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    const Vertex* current = &(perimeter[0]);
213cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    vec2 lastNormal(current->position[1] - last->position[1],
214cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik            last->position[0] - current->position[0]);
215cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    lastNormal.normalize();
216cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    for (unsigned int i = 0; i < perimeter.size(); i++) {
217cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik        const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]);
218710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik        vec2 nextNormal(next->position[1] - current->position[1],
219cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik                current->position[0] - next->position[0]);
220710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik        nextNormal.normalize();
221710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik
22216b897c488a740e004bfce7d50b0d7602277fc0bChris Craik        // AA point offset from original point is that point's normal, such that each side is offset
22316b897c488a740e004bfce7d50b0d7602277fc0bChris Craik        // by .5 pixels
22416b897c488a740e004bfce7d50b0d7602277fc0bChris Craik        vec2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
22516b897c488a740e004bfce7d50b0d7602277fc0bChris Craik        totalOffset.x *= 0.5f * inverseScaleX;
22616b897c488a740e004bfce7d50b0d7602277fc0bChris Craik        totalOffset.y *= 0.5f * inverseScaleY;
227710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik
228710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik        AlphaVertex::set(&buffer[currentIndex++],
229cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik                current->position[0] + totalOffset.x,
230cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik                current->position[1] + totalOffset.y,
231cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik                0.0f);
232710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik        AlphaVertex::set(&buffer[currentIndex++],
233cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik                current->position[0] - totalOffset.x,
234cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik                current->position[1] - totalOffset.y,
235cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik                1.0f);
236cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik
237710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik        last = current;
238cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik        current = next;
239cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik        lastNormal = nextNormal;
240710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik    }
241710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik
242710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik    // wrap around to beginning
243cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    copyAlphaVertex(&buffer[currentIndex++], &buffer[0]);
244cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    copyAlphaVertex(&buffer[currentIndex++], &buffer[1]);
245710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik
246710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik    // zig zag between all previous points on the inside of the hull to create a
247710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik    // triangle strip that fills the hull, repeating the first inner point to
248710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik    // create degenerate tris to start inside path
249710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik    int srcAindex = 0;
250cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    int srcBindex = perimeter.size() - 1;
251710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik    while (srcAindex <= srcBindex) {
252cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik        copyAlphaVertex(&buffer[currentIndex++], &buffer[srcAindex * 2 + 1]);
253710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik        if (srcAindex == srcBindex) break;
254cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik        copyAlphaVertex(&buffer[currentIndex++], &buffer[srcBindex * 2 + 1]);
255710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik        srcAindex++;
256710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik        srcBindex--;
257710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik    }
258710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik
259710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik#if VERTEX_DEBUG
260cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    for (unsigned int i = 0; i < vertexBuffer.getSize(); i++) {
261780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        ALOGD("point at %f %f, alpha %f", buffer[i].position[0], buffer[i].position[1], buffer[i].alpha);
262780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik    }
263780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik#endif
264780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik}
265780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik
266780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik
267780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craikvoid getStrokeVerticesFromUnclosedVerticesAA(const Vector<Vertex>& vertices, float halfStrokeWidth,
268780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        VertexBuffer& vertexBuffer, float inverseScaleX, float inverseScaleY) {
269780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik    AlphaVertex* buffer = vertexBuffer.alloc<AlphaVertex>(6 * vertices.size() + 2);
270780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik
271780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik    // avoid lines smaller than hairline since they break triangle based sampling. instead reducing
272780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik    // alpha value (TODO: support different X/Y scale)
273780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik    float maxAlpha = 1.0f;
274780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik    if (halfStrokeWidth != 0 && inverseScaleX == inverseScaleY &&
275780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik            halfStrokeWidth * inverseScaleX < 0.5f) {
276780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        maxAlpha *= (2 * halfStrokeWidth) / inverseScaleX;
277780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        halfStrokeWidth = 0.0f;
278780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik    }
279780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik
280780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik    // there is no outer/inner here, using them for consistency with below approach
281780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik    int offset = 2 * (vertices.size() - 2);
282780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik    int currentAAOuterIndex = 2;
283780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik    int currentAAInnerIndex = 2 * offset + 5; // reversed
284780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik    int currentStrokeIndex = currentAAInnerIndex + 7;
285780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik
286780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik    const Vertex* last = &(vertices[0]);
287780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik    const Vertex* current = &(vertices[1]);
288780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik    vec2 lastNormal(current->position[1] - last->position[1],
289780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik            last->position[0] - current->position[0]);
290780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik    lastNormal.normalize();
291780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik
292780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik    {
293780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        // start cap
294780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        vec2 totalOffset = lastNormal;
295780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        vec2 AAOffset = totalOffset;
296780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        AAOffset.x *= 0.5f * inverseScaleX;
297780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        AAOffset.y *= 0.5f * inverseScaleY;
298780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik
299780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        vec2 innerOffset = totalOffset;
300780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        scaleOffsetForStrokeWidth(innerOffset, halfStrokeWidth, inverseScaleX, inverseScaleY);
301780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        vec2 outerOffset = innerOffset + AAOffset;
302780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        innerOffset -= AAOffset;
303780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik
304780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        // TODO: support square cap by changing this offset to incorporate halfStrokeWidth
305780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        vec2 capAAOffset(AAOffset.y, -AAOffset.x);
306780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        AlphaVertex::set(&buffer[0],
307780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik                last->position[0] + outerOffset.x + capAAOffset.x,
308780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik                last->position[1] + outerOffset.y + capAAOffset.y,
309780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik                0.0f);
310780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        AlphaVertex::set(&buffer[1],
311780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik                last->position[0] + innerOffset.x - capAAOffset.x,
312780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik                last->position[1] + innerOffset.y - capAAOffset.y,
313780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik                maxAlpha);
314780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik
315780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        AlphaVertex::set(&buffer[2 * offset + 6],
316780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik                last->position[0] - outerOffset.x + capAAOffset.x,
317780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik                last->position[1] - outerOffset.y + capAAOffset.y,
318780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik                0.0f);
319780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        AlphaVertex::set(&buffer[2 * offset + 7],
320780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik                last->position[0] - innerOffset.x - capAAOffset.x,
321780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik                last->position[1] - innerOffset.y - capAAOffset.y,
322780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik                maxAlpha);
323780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        copyAlphaVertex(&buffer[2 * offset + 8], &buffer[0]);
324780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        copyAlphaVertex(&buffer[2 * offset + 9], &buffer[1]);
325780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        copyAlphaVertex(&buffer[2 * offset + 10], &buffer[1]); // degenerate tris (the only two!)
326780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        copyAlphaVertex(&buffer[2 * offset + 11], &buffer[2 * offset + 7]);
327780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik    }
328780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik
329780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik    for (unsigned int i = 1; i < vertices.size() - 1; i++) {
330780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        const Vertex* next = &(vertices[i + 1]);
331780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        vec2 nextNormal(next->position[1] - current->position[1],
332780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik                current->position[0] - next->position[0]);
333780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        nextNormal.normalize();
334780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik
335780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        vec2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
336780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        vec2 AAOffset = totalOffset;
337780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        AAOffset.x *= 0.5f * inverseScaleX;
338780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        AAOffset.y *= 0.5f * inverseScaleY;
339780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik
340780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        vec2 innerOffset = totalOffset;
341780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        scaleOffsetForStrokeWidth(innerOffset, halfStrokeWidth, inverseScaleX, inverseScaleY);
342780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        vec2 outerOffset = innerOffset + AAOffset;
343780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        innerOffset -= AAOffset;
344780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik
345780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        AlphaVertex::set(&buffer[currentAAOuterIndex++],
346780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik                current->position[0] + outerOffset.x,
347780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik                current->position[1] + outerOffset.y,
348780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik                0.0f);
349780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        AlphaVertex::set(&buffer[currentAAOuterIndex++],
350780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik                current->position[0] + innerOffset.x,
351780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik                current->position[1] + innerOffset.y,
352780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik                maxAlpha);
353780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik
354780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        AlphaVertex::set(&buffer[currentStrokeIndex++],
355780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik                current->position[0] + innerOffset.x,
356780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik                current->position[1] + innerOffset.y,
357780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik                maxAlpha);
358780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        AlphaVertex::set(&buffer[currentStrokeIndex++],
359780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik                current->position[0] - innerOffset.x,
360780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik                current->position[1] - innerOffset.y,
361780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik                maxAlpha);
362780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik
363780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        AlphaVertex::set(&buffer[currentAAInnerIndex--],
364780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik                current->position[0] - innerOffset.x,
365780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik                current->position[1] - innerOffset.y,
366780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik                maxAlpha);
367780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        AlphaVertex::set(&buffer[currentAAInnerIndex--],
368780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik                current->position[0] - outerOffset.x,
369780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik                current->position[1] - outerOffset.y,
370780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik                0.0f);
371780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik
372780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        last = current;
373780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        current = next;
374780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        lastNormal = nextNormal;
375780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik    }
376780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik
377780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik    {
378780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        // end cap
379780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        vec2 totalOffset = lastNormal;
380780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        vec2 AAOffset = totalOffset;
381780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        AAOffset.x *= 0.5f * inverseScaleX;
382780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        AAOffset.y *= 0.5f * inverseScaleY;
383780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik
384780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        vec2 innerOffset = totalOffset;
385780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        scaleOffsetForStrokeWidth(innerOffset, halfStrokeWidth, inverseScaleX, inverseScaleY);
386780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        vec2 outerOffset = innerOffset + AAOffset;
387780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        innerOffset -= AAOffset;
388780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik
389780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        // TODO: support square cap by changing this offset to incorporate halfStrokeWidth
390780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        vec2 capAAOffset(-AAOffset.y, AAOffset.x);
391780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik
392780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        AlphaVertex::set(&buffer[offset + 2],
393780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik                current->position[0] + outerOffset.x + capAAOffset.x,
394780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik                current->position[1] + outerOffset.y + capAAOffset.y,
395780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik                0.0f);
396780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        AlphaVertex::set(&buffer[offset + 3],
397780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik                current->position[0] + innerOffset.x - capAAOffset.x,
398780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik                current->position[1] + innerOffset.y - capAAOffset.y,
399780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik                maxAlpha);
400780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik
401780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        AlphaVertex::set(&buffer[offset + 4],
402780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik                current->position[0] - outerOffset.x + capAAOffset.x,
403780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik                current->position[1] - outerOffset.y + capAAOffset.y,
404780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik                0.0f);
405780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        AlphaVertex::set(&buffer[offset + 5],
406780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik                current->position[0] - innerOffset.x - capAAOffset.x,
407780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik                current->position[1] - innerOffset.y - capAAOffset.y,
408780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik                maxAlpha);
409780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik
410780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        copyAlphaVertex(&buffer[vertexBuffer.getSize() - 2], &buffer[offset + 3]);
411780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        copyAlphaVertex(&buffer[vertexBuffer.getSize() - 1], &buffer[offset + 5]);
412780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik    }
413780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik
414780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik#if VERTEX_DEBUG
415780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik    for (unsigned int i = 0; i < vertexBuffer.getSize(); i++) {
416780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        ALOGD("point at %f %f, alpha %f", buffer[i].position[0], buffer[i].position[1], buffer[i].alpha);
417710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik    }
418710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik#endif
419710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik}
420710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik
421780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik
422cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craikvoid getStrokeVerticesFromPerimeterAA(const Vector<Vertex>& perimeter, float halfStrokeWidth,
423cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik        VertexBuffer& vertexBuffer, float inverseScaleX, float inverseScaleY) {
424cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    AlphaVertex* buffer = vertexBuffer.alloc<AlphaVertex>(6 * perimeter.size() + 8);
425cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik
42616b897c488a740e004bfce7d50b0d7602277fc0bChris Craik    // avoid lines smaller than hairline since they break triangle based sampling. instead reducing
42716b897c488a740e004bfce7d50b0d7602277fc0bChris Craik    // alpha value (TODO: support different X/Y scale)
42816b897c488a740e004bfce7d50b0d7602277fc0bChris Craik    float maxAlpha = 1.0f;
42916b897c488a740e004bfce7d50b0d7602277fc0bChris Craik    if (halfStrokeWidth != 0 && inverseScaleX == inverseScaleY &&
4302154af209f6d269e29c6e991ce6c1349dfc85b93Chris Craik            halfStrokeWidth * inverseScaleX < 0.5f) {
43116b897c488a740e004bfce7d50b0d7602277fc0bChris Craik        maxAlpha *= (2 * halfStrokeWidth) / inverseScaleX;
43216b897c488a740e004bfce7d50b0d7602277fc0bChris Craik        halfStrokeWidth = 0.0f;
43316b897c488a740e004bfce7d50b0d7602277fc0bChris Craik    }
43416b897c488a740e004bfce7d50b0d7602277fc0bChris Craik
435cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    int offset = 2 * perimeter.size() + 3;
436cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    int currentAAOuterIndex = 0;
437cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    int currentStrokeIndex = offset;
438cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    int currentAAInnerIndex = offset * 2;
439cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik
440cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    const Vertex* last = &(perimeter[perimeter.size() - 1]);
441cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    const Vertex* current = &(perimeter[0]);
442cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    vec2 lastNormal(current->position[1] - last->position[1],
443cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik            last->position[0] - current->position[0]);
444cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    lastNormal.normalize();
445cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    for (unsigned int i = 0; i < perimeter.size(); i++) {
446cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik        const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]);
447cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik        vec2 nextNormal(next->position[1] - current->position[1],
448cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik                current->position[0] - next->position[0]);
449cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik        nextNormal.normalize();
450cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik
45116b897c488a740e004bfce7d50b0d7602277fc0bChris Craik        vec2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
45216b897c488a740e004bfce7d50b0d7602277fc0bChris Craik        vec2 AAOffset = totalOffset;
45316b897c488a740e004bfce7d50b0d7602277fc0bChris Craik        AAOffset.x *= 0.5f * inverseScaleX;
45416b897c488a740e004bfce7d50b0d7602277fc0bChris Craik        AAOffset.y *= 0.5f * inverseScaleY;
455cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik
45616b897c488a740e004bfce7d50b0d7602277fc0bChris Craik        vec2 innerOffset = totalOffset;
457780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        scaleOffsetForStrokeWidth(innerOffset, halfStrokeWidth, inverseScaleX, inverseScaleY);
458cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik        vec2 outerOffset = innerOffset + AAOffset;
459cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik        innerOffset -= AAOffset;
460cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik
461cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik        AlphaVertex::set(&buffer[currentAAOuterIndex++],
462cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik                current->position[0] + outerOffset.x,
463cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik                current->position[1] + outerOffset.y,
464cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik                0.0f);
465cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik        AlphaVertex::set(&buffer[currentAAOuterIndex++],
466cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik                current->position[0] + innerOffset.x,
467cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik                current->position[1] + innerOffset.y,
46816b897c488a740e004bfce7d50b0d7602277fc0bChris Craik                maxAlpha);
469cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik
470cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik        AlphaVertex::set(&buffer[currentStrokeIndex++],
471cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik                current->position[0] + innerOffset.x,
472cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik                current->position[1] + innerOffset.y,
47316b897c488a740e004bfce7d50b0d7602277fc0bChris Craik                maxAlpha);
474cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik        AlphaVertex::set(&buffer[currentStrokeIndex++],
475cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik                current->position[0] - innerOffset.x,
476cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik                current->position[1] - innerOffset.y,
47716b897c488a740e004bfce7d50b0d7602277fc0bChris Craik                maxAlpha);
478cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik
479cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik        AlphaVertex::set(&buffer[currentAAInnerIndex++],
480cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik                current->position[0] - innerOffset.x,
481cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik                current->position[1] - innerOffset.y,
48216b897c488a740e004bfce7d50b0d7602277fc0bChris Craik                maxAlpha);
483cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik        AlphaVertex::set(&buffer[currentAAInnerIndex++],
484cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik                current->position[0] - outerOffset.x,
485cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik                current->position[1] - outerOffset.y,
486cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik                0.0f);
487cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik
488cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik        last = current;
489cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik        current = next;
490cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik        lastNormal = nextNormal;
491cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    }
492cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik
493cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    // wrap each strip around to beginning, creating degenerate tris to bridge strips
494cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    copyAlphaVertex(&buffer[currentAAOuterIndex++], &buffer[0]);
495cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    copyAlphaVertex(&buffer[currentAAOuterIndex++], &buffer[1]);
496cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    copyAlphaVertex(&buffer[currentAAOuterIndex++], &buffer[1]);
497710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik
498cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    copyAlphaVertex(&buffer[currentStrokeIndex++], &buffer[offset]);
499cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    copyAlphaVertex(&buffer[currentStrokeIndex++], &buffer[offset + 1]);
500cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    copyAlphaVertex(&buffer[currentStrokeIndex++], &buffer[offset + 1]);
501cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik
502cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    copyAlphaVertex(&buffer[currentAAInnerIndex++], &buffer[2 * offset]);
503cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    copyAlphaVertex(&buffer[currentAAInnerIndex++], &buffer[2 * offset + 1]);
504cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    // don't need to create last degenerate tri
505780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik
506780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik#if VERTEX_DEBUG
507780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik    for (unsigned int i = 0; i < vertexBuffer.getSize(); i++) {
508780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        ALOGD("point at %f %f, alpha %f", buffer[i].position[0], buffer[i].position[1], buffer[i].alpha);
509780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik    }
510780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik#endif
511cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik}
512cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik
513cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craikvoid PathRenderer::convexPathVertices(const SkPath &path, const SkPaint* paint,
514cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik        const mat4 *transform, VertexBuffer& vertexBuffer) {
515cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    ATRACE_CALL();
516cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik
517cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    SkPaint::Style style = paint->getStyle();
518cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    bool isAA = paint->isAntiAlias();
519cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik
520cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    float inverseScaleX, inverseScaleY;
521cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    computeInverseScales(transform, inverseScaleX, inverseScaleY);
522cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik
523cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    Vector<Vertex> tempVertices;
52416b897c488a740e004bfce7d50b0d7602277fc0bChris Craik    float threshInvScaleX = inverseScaleX;
52516b897c488a740e004bfce7d50b0d7602277fc0bChris Craik    float threshInvScaleY = inverseScaleY;
52616b897c488a740e004bfce7d50b0d7602277fc0bChris Craik    if (style == SkPaint::kStroke_Style) {
52716b897c488a740e004bfce7d50b0d7602277fc0bChris Craik        // alter the bezier recursion threshold values we calculate in order to compensate for
52816b897c488a740e004bfce7d50b0d7602277fc0bChris Craik        // expansion done after the path vertices are found
52916b897c488a740e004bfce7d50b0d7602277fc0bChris Craik        SkRect bounds = path.getBounds();
53016b897c488a740e004bfce7d50b0d7602277fc0bChris Craik        if (!bounds.isEmpty()) {
53116b897c488a740e004bfce7d50b0d7602277fc0bChris Craik            threshInvScaleX *= bounds.width() / (bounds.width() + paint->getStrokeWidth());
53216b897c488a740e004bfce7d50b0d7602277fc0bChris Craik            threshInvScaleY *= bounds.height() / (bounds.height() + paint->getStrokeWidth());
53316b897c488a740e004bfce7d50b0d7602277fc0bChris Craik        }
53416b897c488a740e004bfce7d50b0d7602277fc0bChris Craik    }
535780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik
536780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik    // force close if we're filling the path, since fill path expects closed perimeter.
537780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik    bool forceClose = style != SkPaint::kStroke_Style;
538780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik    bool wasClosed = convexPathPerimeterVertices(path, forceClose, threshInvScaleX * threshInvScaleX,
53916b897c488a740e004bfce7d50b0d7602277fc0bChris Craik            threshInvScaleY * threshInvScaleY, tempVertices);
540cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik
541bf09ffb4e0dc820aeae56a3e576aed33cab218daChris Craik    if (!tempVertices.size()) {
542bf09ffb4e0dc820aeae56a3e576aed33cab218daChris Craik        // path was empty, return without allocating vertex buffer
543bf09ffb4e0dc820aeae56a3e576aed33cab218daChris Craik        return;
544bf09ffb4e0dc820aeae56a3e576aed33cab218daChris Craik    }
545bf09ffb4e0dc820aeae56a3e576aed33cab218daChris Craik
546cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik#if VERTEX_DEBUG
547cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    for (unsigned int i = 0; i < tempVertices.size(); i++) {
548cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik        ALOGD("orig path: point at %f %f", tempVertices[i].position[0], tempVertices[i].position[1]);
549cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    }
550cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik#endif
551cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik
552cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    if (style == SkPaint::kStroke_Style) {
553cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik        float halfStrokeWidth = paint->getStrokeWidth() * 0.5f;
554cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik        if (!isAA) {
555780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik            if (wasClosed) {
556780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik                getStrokeVerticesFromPerimeter(tempVertices, halfStrokeWidth, vertexBuffer,
557780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik                        inverseScaleX, inverseScaleY);
558780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik            } else {
559780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik                getStrokeVerticesFromUnclosedVertices(tempVertices, halfStrokeWidth, vertexBuffer,
560780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik                        inverseScaleX, inverseScaleY);
561780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik            }
562780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik
563cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik        } else {
564780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik            if (wasClosed) {
565780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik                getStrokeVerticesFromPerimeterAA(tempVertices, halfStrokeWidth, vertexBuffer,
566780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik                        inverseScaleX, inverseScaleY);
567780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik            } else {
568780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik                getStrokeVerticesFromUnclosedVerticesAA(tempVertices, halfStrokeWidth, vertexBuffer,
569780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik                        inverseScaleX, inverseScaleY);
570780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik            }
571cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik        }
572cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    } else {
573cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik        // For kStrokeAndFill style, the path should be adjusted externally, as it will be treated as a fill here.
574cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik        if (!isAA) {
575cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik            getFillVerticesFromPerimeter(tempVertices, vertexBuffer);
576cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik        } else {
577cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik            getFillVerticesFromPerimeterAA(tempVertices, vertexBuffer, inverseScaleX, inverseScaleY);
578cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik        }
579cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    }
580cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik}
581cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik
582cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik
583780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craikvoid pushToVector(Vector<Vertex>& vertices, float x, float y) {
584780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik    // TODO: make this not yuck
585780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik    vertices.push();
586780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik    Vertex* newVertex = &(vertices.editArray()[vertices.size() - 1]);
587780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik    Vertex::set(newVertex, x, y);
588780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik}
589780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik
590780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craikbool PathRenderer::convexPathPerimeterVertices(const SkPath& path, bool forceClose,
591cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik        float sqrInvScaleX, float sqrInvScaleY, Vector<Vertex>& outputVertices) {
592710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik    ATRACE_CALL();
593710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik
594780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik    // TODO: to support joins other than sharp miter, join vertices should be labelled in the
595780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik    // perimeter, or resolved into more vertices. Reconsider forceClose-ing in that case.
596780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik    SkPath::Iter iter(path, forceClose);
597710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik    SkPoint pts[4];
598710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik    SkPath::Verb v;
599710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik    Vertex* newVertex = 0;
600710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik    while (SkPath::kDone_Verb != (v = iter.next(pts))) {
601710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik            switch (v) {
602710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik                case SkPath::kMove_Verb:
603780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik                    pushToVector(outputVertices, pts[0].x(), pts[0].y());
604710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik                    ALOGV("Move to pos %f %f", pts[0].x(), pts[0].y());
605710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik                    break;
606710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik                case SkPath::kClose_Verb:
607710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik                    ALOGV("Close at pos %f %f", pts[0].x(), pts[0].y());
608710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik                    break;
609710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik                case SkPath::kLine_Verb:
610710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik                    ALOGV("kLine_Verb %f %f -> %f %f",
611cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik                            pts[0].x(), pts[0].y(),
612cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik                            pts[1].x(), pts[1].y());
613710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik
614780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik                    pushToVector(outputVertices, pts[1].x(), pts[1].y());
615710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik                    break;
616710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik                case SkPath::kQuad_Verb:
617710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik                    ALOGV("kQuad_Verb");
618710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik                    recursiveQuadraticBezierVertices(
619cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik                            pts[0].x(), pts[0].y(),
620cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik                            pts[2].x(), pts[2].y(),
621cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik                            pts[1].x(), pts[1].y(),
622cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik                            sqrInvScaleX, sqrInvScaleY, outputVertices);
623710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik                    break;
624710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik                case SkPath::kCubic_Verb:
625710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik                    ALOGV("kCubic_Verb");
626710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik                    recursiveCubicBezierVertices(
627cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik                            pts[0].x(), pts[0].y(),
628cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik                            pts[1].x(), pts[1].y(),
629cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik                            pts[3].x(), pts[3].y(),
630cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik                            pts[2].x(), pts[2].y(),
631cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik                        sqrInvScaleX, sqrInvScaleY, outputVertices);
632710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik                    break;
633710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik                default:
634710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik                    break;
635710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik            }
636710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik    }
637780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik
638780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik    int size = outputVertices.size();
639780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik    if (size >= 2 && outputVertices[0].position[0] == outputVertices[size - 1].position[0] &&
640780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik            outputVertices[0].position[1] == outputVertices[size - 1].position[1]) {
641780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        outputVertices.pop();
642780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        return true;
643780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik    }
644780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik    return false;
645710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik}
646710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik
647710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craikvoid PathRenderer::recursiveCubicBezierVertices(
648710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik        float p1x, float p1y, float c1x, float c1y,
649710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik        float p2x, float p2y, float c2x, float c2y,
650cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik        float sqrInvScaleX, float sqrInvScaleY, Vector<Vertex>& outputVertices) {
651710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik    float dx = p2x - p1x;
652710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik    float dy = p2y - p1y;
653710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik    float d1 = fabs((c1x - p2x) * dy - (c1y - p2y) * dx);
654710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik    float d2 = fabs((c2x - p2x) * dy - (c2y - p2y) * dx);
655710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik    float d = d1 + d2;
656710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik
657cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    // multiplying by sqrInvScaleY/X equivalent to multiplying in dimensional scale factors
658cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik
659cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    if (d * d < THRESHOLD * THRESHOLD * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) {
660710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik        // below thresh, draw line by adding endpoint
661780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        pushToVector(outputVertices, p2x, p2y);
662710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik    } else {
663710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik        float p1c1x = (p1x + c1x) * 0.5f;
664710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik        float p1c1y = (p1y + c1y) * 0.5f;
665710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik        float p2c2x = (p2x + c2x) * 0.5f;
666710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik        float p2c2y = (p2y + c2y) * 0.5f;
667710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik
668710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik        float c1c2x = (c1x + c2x) * 0.5f;
669710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik        float c1c2y = (c1y + c2y) * 0.5f;
670710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik
671710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik        float p1c1c2x = (p1c1x + c1c2x) * 0.5f;
672710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik        float p1c1c2y = (p1c1y + c1c2y) * 0.5f;
673710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik
674710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik        float p2c1c2x = (p2c2x + c1c2x) * 0.5f;
675710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik        float p2c1c2y = (p2c2y + c1c2y) * 0.5f;
676710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik
677710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik        float mx = (p1c1c2x + p2c1c2x) * 0.5f;
678710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik        float my = (p1c1c2y + p2c1c2y) * 0.5f;
679710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik
680710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik        recursiveCubicBezierVertices(
681710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik                p1x, p1y, p1c1x, p1c1y,
682710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik                mx, my, p1c1c2x, p1c1c2y,
683cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik                sqrInvScaleX, sqrInvScaleY, outputVertices);
684710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik        recursiveCubicBezierVertices(
685710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik                mx, my, p2c1c2x, p2c1c2y,
686710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik                p2x, p2y, p2c2x, p2c2y,
687cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik                sqrInvScaleX, sqrInvScaleY, outputVertices);
688710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik    }
689710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik}
690710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik
691710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craikvoid PathRenderer::recursiveQuadraticBezierVertices(
692710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik        float ax, float ay,
693710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik        float bx, float by,
694710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik        float cx, float cy,
695cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik        float sqrInvScaleX, float sqrInvScaleY, Vector<Vertex>& outputVertices) {
696710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik    float dx = bx - ax;
697710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik    float dy = by - ay;
698710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik    float d = (cx - bx) * dy - (cy - by) * dx;
699710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik
700cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik    if (d * d < THRESHOLD * THRESHOLD * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) {
701710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik        // below thresh, draw line by adding endpoint
702780c12875ce0c0d3fd072484d4b8b3c327cc4f31Chris Craik        pushToVector(outputVertices, bx, by);
703710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik    } else {
704710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik        float acx = (ax + cx) * 0.5f;
705710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik        float bcx = (bx + cx) * 0.5f;
706710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik        float acy = (ay + cy) * 0.5f;
707710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik        float bcy = (by + cy) * 0.5f;
708710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik
709710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik        // midpoint
710710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik        float mx = (acx + bcx) * 0.5f;
711710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik        float my = (acy + bcy) * 0.5f;
712710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik
713710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik        recursiveQuadraticBezierVertices(ax, ay, mx, my, acx, acy,
714cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik                sqrInvScaleX, sqrInvScaleY, outputVertices);
715710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik        recursiveQuadraticBezierVertices(mx, my, bx, by, bcx, bcy,
716cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik                sqrInvScaleX, sqrInvScaleY, outputVertices);
717710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik    }
718710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik}
719710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik
720710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik}; // namespace uirenderer
721710f46d9d6a5bf9ea1c1833384caf61e1934124fChris Craik}; // namespace android
722