blob: 209341ccb50344b6f62639da15dfd7b594fb7a41 [file] [log] [blame]
Chris Craik65cd6122012-12-10 17:56:27 -08001/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Chris Craikf0a59072013-11-19 18:00:46 -080017#define LOG_TAG "OpenGLRenderer"
Chris Craik65cd6122012-12-10 17:56:27 -080018#define LOG_NDEBUG 1
Chris Craik87f9df82014-03-07 14:34:42 -080019#define ATRACE_TAG ATRACE_TAG_VIEW
Chris Craik65cd6122012-12-10 17:56:27 -080020
21#define VERTEX_DEBUG 0
22
23#if VERTEX_DEBUG
24#define DEBUG_DUMP_ALPHA_BUFFER() \
25 for (unsigned int i = 0; i < vertexBuffer.getSize(); i++) { \
26 ALOGD("point %d at %f %f, alpha %f", \
Romain Guy3380cfd2013-08-15 16:57:57 -070027 i, buffer[i].x, buffer[i].y, buffer[i].alpha); \
Chris Craik65cd6122012-12-10 17:56:27 -080028 }
29#define DEBUG_DUMP_BUFFER() \
30 for (unsigned int i = 0; i < vertexBuffer.getSize(); i++) { \
Romain Guy3380cfd2013-08-15 16:57:57 -070031 ALOGD("point %d at %f %f", i, buffer[i].x, buffer[i].y); \
Chris Craik65cd6122012-12-10 17:56:27 -080032 }
33#else
34#define DEBUG_DUMP_ALPHA_BUFFER()
35#define DEBUG_DUMP_BUFFER()
36#endif
37
38#include <SkPath.h>
39#include <SkPaint.h>
40
41#include <stdlib.h>
42#include <stdint.h>
43#include <sys/types.h>
44
45#include <utils/Log.h>
46#include <utils/Trace.h>
47
48#include "PathTessellator.h"
49#include "Matrix.h"
50#include "Vector.h"
51#include "Vertex.h"
52
53namespace android {
54namespace uirenderer {
55
Chris Craik15a07a22014-01-26 13:43:53 -080056#define OUTLINE_REFINE_THRESHOLD_SQUARED (0.5f * 0.5f)
Chris Craik65cd6122012-12-10 17:56:27 -080057#define ROUND_CAP_THRESH 0.25f
58#define PI 3.1415926535897932f
59
Chris Craikfe02b4b2014-06-16 16:34:29 -070060// temporary error thresholds
61#define ERROR_DEPTH 20
62#define ERROR_SCALE 1e10
63#define ERROR_SQR_INV_THRESH 1e-20
64
Chris Craik05f3d6e2014-06-02 16:27:04 -070065void PathTessellator::extractTessellationScales(const Matrix4& transform,
66 float* scaleX, float* scaleY) {
Chris Craikfe02b4b2014-06-16 16:34:29 -070067 if (CC_LIKELY(transform.isPureTranslate())) {
68 *scaleX = 1.0f;
69 *scaleY = 1.0f;
70 } else {
Chris Craik05f3d6e2014-06-02 16:27:04 -070071 float m00 = transform.data[Matrix4::kScaleX];
72 float m01 = transform.data[Matrix4::kSkewY];
73 float m10 = transform.data[Matrix4::kSkewX];
74 float m11 = transform.data[Matrix4::kScaleY];
75 *scaleX = sqrt(m00 * m00 + m01 * m01);
76 *scaleY = sqrt(m10 * m10 + m11 * m11);
Chris Craikfe02b4b2014-06-16 16:34:29 -070077
78 LOG_ALWAYS_FATAL_IF(*scaleX > ERROR_SCALE || *scaleY > ERROR_SCALE,
79 "scales %e x %e too large for tessellation", *scaleX, *scaleY);
Chris Craik65cd6122012-12-10 17:56:27 -080080 }
81}
82
Chris Craik65cd6122012-12-10 17:56:27 -080083/**
84 * Produces a pseudo-normal for a vertex, given the normals of the two incoming lines. If the offset
85 * from each vertex in a perimeter is calculated, the resultant lines connecting the offset vertices
86 * will be offset by 1.0
87 *
88 * Note that we can't add and normalize the two vectors, that would result in a rectangle having an
89 * offset of (sqrt(2)/2, sqrt(2)/2) at each corner, instead of (1, 1)
90 *
91 * NOTE: assumes angles between normals 90 degrees or less
92 */
John Reck1aa5d2d2014-07-24 13:38:28 -070093inline static Vector2 totalOffsetFromNormals(const Vector2& normalA, const Vector2& normalB) {
Chris Craik65cd6122012-12-10 17:56:27 -080094 return (normalA + normalB) / (1 + fabs(normalA.dot(normalB)));
95}
96
97/**
98 * Structure used for storing useful information about the SkPaint and scale used for tessellating
99 */
100struct PaintInfo {
101public:
Chris Craikd6b65f62014-01-01 14:45:21 -0800102 PaintInfo(const SkPaint* paint, const mat4& transform) :
Chris Craik65cd6122012-12-10 17:56:27 -0800103 style(paint->getStyle()), cap(paint->getStrokeCap()), isAA(paint->isAntiAlias()),
Chris Craik65cd6122012-12-10 17:56:27 -0800104 halfStrokeWidth(paint->getStrokeWidth() * 0.5f), maxAlpha(1.0f) {
105 // compute inverse scales
Chris Craikfe02b4b2014-06-16 16:34:29 -0700106 if (CC_LIKELY(transform.isPureTranslate())) {
107 inverseScaleX = 1.0f;
108 inverseScaleY = 1.0f;
109 } else {
Chris Craik05f3d6e2014-06-02 16:27:04 -0700110 float scaleX, scaleY;
111 PathTessellator::extractTessellationScales(transform, &scaleX, &scaleY);
Chris Craik65cd6122012-12-10 17:56:27 -0800112 inverseScaleX = (scaleX != 0) ? (1.0f / scaleX) : 1.0f;
113 inverseScaleY = (scaleY != 0) ? (1.0f / scaleY) : 1.0f;
114 }
115
116 if (isAA && halfStrokeWidth != 0 && inverseScaleX == inverseScaleY &&
Chris Craik19a390b2013-02-27 15:43:26 -0800117 2 * halfStrokeWidth < inverseScaleX) {
Chris Craik05f3d6e2014-06-02 16:27:04 -0700118 // AA, with non-hairline stroke, width < 1 pixel. Scale alpha and treat as hairline.
Chris Craik65cd6122012-12-10 17:56:27 -0800119 maxAlpha *= (2 * halfStrokeWidth) / inverseScaleX;
120 halfStrokeWidth = 0.0f;
121 }
122 }
123
124 SkPaint::Style style;
125 SkPaint::Cap cap;
126 bool isAA;
127 float inverseScaleX;
128 float inverseScaleY;
129 float halfStrokeWidth;
130 float maxAlpha;
131
John Reck1aa5d2d2014-07-24 13:38:28 -0700132 inline void scaleOffsetForStrokeWidth(Vector2& offset) const {
Chris Craik65cd6122012-12-10 17:56:27 -0800133 if (halfStrokeWidth == 0.0f) {
134 // hairline - compensate for scale
135 offset.x *= 0.5f * inverseScaleX;
136 offset.y *= 0.5f * inverseScaleY;
137 } else {
138 offset *= halfStrokeWidth;
139 }
140 }
141
142 /**
143 * NOTE: the input will not always be a normal, especially for sharp edges - it should be the
144 * result of totalOffsetFromNormals (see documentation there)
145 */
John Reck1aa5d2d2014-07-24 13:38:28 -0700146 inline Vector2 deriveAAOffset(const Vector2& offset) const {
147 return (Vector2){offset.x * 0.5f * inverseScaleX, offset.y * 0.5f * inverseScaleY};
Chris Craik65cd6122012-12-10 17:56:27 -0800148 }
149
150 /**
151 * Returns the number of cap divisions beyond the minimum 2 (kButt_Cap/kSquareCap will return 0)
152 * Should only be used when stroking and drawing caps
153 */
154 inline int capExtraDivisions() const {
155 if (cap == SkPaint::kRound_Cap) {
156 if (halfStrokeWidth == 0.0f) return 2;
157
158 // ROUND_CAP_THRESH is the maximum error for polygonal approximation of the round cap
159 const float errConst = (-ROUND_CAP_THRESH / halfStrokeWidth + 1);
160 const float targetCosVal = 2 * errConst * errConst - 1;
161 int neededDivisions = (int)(ceilf(PI / acos(targetCosVal)/2)) * 2;
162 return neededDivisions;
163 }
164 return 0;
165 }
Chris Craikf0a59072013-11-19 18:00:46 -0800166
167 /**
168 * Outset the bounds of point data (for line endpoints or points) to account for AA stroke
169 * geometry.
170 */
Chris Craik05f3d6e2014-06-02 16:27:04 -0700171 void expandBoundsForStroke(Rect* bounds) const {
Chris Craikf0a59072013-11-19 18:00:46 -0800172 float outset = halfStrokeWidth;
173 if (outset == 0) outset = 0.5f;
Chris Craik05f3d6e2014-06-02 16:27:04 -0700174 bounds->outset(outset * inverseScaleX + Vertex::GeometryFudgeFactor(),
Chris Craik564acf72014-01-02 16:46:18 -0800175 outset * inverseScaleY + Vertex::GeometryFudgeFactor());
Chris Craikf0a59072013-11-19 18:00:46 -0800176 }
Chris Craik65cd6122012-12-10 17:56:27 -0800177};
178
179void getFillVerticesFromPerimeter(const Vector<Vertex>& perimeter, VertexBuffer& vertexBuffer) {
180 Vertex* buffer = vertexBuffer.alloc<Vertex>(perimeter.size());
181
182 int currentIndex = 0;
183 // zig zag between all previous points on the inside of the hull to create a
184 // triangle strip that fills the hull
185 int srcAindex = 0;
186 int srcBindex = perimeter.size() - 1;
187 while (srcAindex <= srcBindex) {
Chris Craik11a75672013-12-16 17:08:15 -0800188 buffer[currentIndex++] = perimeter[srcAindex];
Chris Craik65cd6122012-12-10 17:56:27 -0800189 if (srcAindex == srcBindex) break;
Chris Craik11a75672013-12-16 17:08:15 -0800190 buffer[currentIndex++] = perimeter[srcBindex];
Chris Craik65cd6122012-12-10 17:56:27 -0800191 srcAindex++;
192 srcBindex--;
193 }
194}
195
196/*
197 * Fills a vertexBuffer with non-alpha vertices, zig-zagging at each perimeter point to create a
198 * tri-strip as wide as the stroke.
199 *
200 * Uses an additional 2 vertices at the end to wrap around, closing the tri-strip
201 * (for a total of perimeter.size() * 2 + 2 vertices)
202 */
203void getStrokeVerticesFromPerimeter(const PaintInfo& paintInfo, const Vector<Vertex>& perimeter,
204 VertexBuffer& vertexBuffer) {
205 Vertex* buffer = vertexBuffer.alloc<Vertex>(perimeter.size() * 2 + 2);
206
207 int currentIndex = 0;
208 const Vertex* last = &(perimeter[perimeter.size() - 1]);
209 const Vertex* current = &(perimeter[0]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700210 Vector2 lastNormal = {current->y - last->y, last->x - current->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800211 lastNormal.normalize();
212 for (unsigned int i = 0; i < perimeter.size(); i++) {
213 const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700214 Vector2 nextNormal = {next->y - current->y, current->x - next->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800215 nextNormal.normalize();
216
John Reck1aa5d2d2014-07-24 13:38:28 -0700217 Vector2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
Chris Craik65cd6122012-12-10 17:56:27 -0800218 paintInfo.scaleOffsetForStrokeWidth(totalOffset);
219
220 Vertex::set(&buffer[currentIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700221 current->x + totalOffset.x,
222 current->y + totalOffset.y);
Chris Craik65cd6122012-12-10 17:56:27 -0800223
224 Vertex::set(&buffer[currentIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700225 current->x - totalOffset.x,
226 current->y - totalOffset.y);
Chris Craik65cd6122012-12-10 17:56:27 -0800227
228 last = current;
229 current = next;
230 lastNormal = nextNormal;
231 }
232
233 // wrap around to beginning
Chris Craik11a75672013-12-16 17:08:15 -0800234 buffer[currentIndex++] = buffer[0];
235 buffer[currentIndex++] = buffer[1];
Chris Craik65cd6122012-12-10 17:56:27 -0800236
237 DEBUG_DUMP_BUFFER();
238}
239
Chris Craik6d29c8d2013-05-08 18:35:44 -0700240static inline void storeBeginEnd(const PaintInfo& paintInfo, const Vertex& center,
John Reck1aa5d2d2014-07-24 13:38:28 -0700241 const Vector2& normal, Vertex* buffer, int& currentIndex, bool begin) {
242 Vector2 strokeOffset = normal;
Chris Craik6d29c8d2013-05-08 18:35:44 -0700243 paintInfo.scaleOffsetForStrokeWidth(strokeOffset);
244
John Reck1aa5d2d2014-07-24 13:38:28 -0700245 Vector2 referencePoint = {center.x, center.y};
Chris Craik6d29c8d2013-05-08 18:35:44 -0700246 if (paintInfo.cap == SkPaint::kSquare_Cap) {
John Reck1aa5d2d2014-07-24 13:38:28 -0700247 Vector2 rotated = {-strokeOffset.y, strokeOffset.x};
248 referencePoint += rotated * (begin ? -1 : 1);
Chris Craik6d29c8d2013-05-08 18:35:44 -0700249 }
250
251 Vertex::set(&buffer[currentIndex++], referencePoint + strokeOffset);
252 Vertex::set(&buffer[currentIndex++], referencePoint - strokeOffset);
253}
254
Chris Craik65cd6122012-12-10 17:56:27 -0800255/**
256 * Fills a vertexBuffer with non-alpha vertices similar to getStrokeVerticesFromPerimeter, except:
257 *
258 * 1 - Doesn't need to wrap around, since the input vertices are unclosed
259 *
260 * 2 - can zig-zag across 'extra' vertices at either end, to create round caps
261 */
262void getStrokeVerticesFromUnclosedVertices(const PaintInfo& paintInfo,
263 const Vector<Vertex>& vertices, VertexBuffer& vertexBuffer) {
264 const int extra = paintInfo.capExtraDivisions();
265 const int allocSize = (vertices.size() + extra) * 2;
Chris Craik65cd6122012-12-10 17:56:27 -0800266 Vertex* buffer = vertexBuffer.alloc<Vertex>(allocSize);
267
Chris Craik6d29c8d2013-05-08 18:35:44 -0700268 const int lastIndex = vertices.size() - 1;
Chris Craik65cd6122012-12-10 17:56:27 -0800269 if (extra > 0) {
270 // tessellate both round caps
Chris Craik65cd6122012-12-10 17:56:27 -0800271 float beginTheta = atan2(
Romain Guy3380cfd2013-08-15 16:57:57 -0700272 - (vertices[0].x - vertices[1].x),
273 vertices[0].y - vertices[1].y);
Chris Craik65cd6122012-12-10 17:56:27 -0800274 float endTheta = atan2(
Romain Guy3380cfd2013-08-15 16:57:57 -0700275 - (vertices[lastIndex].x - vertices[lastIndex - 1].x),
276 vertices[lastIndex].y - vertices[lastIndex - 1].y);
Chris Craik65cd6122012-12-10 17:56:27 -0800277 const float dTheta = PI / (extra + 1);
278 const float radialScale = 2.0f / (1 + cos(dTheta));
279
280 int capOffset;
281 for (int i = 0; i < extra; i++) {
282 if (i < extra / 2) {
283 capOffset = extra - 2 * i - 1;
284 } else {
285 capOffset = 2 * i - extra;
286 }
287
288 beginTheta += dTheta;
John Reck1aa5d2d2014-07-24 13:38:28 -0700289 Vector2 beginRadialOffset = {cos(beginTheta), sin(beginTheta)};
Chris Craik65cd6122012-12-10 17:56:27 -0800290 paintInfo.scaleOffsetForStrokeWidth(beginRadialOffset);
291 Vertex::set(&buffer[capOffset],
Romain Guy3380cfd2013-08-15 16:57:57 -0700292 vertices[0].x + beginRadialOffset.x,
293 vertices[0].y + beginRadialOffset.y);
Chris Craik65cd6122012-12-10 17:56:27 -0800294
295 endTheta += dTheta;
John Reck1aa5d2d2014-07-24 13:38:28 -0700296 Vector2 endRadialOffset = {cos(endTheta), sin(endTheta)};
Chris Craik65cd6122012-12-10 17:56:27 -0800297 paintInfo.scaleOffsetForStrokeWidth(endRadialOffset);
298 Vertex::set(&buffer[allocSize - 1 - capOffset],
Romain Guy3380cfd2013-08-15 16:57:57 -0700299 vertices[lastIndex].x + endRadialOffset.x,
300 vertices[lastIndex].y + endRadialOffset.y);
Chris Craik65cd6122012-12-10 17:56:27 -0800301 }
302 }
303
304 int currentIndex = extra;
Chris Craik6d29c8d2013-05-08 18:35:44 -0700305 const Vertex* last = &(vertices[0]);
306 const Vertex* current = &(vertices[1]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700307 Vector2 lastNormal = {current->y - last->y, last->x - current->x};
Chris Craik6d29c8d2013-05-08 18:35:44 -0700308 lastNormal.normalize();
309
310 storeBeginEnd(paintInfo, vertices[0], lastNormal, buffer, currentIndex, true);
311
312 for (unsigned int i = 1; i < vertices.size() - 1; i++) {
Chris Craik65cd6122012-12-10 17:56:27 -0800313 const Vertex* next = &(vertices[i + 1]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700314 Vector2 nextNormal = {next->y - current->y, current->x - next->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800315 nextNormal.normalize();
316
John Reck1aa5d2d2014-07-24 13:38:28 -0700317 Vector2 strokeOffset = totalOffsetFromNormals(lastNormal, nextNormal);
Chris Craik6d29c8d2013-05-08 18:35:44 -0700318 paintInfo.scaleOffsetForStrokeWidth(strokeOffset);
Chris Craik65cd6122012-12-10 17:56:27 -0800319
John Reck1aa5d2d2014-07-24 13:38:28 -0700320 Vector2 center = {current->x, current->y};
Chris Craik6d29c8d2013-05-08 18:35:44 -0700321 Vertex::set(&buffer[currentIndex++], center + strokeOffset);
322 Vertex::set(&buffer[currentIndex++], center - strokeOffset);
Chris Craik65cd6122012-12-10 17:56:27 -0800323
324 current = next;
325 lastNormal = nextNormal;
326 }
327
Chris Craik6d29c8d2013-05-08 18:35:44 -0700328 storeBeginEnd(paintInfo, vertices[lastIndex], lastNormal, buffer, currentIndex, false);
Chris Craik65cd6122012-12-10 17:56:27 -0800329
330 DEBUG_DUMP_BUFFER();
331}
332
333/**
334 * Populates a vertexBuffer with AlphaVertices to create an anti-aliased fill shape tessellation
Chris Craik6d29c8d2013-05-08 18:35:44 -0700335 *
Chris Craik65cd6122012-12-10 17:56:27 -0800336 * 1 - create the AA perimeter of unit width, by zig-zagging at each point around the perimeter of
337 * the shape (using 2 * perimeter.size() vertices)
338 *
339 * 2 - wrap around to the beginning to complete the perimeter (2 vertices)
340 *
341 * 3 - zig zag back and forth inside the shape to fill it (using perimeter.size() vertices)
342 */
343void getFillVerticesFromPerimeterAA(const PaintInfo& paintInfo, const Vector<Vertex>& perimeter,
Chris Craikf0a59072013-11-19 18:00:46 -0800344 VertexBuffer& vertexBuffer, float maxAlpha = 1.0f) {
Chris Craik65cd6122012-12-10 17:56:27 -0800345 AlphaVertex* buffer = vertexBuffer.alloc<AlphaVertex>(perimeter.size() * 3 + 2);
346
347 // generate alpha points - fill Alpha vertex gaps in between each point with
348 // alpha 0 vertex, offset by a scaled normal.
349 int currentIndex = 0;
350 const Vertex* last = &(perimeter[perimeter.size() - 1]);
351 const Vertex* current = &(perimeter[0]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700352 Vector2 lastNormal = {current->y - last->y, last->x - current->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800353 lastNormal.normalize();
354 for (unsigned int i = 0; i < perimeter.size(); i++) {
355 const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700356 Vector2 nextNormal = {next->y - current->y, current->x - next->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800357 nextNormal.normalize();
358
359 // AA point offset from original point is that point's normal, such that each side is offset
360 // by .5 pixels
John Reck1aa5d2d2014-07-24 13:38:28 -0700361 Vector2 totalOffset = paintInfo.deriveAAOffset(totalOffsetFromNormals(lastNormal, nextNormal));
Chris Craik65cd6122012-12-10 17:56:27 -0800362
363 AlphaVertex::set(&buffer[currentIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700364 current->x + totalOffset.x,
365 current->y + totalOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800366 0.0f);
367 AlphaVertex::set(&buffer[currentIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700368 current->x - totalOffset.x,
369 current->y - totalOffset.y,
Chris Craikf0a59072013-11-19 18:00:46 -0800370 maxAlpha);
Chris Craik65cd6122012-12-10 17:56:27 -0800371
372 last = current;
373 current = next;
374 lastNormal = nextNormal;
375 }
376
377 // wrap around to beginning
Chris Craik11a75672013-12-16 17:08:15 -0800378 buffer[currentIndex++] = buffer[0];
379 buffer[currentIndex++] = buffer[1];
Chris Craik65cd6122012-12-10 17:56:27 -0800380
381 // zig zag between all previous points on the inside of the hull to create a
382 // triangle strip that fills the hull, repeating the first inner point to
383 // create degenerate tris to start inside path
384 int srcAindex = 0;
385 int srcBindex = perimeter.size() - 1;
386 while (srcAindex <= srcBindex) {
Chris Craik11a75672013-12-16 17:08:15 -0800387 buffer[currentIndex++] = buffer[srcAindex * 2 + 1];
Chris Craik65cd6122012-12-10 17:56:27 -0800388 if (srcAindex == srcBindex) break;
Chris Craik11a75672013-12-16 17:08:15 -0800389 buffer[currentIndex++] = buffer[srcBindex * 2 + 1];
Chris Craik65cd6122012-12-10 17:56:27 -0800390 srcAindex++;
391 srcBindex--;
392 }
393
394 DEBUG_DUMP_BUFFER();
395}
396
397/**
398 * Stores geometry for a single, AA-perimeter (potentially rounded) cap
399 *
400 * For explanation of constants and general methodoloyg, see comments for
401 * getStrokeVerticesFromUnclosedVerticesAA() below.
402 */
Chris Craik6d29c8d2013-05-08 18:35:44 -0700403inline static void storeCapAA(const PaintInfo& paintInfo, const Vector<Vertex>& vertices,
John Reck1aa5d2d2014-07-24 13:38:28 -0700404 AlphaVertex* buffer, bool isFirst, Vector2 normal, int offset) {
Chris Craik65cd6122012-12-10 17:56:27 -0800405 const int extra = paintInfo.capExtraDivisions();
406 const int extraOffset = (extra + 1) / 2;
407 const int capIndex = isFirst
408 ? 2 * offset + 6 + 2 * (extra + extraOffset)
409 : offset + 2 + 2 * extraOffset;
410 if (isFirst) normal *= -1;
411
412 // TODO: this normal should be scaled by radialScale if extra != 0, see totalOffsetFromNormals()
John Reck1aa5d2d2014-07-24 13:38:28 -0700413 Vector2 AAOffset = paintInfo.deriveAAOffset(normal);
Chris Craik65cd6122012-12-10 17:56:27 -0800414
John Reck1aa5d2d2014-07-24 13:38:28 -0700415 Vector2 strokeOffset = normal;
Chris Craik65cd6122012-12-10 17:56:27 -0800416 paintInfo.scaleOffsetForStrokeWidth(strokeOffset);
John Reck1aa5d2d2014-07-24 13:38:28 -0700417 Vector2 outerOffset = strokeOffset + AAOffset;
418 Vector2 innerOffset = strokeOffset - AAOffset;
Chris Craik65cd6122012-12-10 17:56:27 -0800419
John Reck1aa5d2d2014-07-24 13:38:28 -0700420 Vector2 capAAOffset = {0, 0};
Chris Craik65cd6122012-12-10 17:56:27 -0800421 if (paintInfo.cap != SkPaint::kRound_Cap) {
422 // if the cap is square or butt, the inside primary cap vertices will be inset in two
423 // directions - both normal to the stroke, and parallel to it.
John Reck1aa5d2d2014-07-24 13:38:28 -0700424 capAAOffset = (Vector2){-AAOffset.y, AAOffset.x};
Chris Craik65cd6122012-12-10 17:56:27 -0800425 }
426
427 // determine referencePoint, the center point for the 4 primary cap vertices
428 const Vertex* point = isFirst ? vertices.begin() : (vertices.end() - 1);
John Reck1aa5d2d2014-07-24 13:38:28 -0700429 Vector2 referencePoint = {point->x, point->y};
Chris Craik65cd6122012-12-10 17:56:27 -0800430 if (paintInfo.cap == SkPaint::kSquare_Cap) {
431 // To account for square cap, move the primary cap vertices (that create the AA edge) by the
432 // stroke offset vector (rotated to be parallel to the stroke)
John Reck1aa5d2d2014-07-24 13:38:28 -0700433 Vector2 rotated = {-strokeOffset.y, strokeOffset.x};
434 referencePoint += rotated;
Chris Craik65cd6122012-12-10 17:56:27 -0800435 }
436
437 AlphaVertex::set(&buffer[capIndex + 0],
438 referencePoint.x + outerOffset.x + capAAOffset.x,
439 referencePoint.y + outerOffset.y + capAAOffset.y,
440 0.0f);
441 AlphaVertex::set(&buffer[capIndex + 1],
442 referencePoint.x + innerOffset.x - capAAOffset.x,
443 referencePoint.y + innerOffset.y - capAAOffset.y,
444 paintInfo.maxAlpha);
445
446 bool isRound = paintInfo.cap == SkPaint::kRound_Cap;
447
448 const int postCapIndex = (isRound && isFirst) ? (2 * extraOffset - 2) : capIndex + (2 * extra);
449 AlphaVertex::set(&buffer[postCapIndex + 2],
450 referencePoint.x - outerOffset.x + capAAOffset.x,
451 referencePoint.y - outerOffset.y + capAAOffset.y,
452 0.0f);
453 AlphaVertex::set(&buffer[postCapIndex + 3],
454 referencePoint.x - innerOffset.x - capAAOffset.x,
455 referencePoint.y - innerOffset.y - capAAOffset.y,
456 paintInfo.maxAlpha);
457
458 if (isRound) {
459 const float dTheta = PI / (extra + 1);
460 const float radialScale = 2.0f / (1 + cos(dTheta));
461 float theta = atan2(normal.y, normal.x);
462 int capPerimIndex = capIndex + 2;
463
464 for (int i = 0; i < extra; i++) {
465 theta += dTheta;
466
John Reck1aa5d2d2014-07-24 13:38:28 -0700467 Vector2 radialOffset = {cos(theta), sin(theta)};
Chris Craik65cd6122012-12-10 17:56:27 -0800468
469 // scale to compensate for pinching at sharp angles, see totalOffsetFromNormals()
470 radialOffset *= radialScale;
471
472 AAOffset = paintInfo.deriveAAOffset(radialOffset);
473 paintInfo.scaleOffsetForStrokeWidth(radialOffset);
474 AlphaVertex::set(&buffer[capPerimIndex++],
475 referencePoint.x + radialOffset.x + AAOffset.x,
476 referencePoint.y + radialOffset.y + AAOffset.y,
477 0.0f);
478 AlphaVertex::set(&buffer[capPerimIndex++],
479 referencePoint.x + radialOffset.x - AAOffset.x,
480 referencePoint.y + radialOffset.y - AAOffset.y,
481 paintInfo.maxAlpha);
482
483 if (isFirst && i == extra - extraOffset) {
484 //copy most recent two points to first two points
Chris Craik11a75672013-12-16 17:08:15 -0800485 buffer[0] = buffer[capPerimIndex - 2];
486 buffer[1] = buffer[capPerimIndex - 1];
Chris Craik65cd6122012-12-10 17:56:27 -0800487
488 capPerimIndex = 2; // start writing the rest of the round cap at index 2
489 }
490 }
491
492 if (isFirst) {
493 const int startCapFillIndex = capIndex + 2 * (extra - extraOffset) + 4;
494 int capFillIndex = startCapFillIndex;
495 for (int i = 0; i < extra + 2; i += 2) {
Chris Craik11a75672013-12-16 17:08:15 -0800496 buffer[capFillIndex++] = buffer[1 + i];
Chris Craik65cd6122012-12-10 17:56:27 -0800497 // TODO: to support odd numbers of divisions, break here on the last iteration
Chris Craik11a75672013-12-16 17:08:15 -0800498 buffer[capFillIndex++] = buffer[startCapFillIndex - 3 - i];
Chris Craik65cd6122012-12-10 17:56:27 -0800499 }
500 } else {
501 int capFillIndex = 6 * vertices.size() + 2 + 6 * extra - (extra + 2);
502 for (int i = 0; i < extra + 2; i += 2) {
Chris Craik11a75672013-12-16 17:08:15 -0800503 buffer[capFillIndex++] = buffer[capIndex + 1 + i];
Chris Craik65cd6122012-12-10 17:56:27 -0800504 // TODO: to support odd numbers of divisions, break here on the last iteration
Chris Craik11a75672013-12-16 17:08:15 -0800505 buffer[capFillIndex++] = buffer[capIndex + 3 + 2 * extra - i];
Chris Craik65cd6122012-12-10 17:56:27 -0800506 }
507 }
508 return;
509 }
510 if (isFirst) {
Chris Craik11a75672013-12-16 17:08:15 -0800511 buffer[0] = buffer[postCapIndex + 2];
512 buffer[1] = buffer[postCapIndex + 3];
513 buffer[postCapIndex + 4] = buffer[1]; // degenerate tris (the only two!)
514 buffer[postCapIndex + 5] = buffer[postCapIndex + 1];
Chris Craik65cd6122012-12-10 17:56:27 -0800515 } else {
Chris Craik11a75672013-12-16 17:08:15 -0800516 buffer[6 * vertices.size()] = buffer[postCapIndex + 1];
517 buffer[6 * vertices.size() + 1] = buffer[postCapIndex + 3];
Chris Craik65cd6122012-12-10 17:56:27 -0800518 }
519}
520
521/*
522the geometry for an aa, capped stroke consists of the following:
523
524 # vertices | function
525----------------------------------------------------------------------
526a) 2 | Start AA perimeter
527b) 2, 2 * roundDivOff | First half of begin cap's perimeter
528 |
529 2 * middlePts | 'Outer' or 'Top' AA perimeter half (between caps)
530 |
531a) 4 | End cap's
532b) 2, 2 * roundDivs, 2 | AA perimeter
533 |
534 2 * middlePts | 'Inner' or 'bottom' AA perimeter half
535 |
536a) 6 | Begin cap's perimeter
537b) 2, 2*(rD - rDO + 1), | Last half of begin cap's perimeter
538 roundDivs, 2 |
539 |
540 2 * middlePts | Stroke's full opacity center strip
541 |
542a) 2 | end stroke
543b) 2, roundDivs | (and end cap fill, for round)
544
545Notes:
546* rows starting with 'a)' denote the Butt or Square cap vertex use, 'b)' denote Round
547
548* 'middlePts' is (number of points in the unclosed input vertex list, minus 2) times two
549
550* 'roundDivs' or 'rD' is the number of extra vertices (beyond the minimum of 2) that define the
551 round cap's shape, and is at least two. This will increase with cap size to sufficiently
552 define the cap's level of tessellation.
553
554* 'roundDivOffset' or 'rDO' is the point about halfway along the start cap's round perimeter, where
555 the stream of vertices for the AA perimeter starts. By starting and ending the perimeter at
556 this offset, the fill of the stroke is drawn from this point with minimal extra vertices.
557
558This means the outer perimeter starts at:
559 outerIndex = (2) OR (2 + 2 * roundDivOff)
560the inner perimeter (since it is filled in reverse) starts at:
561 innerIndex = outerIndex + (4 * middlePts) + ((4) OR (4 + 2 * roundDivs)) - 1
562the stroke starts at:
563 strokeIndex = innerIndex + 1 + ((6) OR (6 + 3 * roundDivs - 2 * roundDivOffset))
564
565The total needed allocated space is either:
566 2 + 4 + 6 + 2 + 3 * (2 * middlePts) = 14 + 6 * middlePts = 2 + 6 * pts
567or, for rounded caps:
568 (2 + 2 * rDO) + (4 + 2 * rD) + (2 * (rD - rDO + 1)
569 + roundDivs + 4) + (2 + roundDivs) + 3 * (2 * middlePts)
570 = 14 + 6 * middlePts + 6 * roundDivs
571 = 2 + 6 * pts + 6 * roundDivs
572 */
573void getStrokeVerticesFromUnclosedVerticesAA(const PaintInfo& paintInfo,
574 const Vector<Vertex>& vertices, VertexBuffer& vertexBuffer) {
575
576 const int extra = paintInfo.capExtraDivisions();
577 const int allocSize = 6 * vertices.size() + 2 + 6 * extra;
578
579 AlphaVertex* buffer = vertexBuffer.alloc<AlphaVertex>(allocSize);
580
581 const int extraOffset = (extra + 1) / 2;
582 int offset = 2 * (vertices.size() - 2);
583 // there is no outer/inner here, using them for consistency with below approach
584 int currentAAOuterIndex = 2 + 2 * extraOffset;
585 int currentAAInnerIndex = currentAAOuterIndex + (2 * offset) + 3 + (2 * extra);
586 int currentStrokeIndex = currentAAInnerIndex + 7 + (3 * extra - 2 * extraOffset);
587
588 const Vertex* last = &(vertices[0]);
589 const Vertex* current = &(vertices[1]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700590 Vector2 lastNormal = {current->y - last->y, last->x - current->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800591 lastNormal.normalize();
592
593 // TODO: use normal from bezier traversal for cap, instead of from vertices
594 storeCapAA(paintInfo, vertices, buffer, true, lastNormal, offset);
595
596 for (unsigned int i = 1; i < vertices.size() - 1; i++) {
597 const Vertex* next = &(vertices[i + 1]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700598 Vector2 nextNormal = {next->y - current->y, current->x - next->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800599 nextNormal.normalize();
600
John Reck1aa5d2d2014-07-24 13:38:28 -0700601 Vector2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
602 Vector2 AAOffset = paintInfo.deriveAAOffset(totalOffset);
Chris Craik65cd6122012-12-10 17:56:27 -0800603
John Reck1aa5d2d2014-07-24 13:38:28 -0700604 Vector2 innerOffset = totalOffset;
Chris Craik65cd6122012-12-10 17:56:27 -0800605 paintInfo.scaleOffsetForStrokeWidth(innerOffset);
John Reck1aa5d2d2014-07-24 13:38:28 -0700606 Vector2 outerOffset = innerOffset + AAOffset;
Chris Craik65cd6122012-12-10 17:56:27 -0800607 innerOffset -= AAOffset;
608
609 AlphaVertex::set(&buffer[currentAAOuterIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700610 current->x + outerOffset.x,
611 current->y + outerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800612 0.0f);
613 AlphaVertex::set(&buffer[currentAAOuterIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700614 current->x + innerOffset.x,
615 current->y + innerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800616 paintInfo.maxAlpha);
617
618 AlphaVertex::set(&buffer[currentStrokeIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700619 current->x + innerOffset.x,
620 current->y + innerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800621 paintInfo.maxAlpha);
622 AlphaVertex::set(&buffer[currentStrokeIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700623 current->x - innerOffset.x,
624 current->y - innerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800625 paintInfo.maxAlpha);
626
627 AlphaVertex::set(&buffer[currentAAInnerIndex--],
Romain Guy3380cfd2013-08-15 16:57:57 -0700628 current->x - innerOffset.x,
629 current->y - innerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800630 paintInfo.maxAlpha);
631 AlphaVertex::set(&buffer[currentAAInnerIndex--],
Romain Guy3380cfd2013-08-15 16:57:57 -0700632 current->x - outerOffset.x,
633 current->y - outerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800634 0.0f);
635
636 current = next;
637 lastNormal = nextNormal;
638 }
639
640 // TODO: use normal from bezier traversal for cap, instead of from vertices
641 storeCapAA(paintInfo, vertices, buffer, false, lastNormal, offset);
642
643 DEBUG_DUMP_ALPHA_BUFFER();
644}
645
646
647void getStrokeVerticesFromPerimeterAA(const PaintInfo& paintInfo, const Vector<Vertex>& perimeter,
648 VertexBuffer& vertexBuffer) {
649 AlphaVertex* buffer = vertexBuffer.alloc<AlphaVertex>(6 * perimeter.size() + 8);
650
651 int offset = 2 * perimeter.size() + 3;
652 int currentAAOuterIndex = 0;
653 int currentStrokeIndex = offset;
654 int currentAAInnerIndex = offset * 2;
655
656 const Vertex* last = &(perimeter[perimeter.size() - 1]);
657 const Vertex* current = &(perimeter[0]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700658 Vector2 lastNormal = {current->y - last->y, last->x - current->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800659 lastNormal.normalize();
660 for (unsigned int i = 0; i < perimeter.size(); i++) {
661 const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700662 Vector2 nextNormal = {next->y - current->y, current->x - next->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800663 nextNormal.normalize();
664
John Reck1aa5d2d2014-07-24 13:38:28 -0700665 Vector2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
666 Vector2 AAOffset = paintInfo.deriveAAOffset(totalOffset);
Chris Craik65cd6122012-12-10 17:56:27 -0800667
John Reck1aa5d2d2014-07-24 13:38:28 -0700668 Vector2 innerOffset = totalOffset;
Chris Craik65cd6122012-12-10 17:56:27 -0800669 paintInfo.scaleOffsetForStrokeWidth(innerOffset);
John Reck1aa5d2d2014-07-24 13:38:28 -0700670 Vector2 outerOffset = innerOffset + AAOffset;
Chris Craik65cd6122012-12-10 17:56:27 -0800671 innerOffset -= AAOffset;
672
673 AlphaVertex::set(&buffer[currentAAOuterIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700674 current->x + outerOffset.x,
675 current->y + outerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800676 0.0f);
677 AlphaVertex::set(&buffer[currentAAOuterIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700678 current->x + innerOffset.x,
679 current->y + innerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800680 paintInfo.maxAlpha);
681
682 AlphaVertex::set(&buffer[currentStrokeIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700683 current->x + innerOffset.x,
684 current->y + innerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800685 paintInfo.maxAlpha);
686 AlphaVertex::set(&buffer[currentStrokeIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700687 current->x - innerOffset.x,
688 current->y - innerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800689 paintInfo.maxAlpha);
690
691 AlphaVertex::set(&buffer[currentAAInnerIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700692 current->x - innerOffset.x,
693 current->y - innerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800694 paintInfo.maxAlpha);
695 AlphaVertex::set(&buffer[currentAAInnerIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700696 current->x - outerOffset.x,
697 current->y - outerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800698 0.0f);
699
700 last = current;
701 current = next;
702 lastNormal = nextNormal;
703 }
704
705 // wrap each strip around to beginning, creating degenerate tris to bridge strips
Chris Craik11a75672013-12-16 17:08:15 -0800706 buffer[currentAAOuterIndex++] = buffer[0];
707 buffer[currentAAOuterIndex++] = buffer[1];
708 buffer[currentAAOuterIndex++] = buffer[1];
Chris Craik65cd6122012-12-10 17:56:27 -0800709
Chris Craik11a75672013-12-16 17:08:15 -0800710 buffer[currentStrokeIndex++] = buffer[offset];
711 buffer[currentStrokeIndex++] = buffer[offset + 1];
712 buffer[currentStrokeIndex++] = buffer[offset + 1];
Chris Craik65cd6122012-12-10 17:56:27 -0800713
Chris Craik11a75672013-12-16 17:08:15 -0800714 buffer[currentAAInnerIndex++] = buffer[2 * offset];
715 buffer[currentAAInnerIndex++] = buffer[2 * offset + 1];
Chris Craik65cd6122012-12-10 17:56:27 -0800716 // don't need to create last degenerate tri
717
718 DEBUG_DUMP_ALPHA_BUFFER();
719}
720
721void PathTessellator::tessellatePath(const SkPath &path, const SkPaint* paint,
Chris Craikd6b65f62014-01-01 14:45:21 -0800722 const mat4& transform, VertexBuffer& vertexBuffer) {
Chris Craik65cd6122012-12-10 17:56:27 -0800723 ATRACE_CALL();
724
725 const PaintInfo paintInfo(paint, transform);
726
727 Vector<Vertex> tempVertices;
728 float threshInvScaleX = paintInfo.inverseScaleX;
729 float threshInvScaleY = paintInfo.inverseScaleY;
730 if (paintInfo.style == SkPaint::kStroke_Style) {
731 // alter the bezier recursion threshold values we calculate in order to compensate for
732 // expansion done after the path vertices are found
733 SkRect bounds = path.getBounds();
734 if (!bounds.isEmpty()) {
735 threshInvScaleX *= bounds.width() / (bounds.width() + paint->getStrokeWidth());
736 threshInvScaleY *= bounds.height() / (bounds.height() + paint->getStrokeWidth());
737 }
738 }
739
740 // force close if we're filling the path, since fill path expects closed perimeter.
741 bool forceClose = paintInfo.style != SkPaint::kStroke_Style;
742 bool wasClosed = approximatePathOutlineVertices(path, forceClose,
Chris Craik15a07a22014-01-26 13:43:53 -0800743 threshInvScaleX * threshInvScaleX, threshInvScaleY * threshInvScaleY,
744 OUTLINE_REFINE_THRESHOLD_SQUARED, tempVertices);
Chris Craik65cd6122012-12-10 17:56:27 -0800745
746 if (!tempVertices.size()) {
747 // path was empty, return without allocating vertex buffer
748 return;
749 }
750
751#if VERTEX_DEBUG
752 for (unsigned int i = 0; i < tempVertices.size(); i++) {
753 ALOGD("orig path: point at %f %f",
Romain Guy3380cfd2013-08-15 16:57:57 -0700754 tempVertices[i].x, tempVertices[i].y);
Chris Craik65cd6122012-12-10 17:56:27 -0800755 }
756#endif
757
758 if (paintInfo.style == SkPaint::kStroke_Style) {
759 if (!paintInfo.isAA) {
760 if (wasClosed) {
761 getStrokeVerticesFromPerimeter(paintInfo, tempVertices, vertexBuffer);
762 } else {
763 getStrokeVerticesFromUnclosedVertices(paintInfo, tempVertices, vertexBuffer);
764 }
765
766 } else {
767 if (wasClosed) {
768 getStrokeVerticesFromPerimeterAA(paintInfo, tempVertices, vertexBuffer);
769 } else {
770 getStrokeVerticesFromUnclosedVerticesAA(paintInfo, tempVertices, vertexBuffer);
771 }
772 }
773 } else {
774 // For kStrokeAndFill style, the path should be adjusted externally.
775 // It will be treated as a fill here.
776 if (!paintInfo.isAA) {
777 getFillVerticesFromPerimeter(tempVertices, vertexBuffer);
778 } else {
779 getFillVerticesFromPerimeterAA(paintInfo, tempVertices, vertexBuffer);
780 }
781 }
Chris Craik05f3d6e2014-06-02 16:27:04 -0700782
783 Rect bounds(path.getBounds());
784 paintInfo.expandBoundsForStroke(&bounds);
785 vertexBuffer.setBounds(bounds);
Chris Craik65cd6122012-12-10 17:56:27 -0800786}
787
Chris Craik6d29c8d2013-05-08 18:35:44 -0700788template <class TYPE>
789static void instanceVertices(VertexBuffer& srcBuffer, VertexBuffer& dstBuffer,
Chris Craik05f3d6e2014-06-02 16:27:04 -0700790 const float* points, int count, Rect& bounds) {
Chris Craik6d29c8d2013-05-08 18:35:44 -0700791 bounds.set(points[0], points[1], points[0], points[1]);
792
793 int numPoints = count / 2;
794 int verticesPerPoint = srcBuffer.getVertexCount();
795 dstBuffer.alloc<TYPE>(numPoints * verticesPerPoint + (numPoints - 1) * 2);
796
797 for (int i = 0; i < count; i += 2) {
Chris Craikc93e45c2014-07-16 10:15:56 -0700798 bounds.expandToCoverVertex(points[i + 0], points[i + 1]);
Chris Craik6d29c8d2013-05-08 18:35:44 -0700799 dstBuffer.copyInto<TYPE>(srcBuffer, points[i + 0], points[i + 1]);
800 }
801 dstBuffer.createDegenerateSeparators<TYPE>(verticesPerPoint);
802}
803
Chris Craikd218a922014-01-02 17:13:34 -0800804void PathTessellator::tessellatePoints(const float* points, int count, const SkPaint* paint,
Chris Craik05f3d6e2014-06-02 16:27:04 -0700805 const mat4& transform, VertexBuffer& vertexBuffer) {
Chris Craik6d29c8d2013-05-08 18:35:44 -0700806 const PaintInfo paintInfo(paint, transform);
807
808 // determine point shape
809 SkPath path;
810 float radius = paintInfo.halfStrokeWidth;
811 if (radius == 0.0f) radius = 0.25f;
812
813 if (paintInfo.cap == SkPaint::kRound_Cap) {
814 path.addCircle(0, 0, radius);
815 } else {
816 path.addRect(-radius, -radius, radius, radius);
817 }
818
819 // calculate outline
820 Vector<Vertex> outlineVertices;
821 approximatePathOutlineVertices(path, true,
822 paintInfo.inverseScaleX * paintInfo.inverseScaleX,
Chris Craik15a07a22014-01-26 13:43:53 -0800823 paintInfo.inverseScaleY * paintInfo.inverseScaleY,
824 OUTLINE_REFINE_THRESHOLD_SQUARED, outlineVertices);
Chris Craik6d29c8d2013-05-08 18:35:44 -0700825
826 if (!outlineVertices.size()) return;
827
Chris Craik05f3d6e2014-06-02 16:27:04 -0700828 Rect bounds;
Chris Craik6d29c8d2013-05-08 18:35:44 -0700829 // tessellate, then duplicate outline across points
830 int numPoints = count / 2;
831 VertexBuffer tempBuffer;
832 if (!paintInfo.isAA) {
833 getFillVerticesFromPerimeter(outlineVertices, tempBuffer);
834 instanceVertices<Vertex>(tempBuffer, vertexBuffer, points, count, bounds);
835 } else {
Chris Craikf0a59072013-11-19 18:00:46 -0800836 // note: pass maxAlpha directly, since we want fill to be alpha modulated
837 getFillVerticesFromPerimeterAA(paintInfo, outlineVertices, tempBuffer, paintInfo.maxAlpha);
Chris Craik6d29c8d2013-05-08 18:35:44 -0700838 instanceVertices<AlphaVertex>(tempBuffer, vertexBuffer, points, count, bounds);
839 }
840
Chris Craikf0a59072013-11-19 18:00:46 -0800841 // expand bounds from vertex coords to pixel data
Chris Craik05f3d6e2014-06-02 16:27:04 -0700842 paintInfo.expandBoundsForStroke(&bounds);
843 vertexBuffer.setBounds(bounds);
Chris Craik65cd6122012-12-10 17:56:27 -0800844}
845
Chris Craikd218a922014-01-02 17:13:34 -0800846void PathTessellator::tessellateLines(const float* points, int count, const SkPaint* paint,
Chris Craik05f3d6e2014-06-02 16:27:04 -0700847 const mat4& transform, VertexBuffer& vertexBuffer) {
Chris Craik65cd6122012-12-10 17:56:27 -0800848 ATRACE_CALL();
849 const PaintInfo paintInfo(paint, transform);
850
851 const int extra = paintInfo.capExtraDivisions();
852 int numLines = count / 4;
853 int lineAllocSize;
854 // pre-allocate space for lines in the buffer, and degenerate tris in between
855 if (paintInfo.isAA) {
856 lineAllocSize = 6 * (2) + 2 + 6 * extra;
857 vertexBuffer.alloc<AlphaVertex>(numLines * lineAllocSize + (numLines - 1) * 2);
858 } else {
859 lineAllocSize = 2 * ((2) + extra);
860 vertexBuffer.alloc<Vertex>(numLines * lineAllocSize + (numLines - 1) * 2);
861 }
862
863 Vector<Vertex> tempVertices;
864 tempVertices.push();
865 tempVertices.push();
866 Vertex* tempVerticesData = tempVertices.editArray();
Chris Craik05f3d6e2014-06-02 16:27:04 -0700867 Rect bounds;
Chris Craik65cd6122012-12-10 17:56:27 -0800868 bounds.set(points[0], points[1], points[0], points[1]);
869 for (int i = 0; i < count; i += 4) {
870 Vertex::set(&(tempVerticesData[0]), points[i + 0], points[i + 1]);
871 Vertex::set(&(tempVerticesData[1]), points[i + 2], points[i + 3]);
872
873 if (paintInfo.isAA) {
874 getStrokeVerticesFromUnclosedVerticesAA(paintInfo, tempVertices, vertexBuffer);
875 } else {
876 getStrokeVerticesFromUnclosedVertices(paintInfo, tempVertices, vertexBuffer);
877 }
878
879 // calculate bounds
Chris Craikc93e45c2014-07-16 10:15:56 -0700880 bounds.expandToCoverVertex(tempVerticesData[0].x, tempVerticesData[0].y);
881 bounds.expandToCoverVertex(tempVerticesData[1].x, tempVerticesData[1].y);
Chris Craik65cd6122012-12-10 17:56:27 -0800882 }
883
Chris Craik65cd6122012-12-10 17:56:27 -0800884 // since multiple objects tessellated into buffer, separate them with degen tris
885 if (paintInfo.isAA) {
886 vertexBuffer.createDegenerateSeparators<AlphaVertex>(lineAllocSize);
887 } else {
888 vertexBuffer.createDegenerateSeparators<Vertex>(lineAllocSize);
889 }
Chris Craikf0a59072013-11-19 18:00:46 -0800890
891 // expand bounds from vertex coords to pixel data
Chris Craik05f3d6e2014-06-02 16:27:04 -0700892 paintInfo.expandBoundsForStroke(&bounds);
893 vertexBuffer.setBounds(bounds);
Chris Craik65cd6122012-12-10 17:56:27 -0800894}
895
896///////////////////////////////////////////////////////////////////////////////
897// Simple path line approximation
898///////////////////////////////////////////////////////////////////////////////
899
Chris Craik15a07a22014-01-26 13:43:53 -0800900bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, float thresholdSquared,
901 Vector<Vertex>& outputVertices) {
902 return approximatePathOutlineVertices(path, true, 1.0f, 1.0f, thresholdSquared, outputVertices);
903}
904
Chris Craik65cd6122012-12-10 17:56:27 -0800905void pushToVector(Vector<Vertex>& vertices, float x, float y) {
906 // TODO: make this not yuck
907 vertices.push();
908 Vertex* newVertex = &(vertices.editArray()[vertices.size() - 1]);
909 Vertex::set(newVertex, x, y);
910}
911
912bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool forceClose,
Chris Craik15a07a22014-01-26 13:43:53 -0800913 float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared,
914 Vector<Vertex>& outputVertices) {
Chris Craik65cd6122012-12-10 17:56:27 -0800915 ATRACE_CALL();
916
Chris Craikfe02b4b2014-06-16 16:34:29 -0700917 LOG_ALWAYS_FATAL_IF(sqrInvScaleX < ERROR_SQR_INV_THRESH || sqrInvScaleY < ERROR_SQR_INV_THRESH,
918 "Invalid scale factors used for approx %e, %e", sqrInvScaleX, sqrInvScaleY);
919
Chris Craik65cd6122012-12-10 17:56:27 -0800920 // TODO: to support joins other than sharp miter, join vertices should be labelled in the
921 // perimeter, or resolved into more vertices. Reconsider forceClose-ing in that case.
922 SkPath::Iter iter(path, forceClose);
923 SkPoint pts[4];
924 SkPath::Verb v;
925 while (SkPath::kDone_Verb != (v = iter.next(pts))) {
926 switch (v) {
927 case SkPath::kMove_Verb:
928 pushToVector(outputVertices, pts[0].x(), pts[0].y());
929 ALOGV("Move to pos %f %f", pts[0].x(), pts[0].y());
930 break;
931 case SkPath::kClose_Verb:
932 ALOGV("Close at pos %f %f", pts[0].x(), pts[0].y());
933 break;
934 case SkPath::kLine_Verb:
935 ALOGV("kLine_Verb %f %f -> %f %f", pts[0].x(), pts[0].y(), pts[1].x(), pts[1].y());
936 pushToVector(outputVertices, pts[1].x(), pts[1].y());
937 break;
938 case SkPath::kQuad_Verb:
939 ALOGV("kQuad_Verb");
940 recursiveQuadraticBezierVertices(
941 pts[0].x(), pts[0].y(),
942 pts[2].x(), pts[2].y(),
943 pts[1].x(), pts[1].y(),
Chris Craik15a07a22014-01-26 13:43:53 -0800944 sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices);
Chris Craik65cd6122012-12-10 17:56:27 -0800945 break;
946 case SkPath::kCubic_Verb:
947 ALOGV("kCubic_Verb");
948 recursiveCubicBezierVertices(
949 pts[0].x(), pts[0].y(),
950 pts[1].x(), pts[1].y(),
951 pts[3].x(), pts[3].y(),
952 pts[2].x(), pts[2].y(),
Chris Craik15a07a22014-01-26 13:43:53 -0800953 sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices);
Chris Craik65cd6122012-12-10 17:56:27 -0800954 break;
955 default:
956 break;
957 }
958 }
959
960 int size = outputVertices.size();
Romain Guy3380cfd2013-08-15 16:57:57 -0700961 if (size >= 2 && outputVertices[0].x == outputVertices[size - 1].x &&
962 outputVertices[0].y == outputVertices[size - 1].y) {
Chris Craik65cd6122012-12-10 17:56:27 -0800963 outputVertices.pop();
964 return true;
965 }
966 return false;
967}
968
969///////////////////////////////////////////////////////////////////////////////
970// Bezier approximation
971///////////////////////////////////////////////////////////////////////////////
972
973void PathTessellator::recursiveCubicBezierVertices(
974 float p1x, float p1y, float c1x, float c1y,
975 float p2x, float p2y, float c2x, float c2y,
Chris Craik15a07a22014-01-26 13:43:53 -0800976 float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared,
Chris Craik9c3dd622014-06-11 17:24:51 -0700977 Vector<Vertex>& outputVertices, int depth) {
Chris Craikfe02b4b2014-06-16 16:34:29 -0700978 LOG_ALWAYS_FATAL_IF(depth >= ERROR_DEPTH, "ERROR DEPTH exceeded: cubic approx, invscale %e x %e, vertcount %d",
979 sqrInvScaleX, sqrInvScaleY, outputVertices.size());
980
Chris Craik65cd6122012-12-10 17:56:27 -0800981 float dx = p2x - p1x;
982 float dy = p2y - p1y;
983 float d1 = fabs((c1x - p2x) * dy - (c1y - p2y) * dx);
984 float d2 = fabs((c2x - p2x) * dy - (c2y - p2y) * dx);
985 float d = d1 + d2;
986
987 // multiplying by sqrInvScaleY/X equivalent to multiplying in dimensional scale factors
Chris Craikfe02b4b2014-06-16 16:34:29 -0700988 if (d * d < thresholdSquared * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) {
Chris Craik65cd6122012-12-10 17:56:27 -0800989 // below thresh, draw line by adding endpoint
990 pushToVector(outputVertices, p2x, p2y);
991 } else {
992 float p1c1x = (p1x + c1x) * 0.5f;
993 float p1c1y = (p1y + c1y) * 0.5f;
994 float p2c2x = (p2x + c2x) * 0.5f;
995 float p2c2y = (p2y + c2y) * 0.5f;
996
997 float c1c2x = (c1x + c2x) * 0.5f;
998 float c1c2y = (c1y + c2y) * 0.5f;
999
1000 float p1c1c2x = (p1c1x + c1c2x) * 0.5f;
1001 float p1c1c2y = (p1c1y + c1c2y) * 0.5f;
1002
1003 float p2c1c2x = (p2c2x + c1c2x) * 0.5f;
1004 float p2c1c2y = (p2c2y + c1c2y) * 0.5f;
1005
1006 float mx = (p1c1c2x + p2c1c2x) * 0.5f;
1007 float my = (p1c1c2y + p2c1c2y) * 0.5f;
1008
1009 recursiveCubicBezierVertices(
1010 p1x, p1y, p1c1x, p1c1y,
1011 mx, my, p1c1c2x, p1c1c2y,
Chris Craik9c3dd622014-06-11 17:24:51 -07001012 sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices, depth + 1);
Chris Craik65cd6122012-12-10 17:56:27 -08001013 recursiveCubicBezierVertices(
1014 mx, my, p2c1c2x, p2c1c2y,
1015 p2x, p2y, p2c2x, p2c2y,
Chris Craik9c3dd622014-06-11 17:24:51 -07001016 sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices, depth + 1);
Chris Craik65cd6122012-12-10 17:56:27 -08001017 }
1018}
1019
1020void PathTessellator::recursiveQuadraticBezierVertices(
1021 float ax, float ay,
1022 float bx, float by,
1023 float cx, float cy,
Chris Craik15a07a22014-01-26 13:43:53 -08001024 float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared,
Chris Craik9c3dd622014-06-11 17:24:51 -07001025 Vector<Vertex>& outputVertices, int depth) {
Chris Craikfe02b4b2014-06-16 16:34:29 -07001026 LOG_ALWAYS_FATAL_IF(depth >= ERROR_DEPTH, "ERROR_DEPTH exceeded: quadratic approx, invscale %e x %e, vertcount %d",
1027 sqrInvScaleX, sqrInvScaleY, outputVertices.size());
1028
Chris Craik65cd6122012-12-10 17:56:27 -08001029 float dx = bx - ax;
1030 float dy = by - ay;
1031 float d = (cx - bx) * dy - (cy - by) * dx;
1032
Chris Craikfe02b4b2014-06-16 16:34:29 -07001033 if (d * d < thresholdSquared * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) {
Chris Craik65cd6122012-12-10 17:56:27 -08001034 // below thresh, draw line by adding endpoint
1035 pushToVector(outputVertices, bx, by);
1036 } else {
1037 float acx = (ax + cx) * 0.5f;
1038 float bcx = (bx + cx) * 0.5f;
1039 float acy = (ay + cy) * 0.5f;
1040 float bcy = (by + cy) * 0.5f;
1041
1042 // midpoint
1043 float mx = (acx + bcx) * 0.5f;
1044 float my = (acy + bcy) * 0.5f;
1045
1046 recursiveQuadraticBezierVertices(ax, ay, mx, my, acx, acy,
Chris Craik9c3dd622014-06-11 17:24:51 -07001047 sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices, depth + 1);
Chris Craik65cd6122012-12-10 17:56:27 -08001048 recursiveQuadraticBezierVertices(mx, my, bx, by, bcx, bcy,
Chris Craik9c3dd622014-06-11 17:24:51 -07001049 sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices, depth + 1);
Chris Craik65cd6122012-12-10 17:56:27 -08001050 }
1051}
1052
1053}; // namespace uirenderer
1054}; // namespace android