blob: 9246237aeffbe04d04815b22323c4da5cc082c76 [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 */
Chris Craik65cd6122012-12-10 17:56:27 -080016#define LOG_NDEBUG 1
Chris Craik65cd6122012-12-10 17:56:27 -080017
18#define VERTEX_DEBUG 0
19
20#if VERTEX_DEBUG
21#define DEBUG_DUMP_ALPHA_BUFFER() \
22 for (unsigned int i = 0; i < vertexBuffer.getSize(); i++) { \
23 ALOGD("point %d at %f %f, alpha %f", \
Romain Guy3380cfd2013-08-15 16:57:57 -070024 i, buffer[i].x, buffer[i].y, buffer[i].alpha); \
Chris Craik65cd6122012-12-10 17:56:27 -080025 }
26#define DEBUG_DUMP_BUFFER() \
27 for (unsigned int i = 0; i < vertexBuffer.getSize(); i++) { \
Romain Guy3380cfd2013-08-15 16:57:57 -070028 ALOGD("point %d at %f %f", i, buffer[i].x, buffer[i].y); \
Chris Craik65cd6122012-12-10 17:56:27 -080029 }
30#else
31#define DEBUG_DUMP_ALPHA_BUFFER()
32#define DEBUG_DUMP_BUFFER()
33#endif
34
Chris Craik9db58c02015-08-19 15:19:18 -070035#include "PathTessellator.h"
36
37#include "Matrix.h"
38#include "Vector.h"
39#include "Vertex.h"
40#include "utils/MathUtils.h"
41
42#include <algorithm>
43
Chris Craik65cd6122012-12-10 17:56:27 -080044#include <SkPath.h>
45#include <SkPaint.h>
Chris Craikfca52b752015-04-28 11:45:59 -070046#include <SkPoint.h>
Derek Sollenbergerb0772a62015-03-16 14:35:55 -040047#include <SkGeometry.h> // WARNING: Internal Skia Header
Chris Craik65cd6122012-12-10 17:56:27 -080048
49#include <stdlib.h>
50#include <stdint.h>
51#include <sys/types.h>
52
53#include <utils/Log.h>
54#include <utils/Trace.h>
55
Chris Craik65cd6122012-12-10 17:56:27 -080056namespace android {
57namespace uirenderer {
58
ztenghui21ef8202015-05-28 16:06:28 -070059#define OUTLINE_REFINE_THRESHOLD 0.5f
Chris Craik65cd6122012-12-10 17:56:27 -080060#define ROUND_CAP_THRESH 0.25f
61#define PI 3.1415926535897932f
Chris Craik74cf7e62014-08-07 14:34:46 -070062#define MAX_DEPTH 15
Chris Craik65cd6122012-12-10 17:56:27 -080063
Chris Craik74cf7e62014-08-07 14:34:46 -070064/**
65 * Extracts the x and y scale from the transform as positive values, and clamps them
66 */
Chris Craik05f3d6e2014-06-02 16:27:04 -070067void PathTessellator::extractTessellationScales(const Matrix4& transform,
68 float* scaleX, float* scaleY) {
Chris Craikfe02b4b2014-06-16 16:34:29 -070069 if (CC_LIKELY(transform.isPureTranslate())) {
70 *scaleX = 1.0f;
71 *scaleY = 1.0f;
72 } else {
Chris Craik05f3d6e2014-06-02 16:27:04 -070073 float m00 = transform.data[Matrix4::kScaleX];
74 float m01 = transform.data[Matrix4::kSkewY];
75 float m10 = transform.data[Matrix4::kSkewX];
76 float m11 = transform.data[Matrix4::kScaleY];
Chris Craik74cf7e62014-08-07 14:34:46 -070077 *scaleX = MathUtils::clampTessellationScale(sqrt(m00 * m00 + m01 * m01));
78 *scaleY = MathUtils::clampTessellationScale(sqrt(m10 * m10 + m11 * m11));
Chris Craik65cd6122012-12-10 17:56:27 -080079 }
80}
81
Chris Craik65cd6122012-12-10 17:56:27 -080082/**
83 * Produces a pseudo-normal for a vertex, given the normals of the two incoming lines. If the offset
84 * from each vertex in a perimeter is calculated, the resultant lines connecting the offset vertices
85 * will be offset by 1.0
86 *
87 * Note that we can't add and normalize the two vectors, that would result in a rectangle having an
88 * offset of (sqrt(2)/2, sqrt(2)/2) at each corner, instead of (1, 1)
89 *
90 * NOTE: assumes angles between normals 90 degrees or less
91 */
John Reck1aa5d2d2014-07-24 13:38:28 -070092inline static Vector2 totalOffsetFromNormals(const Vector2& normalA, const Vector2& normalB) {
Chris Craik65cd6122012-12-10 17:56:27 -080093 return (normalA + normalB) / (1 + fabs(normalA.dot(normalB)));
94}
95
96/**
97 * Structure used for storing useful information about the SkPaint and scale used for tessellating
98 */
99struct PaintInfo {
100public:
Chris Craikd6b65f62014-01-01 14:45:21 -0800101 PaintInfo(const SkPaint* paint, const mat4& transform) :
Chris Craik65cd6122012-12-10 17:56:27 -0800102 style(paint->getStyle()), cap(paint->getStrokeCap()), isAA(paint->isAntiAlias()),
Chris Craik65cd6122012-12-10 17:56:27 -0800103 halfStrokeWidth(paint->getStrokeWidth() * 0.5f), maxAlpha(1.0f) {
104 // compute inverse scales
Chris Craikfe02b4b2014-06-16 16:34:29 -0700105 if (CC_LIKELY(transform.isPureTranslate())) {
106 inverseScaleX = 1.0f;
107 inverseScaleY = 1.0f;
108 } else {
Chris Craik05f3d6e2014-06-02 16:27:04 -0700109 float scaleX, scaleY;
110 PathTessellator::extractTessellationScales(transform, &scaleX, &scaleY);
Chris Craik74cf7e62014-08-07 14:34:46 -0700111 inverseScaleX = 1.0f / scaleX;
112 inverseScaleY = 1.0f / scaleY;
Chris Craik65cd6122012-12-10 17:56:27 -0800113 }
114
115 if (isAA && halfStrokeWidth != 0 && inverseScaleX == inverseScaleY &&
Chris Craik19a390b2013-02-27 15:43:26 -0800116 2 * halfStrokeWidth < inverseScaleX) {
Chris Craik05f3d6e2014-06-02 16:27:04 -0700117 // AA, with non-hairline stroke, width < 1 pixel. Scale alpha and treat as hairline.
Chris Craik65cd6122012-12-10 17:56:27 -0800118 maxAlpha *= (2 * halfStrokeWidth) / inverseScaleX;
119 halfStrokeWidth = 0.0f;
120 }
121 }
122
123 SkPaint::Style style;
124 SkPaint::Cap cap;
125 bool isAA;
126 float inverseScaleX;
127 float inverseScaleY;
128 float halfStrokeWidth;
129 float maxAlpha;
130
John Reck1aa5d2d2014-07-24 13:38:28 -0700131 inline void scaleOffsetForStrokeWidth(Vector2& offset) const {
Chris Craik65cd6122012-12-10 17:56:27 -0800132 if (halfStrokeWidth == 0.0f) {
133 // hairline - compensate for scale
134 offset.x *= 0.5f * inverseScaleX;
135 offset.y *= 0.5f * inverseScaleY;
136 } else {
137 offset *= halfStrokeWidth;
138 }
139 }
140
141 /**
142 * NOTE: the input will not always be a normal, especially for sharp edges - it should be the
143 * result of totalOffsetFromNormals (see documentation there)
144 */
John Reck1aa5d2d2014-07-24 13:38:28 -0700145 inline Vector2 deriveAAOffset(const Vector2& offset) const {
146 return (Vector2){offset.x * 0.5f * inverseScaleX, offset.y * 0.5f * inverseScaleY};
Chris Craik65cd6122012-12-10 17:56:27 -0800147 }
148
149 /**
150 * Returns the number of cap divisions beyond the minimum 2 (kButt_Cap/kSquareCap will return 0)
151 * Should only be used when stroking and drawing caps
152 */
153 inline int capExtraDivisions() const {
154 if (cap == SkPaint::kRound_Cap) {
Chris Craikff29b5a2015-05-27 18:17:03 -0700155 // always use 2 points for hairline
Chris Craik65cd6122012-12-10 17:56:27 -0800156 if (halfStrokeWidth == 0.0f) return 2;
157
Chris Craik9db58c02015-08-19 15:19:18 -0700158 float threshold = std::min(inverseScaleX, inverseScaleY) * ROUND_CAP_THRESH;
Chris Craikff29b5a2015-05-27 18:17:03 -0700159 return MathUtils::divisionsNeededToApproximateArc(halfStrokeWidth, PI, threshold);
Chris Craik65cd6122012-12-10 17:56:27 -0800160 }
161 return 0;
162 }
Chris Craikf0a59072013-11-19 18:00:46 -0800163
164 /**
Chris Craik21bcfc4f2014-09-08 18:43:30 -0700165 * Outset the bounds of point data (for line endpoints or points) to account for stroke
Chris Craikf0a59072013-11-19 18:00:46 -0800166 * geometry.
Chris Craik21bcfc4f2014-09-08 18:43:30 -0700167 *
168 * bounds are in pre-scaled space.
Chris Craikf0a59072013-11-19 18:00:46 -0800169 */
Chris Craik05f3d6e2014-06-02 16:27:04 -0700170 void expandBoundsForStroke(Rect* bounds) const {
Chris Craik21bcfc4f2014-09-08 18:43:30 -0700171 if (halfStrokeWidth == 0) {
172 // hairline, outset by (0.5f + fudge factor) in post-scaling space
173 bounds->outset(fabs(inverseScaleX) * (0.5f + Vertex::GeometryFudgeFactor()),
174 fabs(inverseScaleY) * (0.5f + Vertex::GeometryFudgeFactor()));
175 } else {
176 // non hairline, outset by half stroke width pre-scaled, and fudge factor post scaled
177 bounds->outset(halfStrokeWidth + fabs(inverseScaleX) * Vertex::GeometryFudgeFactor(),
178 halfStrokeWidth + fabs(inverseScaleY) * Vertex::GeometryFudgeFactor());
179 }
Chris Craikf0a59072013-11-19 18:00:46 -0800180 }
Chris Craik65cd6122012-12-10 17:56:27 -0800181};
182
John Reck272a6852015-07-29 16:48:58 -0700183void getFillVerticesFromPerimeter(const std::vector<Vertex>& perimeter,
184 VertexBuffer& vertexBuffer) {
Chris Craik65cd6122012-12-10 17:56:27 -0800185 Vertex* buffer = vertexBuffer.alloc<Vertex>(perimeter.size());
186
187 int currentIndex = 0;
188 // zig zag between all previous points on the inside of the hull to create a
189 // triangle strip that fills the hull
190 int srcAindex = 0;
191 int srcBindex = perimeter.size() - 1;
192 while (srcAindex <= srcBindex) {
Chris Craik11a75672013-12-16 17:08:15 -0800193 buffer[currentIndex++] = perimeter[srcAindex];
Chris Craik65cd6122012-12-10 17:56:27 -0800194 if (srcAindex == srcBindex) break;
Chris Craik11a75672013-12-16 17:08:15 -0800195 buffer[currentIndex++] = perimeter[srcBindex];
Chris Craik65cd6122012-12-10 17:56:27 -0800196 srcAindex++;
197 srcBindex--;
198 }
199}
200
201/*
202 * Fills a vertexBuffer with non-alpha vertices, zig-zagging at each perimeter point to create a
203 * tri-strip as wide as the stroke.
204 *
205 * Uses an additional 2 vertices at the end to wrap around, closing the tri-strip
206 * (for a total of perimeter.size() * 2 + 2 vertices)
207 */
John Reck272a6852015-07-29 16:48:58 -0700208void getStrokeVerticesFromPerimeter(const PaintInfo& paintInfo,
209 const std::vector<Vertex>& perimeter, VertexBuffer& vertexBuffer) {
Chris Craik65cd6122012-12-10 17:56:27 -0800210 Vertex* buffer = vertexBuffer.alloc<Vertex>(perimeter.size() * 2 + 2);
211
212 int currentIndex = 0;
213 const Vertex* last = &(perimeter[perimeter.size() - 1]);
214 const Vertex* current = &(perimeter[0]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700215 Vector2 lastNormal = {current->y - last->y, last->x - current->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800216 lastNormal.normalize();
217 for (unsigned int i = 0; i < perimeter.size(); i++) {
218 const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700219 Vector2 nextNormal = {next->y - current->y, current->x - next->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800220 nextNormal.normalize();
221
John Reck1aa5d2d2014-07-24 13:38:28 -0700222 Vector2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
Chris Craik65cd6122012-12-10 17:56:27 -0800223 paintInfo.scaleOffsetForStrokeWidth(totalOffset);
224
225 Vertex::set(&buffer[currentIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700226 current->x + totalOffset.x,
227 current->y + totalOffset.y);
Chris Craik65cd6122012-12-10 17:56:27 -0800228
229 Vertex::set(&buffer[currentIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700230 current->x - totalOffset.x,
231 current->y - totalOffset.y);
Chris Craik65cd6122012-12-10 17:56:27 -0800232
Chris Craik65cd6122012-12-10 17:56:27 -0800233 current = next;
234 lastNormal = nextNormal;
235 }
236
237 // wrap around to beginning
Chris Craik11a75672013-12-16 17:08:15 -0800238 buffer[currentIndex++] = buffer[0];
239 buffer[currentIndex++] = buffer[1];
Chris Craik65cd6122012-12-10 17:56:27 -0800240
241 DEBUG_DUMP_BUFFER();
242}
243
Chris Craik6d29c8d2013-05-08 18:35:44 -0700244static inline void storeBeginEnd(const PaintInfo& paintInfo, const Vertex& center,
John Reck1aa5d2d2014-07-24 13:38:28 -0700245 const Vector2& normal, Vertex* buffer, int& currentIndex, bool begin) {
246 Vector2 strokeOffset = normal;
Chris Craik6d29c8d2013-05-08 18:35:44 -0700247 paintInfo.scaleOffsetForStrokeWidth(strokeOffset);
248
John Reck1aa5d2d2014-07-24 13:38:28 -0700249 Vector2 referencePoint = {center.x, center.y};
Chris Craik6d29c8d2013-05-08 18:35:44 -0700250 if (paintInfo.cap == SkPaint::kSquare_Cap) {
John Reck1aa5d2d2014-07-24 13:38:28 -0700251 Vector2 rotated = {-strokeOffset.y, strokeOffset.x};
252 referencePoint += rotated * (begin ? -1 : 1);
Chris Craik6d29c8d2013-05-08 18:35:44 -0700253 }
254
255 Vertex::set(&buffer[currentIndex++], referencePoint + strokeOffset);
256 Vertex::set(&buffer[currentIndex++], referencePoint - strokeOffset);
257}
258
Chris Craik65cd6122012-12-10 17:56:27 -0800259/**
260 * Fills a vertexBuffer with non-alpha vertices similar to getStrokeVerticesFromPerimeter, except:
261 *
262 * 1 - Doesn't need to wrap around, since the input vertices are unclosed
263 *
264 * 2 - can zig-zag across 'extra' vertices at either end, to create round caps
265 */
266void getStrokeVerticesFromUnclosedVertices(const PaintInfo& paintInfo,
John Reck272a6852015-07-29 16:48:58 -0700267 const std::vector<Vertex>& vertices, VertexBuffer& vertexBuffer) {
Chris Craik65cd6122012-12-10 17:56:27 -0800268 const int extra = paintInfo.capExtraDivisions();
269 const int allocSize = (vertices.size() + extra) * 2;
Chris Craik65cd6122012-12-10 17:56:27 -0800270 Vertex* buffer = vertexBuffer.alloc<Vertex>(allocSize);
271
Chris Craik6d29c8d2013-05-08 18:35:44 -0700272 const int lastIndex = vertices.size() - 1;
Chris Craik65cd6122012-12-10 17:56:27 -0800273 if (extra > 0) {
274 // tessellate both round caps
Chris Craik65cd6122012-12-10 17:56:27 -0800275 float beginTheta = atan2(
Romain Guy3380cfd2013-08-15 16:57:57 -0700276 - (vertices[0].x - vertices[1].x),
277 vertices[0].y - vertices[1].y);
Chris Craik65cd6122012-12-10 17:56:27 -0800278 float endTheta = atan2(
Romain Guy3380cfd2013-08-15 16:57:57 -0700279 - (vertices[lastIndex].x - vertices[lastIndex - 1].x),
280 vertices[lastIndex].y - vertices[lastIndex - 1].y);
Chris Craik65cd6122012-12-10 17:56:27 -0800281 const float dTheta = PI / (extra + 1);
Chris Craik65cd6122012-12-10 17:56:27 -0800282
283 int capOffset;
284 for (int i = 0; i < extra; i++) {
285 if (i < extra / 2) {
286 capOffset = extra - 2 * i - 1;
287 } else {
288 capOffset = 2 * i - extra;
289 }
290
291 beginTheta += dTheta;
Dan Albertff1d8a62014-11-11 19:31:26 -0800292 Vector2 beginRadialOffset = {cosf(beginTheta), sinf(beginTheta)};
Chris Craik65cd6122012-12-10 17:56:27 -0800293 paintInfo.scaleOffsetForStrokeWidth(beginRadialOffset);
294 Vertex::set(&buffer[capOffset],
Romain Guy3380cfd2013-08-15 16:57:57 -0700295 vertices[0].x + beginRadialOffset.x,
296 vertices[0].y + beginRadialOffset.y);
Chris Craik65cd6122012-12-10 17:56:27 -0800297
298 endTheta += dTheta;
Dan Albertff1d8a62014-11-11 19:31:26 -0800299 Vector2 endRadialOffset = {cosf(endTheta), sinf(endTheta)};
Chris Craik65cd6122012-12-10 17:56:27 -0800300 paintInfo.scaleOffsetForStrokeWidth(endRadialOffset);
301 Vertex::set(&buffer[allocSize - 1 - capOffset],
Romain Guy3380cfd2013-08-15 16:57:57 -0700302 vertices[lastIndex].x + endRadialOffset.x,
303 vertices[lastIndex].y + endRadialOffset.y);
Chris Craik65cd6122012-12-10 17:56:27 -0800304 }
305 }
306
307 int currentIndex = extra;
Chris Craik6d29c8d2013-05-08 18:35:44 -0700308 const Vertex* last = &(vertices[0]);
309 const Vertex* current = &(vertices[1]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700310 Vector2 lastNormal = {current->y - last->y, last->x - current->x};
Chris Craik6d29c8d2013-05-08 18:35:44 -0700311 lastNormal.normalize();
312
313 storeBeginEnd(paintInfo, vertices[0], lastNormal, buffer, currentIndex, true);
314
315 for (unsigned int i = 1; i < vertices.size() - 1; i++) {
Chris Craik65cd6122012-12-10 17:56:27 -0800316 const Vertex* next = &(vertices[i + 1]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700317 Vector2 nextNormal = {next->y - current->y, current->x - next->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800318 nextNormal.normalize();
319
John Reck1aa5d2d2014-07-24 13:38:28 -0700320 Vector2 strokeOffset = totalOffsetFromNormals(lastNormal, nextNormal);
Chris Craik6d29c8d2013-05-08 18:35:44 -0700321 paintInfo.scaleOffsetForStrokeWidth(strokeOffset);
Chris Craik65cd6122012-12-10 17:56:27 -0800322
John Reck1aa5d2d2014-07-24 13:38:28 -0700323 Vector2 center = {current->x, current->y};
Chris Craik6d29c8d2013-05-08 18:35:44 -0700324 Vertex::set(&buffer[currentIndex++], center + strokeOffset);
325 Vertex::set(&buffer[currentIndex++], center - strokeOffset);
Chris Craik65cd6122012-12-10 17:56:27 -0800326
327 current = next;
328 lastNormal = nextNormal;
329 }
330
Chris Craik6d29c8d2013-05-08 18:35:44 -0700331 storeBeginEnd(paintInfo, vertices[lastIndex], lastNormal, buffer, currentIndex, false);
Chris Craik65cd6122012-12-10 17:56:27 -0800332
333 DEBUG_DUMP_BUFFER();
334}
335
336/**
337 * Populates a vertexBuffer with AlphaVertices to create an anti-aliased fill shape tessellation
Chris Craik6d29c8d2013-05-08 18:35:44 -0700338 *
Chris Craik65cd6122012-12-10 17:56:27 -0800339 * 1 - create the AA perimeter of unit width, by zig-zagging at each point around the perimeter of
340 * the shape (using 2 * perimeter.size() vertices)
341 *
342 * 2 - wrap around to the beginning to complete the perimeter (2 vertices)
343 *
344 * 3 - zig zag back and forth inside the shape to fill it (using perimeter.size() vertices)
345 */
John Reck272a6852015-07-29 16:48:58 -0700346void getFillVerticesFromPerimeterAA(const PaintInfo& paintInfo,
347 const std::vector<Vertex>& perimeter, VertexBuffer& vertexBuffer,
348 float maxAlpha = 1.0f) {
Chris Craik65cd6122012-12-10 17:56:27 -0800349 AlphaVertex* buffer = vertexBuffer.alloc<AlphaVertex>(perimeter.size() * 3 + 2);
350
351 // generate alpha points - fill Alpha vertex gaps in between each point with
352 // alpha 0 vertex, offset by a scaled normal.
353 int currentIndex = 0;
354 const Vertex* last = &(perimeter[perimeter.size() - 1]);
355 const Vertex* current = &(perimeter[0]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700356 Vector2 lastNormal = {current->y - last->y, last->x - current->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800357 lastNormal.normalize();
358 for (unsigned int i = 0; i < perimeter.size(); i++) {
359 const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700360 Vector2 nextNormal = {next->y - current->y, current->x - next->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800361 nextNormal.normalize();
362
363 // AA point offset from original point is that point's normal, such that each side is offset
364 // by .5 pixels
John Reck1aa5d2d2014-07-24 13:38:28 -0700365 Vector2 totalOffset = paintInfo.deriveAAOffset(totalOffsetFromNormals(lastNormal, nextNormal));
Chris Craik65cd6122012-12-10 17:56:27 -0800366
367 AlphaVertex::set(&buffer[currentIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700368 current->x + totalOffset.x,
369 current->y + totalOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800370 0.0f);
371 AlphaVertex::set(&buffer[currentIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700372 current->x - totalOffset.x,
373 current->y - totalOffset.y,
Chris Craikf0a59072013-11-19 18:00:46 -0800374 maxAlpha);
Chris Craik65cd6122012-12-10 17:56:27 -0800375
Chris Craik65cd6122012-12-10 17:56:27 -0800376 current = next;
377 lastNormal = nextNormal;
378 }
379
380 // wrap around to beginning
Chris Craik11a75672013-12-16 17:08:15 -0800381 buffer[currentIndex++] = buffer[0];
382 buffer[currentIndex++] = buffer[1];
Chris Craik65cd6122012-12-10 17:56:27 -0800383
384 // zig zag between all previous points on the inside of the hull to create a
385 // triangle strip that fills the hull, repeating the first inner point to
386 // create degenerate tris to start inside path
387 int srcAindex = 0;
388 int srcBindex = perimeter.size() - 1;
389 while (srcAindex <= srcBindex) {
Chris Craik11a75672013-12-16 17:08:15 -0800390 buffer[currentIndex++] = buffer[srcAindex * 2 + 1];
Chris Craik65cd6122012-12-10 17:56:27 -0800391 if (srcAindex == srcBindex) break;
Chris Craik11a75672013-12-16 17:08:15 -0800392 buffer[currentIndex++] = buffer[srcBindex * 2 + 1];
Chris Craik65cd6122012-12-10 17:56:27 -0800393 srcAindex++;
394 srcBindex--;
395 }
396
397 DEBUG_DUMP_BUFFER();
398}
399
400/**
401 * Stores geometry for a single, AA-perimeter (potentially rounded) cap
402 *
403 * For explanation of constants and general methodoloyg, see comments for
404 * getStrokeVerticesFromUnclosedVerticesAA() below.
405 */
John Reck272a6852015-07-29 16:48:58 -0700406inline static void storeCapAA(const PaintInfo& paintInfo, const std::vector<Vertex>& vertices,
John Reck1aa5d2d2014-07-24 13:38:28 -0700407 AlphaVertex* buffer, bool isFirst, Vector2 normal, int offset) {
Chris Craik65cd6122012-12-10 17:56:27 -0800408 const int extra = paintInfo.capExtraDivisions();
409 const int extraOffset = (extra + 1) / 2;
410 const int capIndex = isFirst
411 ? 2 * offset + 6 + 2 * (extra + extraOffset)
412 : offset + 2 + 2 * extraOffset;
413 if (isFirst) normal *= -1;
414
415 // TODO: this normal should be scaled by radialScale if extra != 0, see totalOffsetFromNormals()
John Reck1aa5d2d2014-07-24 13:38:28 -0700416 Vector2 AAOffset = paintInfo.deriveAAOffset(normal);
Chris Craik65cd6122012-12-10 17:56:27 -0800417
John Reck1aa5d2d2014-07-24 13:38:28 -0700418 Vector2 strokeOffset = normal;
Chris Craik65cd6122012-12-10 17:56:27 -0800419 paintInfo.scaleOffsetForStrokeWidth(strokeOffset);
John Reck1aa5d2d2014-07-24 13:38:28 -0700420 Vector2 outerOffset = strokeOffset + AAOffset;
421 Vector2 innerOffset = strokeOffset - AAOffset;
Chris Craik65cd6122012-12-10 17:56:27 -0800422
John Reck1aa5d2d2014-07-24 13:38:28 -0700423 Vector2 capAAOffset = {0, 0};
Chris Craik65cd6122012-12-10 17:56:27 -0800424 if (paintInfo.cap != SkPaint::kRound_Cap) {
425 // if the cap is square or butt, the inside primary cap vertices will be inset in two
426 // directions - both normal to the stroke, and parallel to it.
John Reck1aa5d2d2014-07-24 13:38:28 -0700427 capAAOffset = (Vector2){-AAOffset.y, AAOffset.x};
Chris Craik65cd6122012-12-10 17:56:27 -0800428 }
429
430 // determine referencePoint, the center point for the 4 primary cap vertices
John Reck272a6852015-07-29 16:48:58 -0700431 const Vertex& point = isFirst ? vertices.front() : vertices.back();
432 Vector2 referencePoint = {point.x, point.y};
Chris Craik65cd6122012-12-10 17:56:27 -0800433 if (paintInfo.cap == SkPaint::kSquare_Cap) {
434 // To account for square cap, move the primary cap vertices (that create the AA edge) by the
435 // stroke offset vector (rotated to be parallel to the stroke)
John Reck1aa5d2d2014-07-24 13:38:28 -0700436 Vector2 rotated = {-strokeOffset.y, strokeOffset.x};
437 referencePoint += rotated;
Chris Craik65cd6122012-12-10 17:56:27 -0800438 }
439
440 AlphaVertex::set(&buffer[capIndex + 0],
441 referencePoint.x + outerOffset.x + capAAOffset.x,
442 referencePoint.y + outerOffset.y + capAAOffset.y,
443 0.0f);
444 AlphaVertex::set(&buffer[capIndex + 1],
445 referencePoint.x + innerOffset.x - capAAOffset.x,
446 referencePoint.y + innerOffset.y - capAAOffset.y,
447 paintInfo.maxAlpha);
448
449 bool isRound = paintInfo.cap == SkPaint::kRound_Cap;
450
451 const int postCapIndex = (isRound && isFirst) ? (2 * extraOffset - 2) : capIndex + (2 * extra);
452 AlphaVertex::set(&buffer[postCapIndex + 2],
453 referencePoint.x - outerOffset.x + capAAOffset.x,
454 referencePoint.y - outerOffset.y + capAAOffset.y,
455 0.0f);
456 AlphaVertex::set(&buffer[postCapIndex + 3],
457 referencePoint.x - innerOffset.x - capAAOffset.x,
458 referencePoint.y - innerOffset.y - capAAOffset.y,
459 paintInfo.maxAlpha);
460
461 if (isRound) {
462 const float dTheta = PI / (extra + 1);
463 const float radialScale = 2.0f / (1 + cos(dTheta));
464 float theta = atan2(normal.y, normal.x);
465 int capPerimIndex = capIndex + 2;
466
467 for (int i = 0; i < extra; i++) {
468 theta += dTheta;
469
Dan Albertff1d8a62014-11-11 19:31:26 -0800470 Vector2 radialOffset = {cosf(theta), sinf(theta)};
Chris Craik65cd6122012-12-10 17:56:27 -0800471
472 // scale to compensate for pinching at sharp angles, see totalOffsetFromNormals()
473 radialOffset *= radialScale;
474
475 AAOffset = paintInfo.deriveAAOffset(radialOffset);
476 paintInfo.scaleOffsetForStrokeWidth(radialOffset);
477 AlphaVertex::set(&buffer[capPerimIndex++],
478 referencePoint.x + radialOffset.x + AAOffset.x,
479 referencePoint.y + radialOffset.y + AAOffset.y,
480 0.0f);
481 AlphaVertex::set(&buffer[capPerimIndex++],
482 referencePoint.x + radialOffset.x - AAOffset.x,
483 referencePoint.y + radialOffset.y - AAOffset.y,
484 paintInfo.maxAlpha);
485
486 if (isFirst && i == extra - extraOffset) {
487 //copy most recent two points to first two points
Chris Craik11a75672013-12-16 17:08:15 -0800488 buffer[0] = buffer[capPerimIndex - 2];
489 buffer[1] = buffer[capPerimIndex - 1];
Chris Craik65cd6122012-12-10 17:56:27 -0800490
491 capPerimIndex = 2; // start writing the rest of the round cap at index 2
492 }
493 }
494
495 if (isFirst) {
496 const int startCapFillIndex = capIndex + 2 * (extra - extraOffset) + 4;
497 int capFillIndex = startCapFillIndex;
498 for (int i = 0; i < extra + 2; i += 2) {
Chris Craik11a75672013-12-16 17:08:15 -0800499 buffer[capFillIndex++] = buffer[1 + i];
Chris Craik65cd6122012-12-10 17:56:27 -0800500 // TODO: to support odd numbers of divisions, break here on the last iteration
Chris Craik11a75672013-12-16 17:08:15 -0800501 buffer[capFillIndex++] = buffer[startCapFillIndex - 3 - i];
Chris Craik65cd6122012-12-10 17:56:27 -0800502 }
503 } else {
504 int capFillIndex = 6 * vertices.size() + 2 + 6 * extra - (extra + 2);
505 for (int i = 0; i < extra + 2; i += 2) {
Chris Craik11a75672013-12-16 17:08:15 -0800506 buffer[capFillIndex++] = buffer[capIndex + 1 + i];
Chris Craik65cd6122012-12-10 17:56:27 -0800507 // TODO: to support odd numbers of divisions, break here on the last iteration
Chris Craik11a75672013-12-16 17:08:15 -0800508 buffer[capFillIndex++] = buffer[capIndex + 3 + 2 * extra - i];
Chris Craik65cd6122012-12-10 17:56:27 -0800509 }
510 }
511 return;
512 }
513 if (isFirst) {
Chris Craik11a75672013-12-16 17:08:15 -0800514 buffer[0] = buffer[postCapIndex + 2];
515 buffer[1] = buffer[postCapIndex + 3];
516 buffer[postCapIndex + 4] = buffer[1]; // degenerate tris (the only two!)
517 buffer[postCapIndex + 5] = buffer[postCapIndex + 1];
Chris Craik65cd6122012-12-10 17:56:27 -0800518 } else {
Chris Craik11a75672013-12-16 17:08:15 -0800519 buffer[6 * vertices.size()] = buffer[postCapIndex + 1];
520 buffer[6 * vertices.size() + 1] = buffer[postCapIndex + 3];
Chris Craik65cd6122012-12-10 17:56:27 -0800521 }
522}
523
524/*
525the geometry for an aa, capped stroke consists of the following:
526
527 # vertices | function
528----------------------------------------------------------------------
529a) 2 | Start AA perimeter
530b) 2, 2 * roundDivOff | First half of begin cap's perimeter
531 |
532 2 * middlePts | 'Outer' or 'Top' AA perimeter half (between caps)
533 |
534a) 4 | End cap's
535b) 2, 2 * roundDivs, 2 | AA perimeter
536 |
537 2 * middlePts | 'Inner' or 'bottom' AA perimeter half
538 |
539a) 6 | Begin cap's perimeter
540b) 2, 2*(rD - rDO + 1), | Last half of begin cap's perimeter
541 roundDivs, 2 |
542 |
543 2 * middlePts | Stroke's full opacity center strip
544 |
545a) 2 | end stroke
546b) 2, roundDivs | (and end cap fill, for round)
547
548Notes:
549* rows starting with 'a)' denote the Butt or Square cap vertex use, 'b)' denote Round
550
551* 'middlePts' is (number of points in the unclosed input vertex list, minus 2) times two
552
553* 'roundDivs' or 'rD' is the number of extra vertices (beyond the minimum of 2) that define the
554 round cap's shape, and is at least two. This will increase with cap size to sufficiently
555 define the cap's level of tessellation.
556
557* 'roundDivOffset' or 'rDO' is the point about halfway along the start cap's round perimeter, where
558 the stream of vertices for the AA perimeter starts. By starting and ending the perimeter at
559 this offset, the fill of the stroke is drawn from this point with minimal extra vertices.
560
561This means the outer perimeter starts at:
562 outerIndex = (2) OR (2 + 2 * roundDivOff)
563the inner perimeter (since it is filled in reverse) starts at:
564 innerIndex = outerIndex + (4 * middlePts) + ((4) OR (4 + 2 * roundDivs)) - 1
565the stroke starts at:
566 strokeIndex = innerIndex + 1 + ((6) OR (6 + 3 * roundDivs - 2 * roundDivOffset))
567
568The total needed allocated space is either:
569 2 + 4 + 6 + 2 + 3 * (2 * middlePts) = 14 + 6 * middlePts = 2 + 6 * pts
570or, for rounded caps:
571 (2 + 2 * rDO) + (4 + 2 * rD) + (2 * (rD - rDO + 1)
572 + roundDivs + 4) + (2 + roundDivs) + 3 * (2 * middlePts)
573 = 14 + 6 * middlePts + 6 * roundDivs
574 = 2 + 6 * pts + 6 * roundDivs
575 */
576void getStrokeVerticesFromUnclosedVerticesAA(const PaintInfo& paintInfo,
John Reck272a6852015-07-29 16:48:58 -0700577 const std::vector<Vertex>& vertices, VertexBuffer& vertexBuffer) {
Chris Craik65cd6122012-12-10 17:56:27 -0800578
579 const int extra = paintInfo.capExtraDivisions();
580 const int allocSize = 6 * vertices.size() + 2 + 6 * extra;
581
582 AlphaVertex* buffer = vertexBuffer.alloc<AlphaVertex>(allocSize);
583
584 const int extraOffset = (extra + 1) / 2;
585 int offset = 2 * (vertices.size() - 2);
586 // there is no outer/inner here, using them for consistency with below approach
587 int currentAAOuterIndex = 2 + 2 * extraOffset;
588 int currentAAInnerIndex = currentAAOuterIndex + (2 * offset) + 3 + (2 * extra);
589 int currentStrokeIndex = currentAAInnerIndex + 7 + (3 * extra - 2 * extraOffset);
590
591 const Vertex* last = &(vertices[0]);
592 const Vertex* current = &(vertices[1]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700593 Vector2 lastNormal = {current->y - last->y, last->x - current->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800594 lastNormal.normalize();
595
596 // TODO: use normal from bezier traversal for cap, instead of from vertices
597 storeCapAA(paintInfo, vertices, buffer, true, lastNormal, offset);
598
599 for (unsigned int i = 1; i < vertices.size() - 1; i++) {
600 const Vertex* next = &(vertices[i + 1]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700601 Vector2 nextNormal = {next->y - current->y, current->x - next->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800602 nextNormal.normalize();
603
John Reck1aa5d2d2014-07-24 13:38:28 -0700604 Vector2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
605 Vector2 AAOffset = paintInfo.deriveAAOffset(totalOffset);
Chris Craik65cd6122012-12-10 17:56:27 -0800606
John Reck1aa5d2d2014-07-24 13:38:28 -0700607 Vector2 innerOffset = totalOffset;
Chris Craik65cd6122012-12-10 17:56:27 -0800608 paintInfo.scaleOffsetForStrokeWidth(innerOffset);
John Reck1aa5d2d2014-07-24 13:38:28 -0700609 Vector2 outerOffset = innerOffset + AAOffset;
Chris Craik65cd6122012-12-10 17:56:27 -0800610 innerOffset -= AAOffset;
611
612 AlphaVertex::set(&buffer[currentAAOuterIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700613 current->x + outerOffset.x,
614 current->y + outerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800615 0.0f);
616 AlphaVertex::set(&buffer[currentAAOuterIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700617 current->x + innerOffset.x,
618 current->y + innerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800619 paintInfo.maxAlpha);
620
621 AlphaVertex::set(&buffer[currentStrokeIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700622 current->x + innerOffset.x,
623 current->y + innerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800624 paintInfo.maxAlpha);
625 AlphaVertex::set(&buffer[currentStrokeIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700626 current->x - innerOffset.x,
627 current->y - innerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800628 paintInfo.maxAlpha);
629
630 AlphaVertex::set(&buffer[currentAAInnerIndex--],
Romain Guy3380cfd2013-08-15 16:57:57 -0700631 current->x - innerOffset.x,
632 current->y - innerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800633 paintInfo.maxAlpha);
634 AlphaVertex::set(&buffer[currentAAInnerIndex--],
Romain Guy3380cfd2013-08-15 16:57:57 -0700635 current->x - outerOffset.x,
636 current->y - outerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800637 0.0f);
638
639 current = next;
640 lastNormal = nextNormal;
641 }
642
643 // TODO: use normal from bezier traversal for cap, instead of from vertices
644 storeCapAA(paintInfo, vertices, buffer, false, lastNormal, offset);
645
646 DEBUG_DUMP_ALPHA_BUFFER();
647}
648
649
John Reck272a6852015-07-29 16:48:58 -0700650void getStrokeVerticesFromPerimeterAA(const PaintInfo& paintInfo,
651 const std::vector<Vertex>& perimeter, VertexBuffer& vertexBuffer) {
Chris Craik65cd6122012-12-10 17:56:27 -0800652 AlphaVertex* buffer = vertexBuffer.alloc<AlphaVertex>(6 * perimeter.size() + 8);
653
654 int offset = 2 * perimeter.size() + 3;
655 int currentAAOuterIndex = 0;
656 int currentStrokeIndex = offset;
657 int currentAAInnerIndex = offset * 2;
658
659 const Vertex* last = &(perimeter[perimeter.size() - 1]);
660 const Vertex* current = &(perimeter[0]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700661 Vector2 lastNormal = {current->y - last->y, last->x - current->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800662 lastNormal.normalize();
663 for (unsigned int i = 0; i < perimeter.size(); i++) {
664 const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700665 Vector2 nextNormal = {next->y - current->y, current->x - next->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800666 nextNormal.normalize();
667
John Reck1aa5d2d2014-07-24 13:38:28 -0700668 Vector2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
669 Vector2 AAOffset = paintInfo.deriveAAOffset(totalOffset);
Chris Craik65cd6122012-12-10 17:56:27 -0800670
John Reck1aa5d2d2014-07-24 13:38:28 -0700671 Vector2 innerOffset = totalOffset;
Chris Craik65cd6122012-12-10 17:56:27 -0800672 paintInfo.scaleOffsetForStrokeWidth(innerOffset);
John Reck1aa5d2d2014-07-24 13:38:28 -0700673 Vector2 outerOffset = innerOffset + AAOffset;
Chris Craik65cd6122012-12-10 17:56:27 -0800674 innerOffset -= AAOffset;
675
676 AlphaVertex::set(&buffer[currentAAOuterIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700677 current->x + outerOffset.x,
678 current->y + outerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800679 0.0f);
680 AlphaVertex::set(&buffer[currentAAOuterIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700681 current->x + innerOffset.x,
682 current->y + innerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800683 paintInfo.maxAlpha);
684
685 AlphaVertex::set(&buffer[currentStrokeIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700686 current->x + innerOffset.x,
687 current->y + innerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800688 paintInfo.maxAlpha);
689 AlphaVertex::set(&buffer[currentStrokeIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700690 current->x - innerOffset.x,
691 current->y - innerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800692 paintInfo.maxAlpha);
693
694 AlphaVertex::set(&buffer[currentAAInnerIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700695 current->x - innerOffset.x,
696 current->y - innerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800697 paintInfo.maxAlpha);
698 AlphaVertex::set(&buffer[currentAAInnerIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700699 current->x - outerOffset.x,
700 current->y - outerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800701 0.0f);
702
Chris Craik65cd6122012-12-10 17:56:27 -0800703 current = next;
704 lastNormal = nextNormal;
705 }
706
707 // wrap each strip around to beginning, creating degenerate tris to bridge strips
Chris Craik11a75672013-12-16 17:08:15 -0800708 buffer[currentAAOuterIndex++] = buffer[0];
709 buffer[currentAAOuterIndex++] = buffer[1];
710 buffer[currentAAOuterIndex++] = buffer[1];
Chris Craik65cd6122012-12-10 17:56:27 -0800711
Chris Craik11a75672013-12-16 17:08:15 -0800712 buffer[currentStrokeIndex++] = buffer[offset];
713 buffer[currentStrokeIndex++] = buffer[offset + 1];
714 buffer[currentStrokeIndex++] = buffer[offset + 1];
Chris Craik65cd6122012-12-10 17:56:27 -0800715
Chris Craik11a75672013-12-16 17:08:15 -0800716 buffer[currentAAInnerIndex++] = buffer[2 * offset];
717 buffer[currentAAInnerIndex++] = buffer[2 * offset + 1];
Chris Craik65cd6122012-12-10 17:56:27 -0800718 // don't need to create last degenerate tri
719
720 DEBUG_DUMP_ALPHA_BUFFER();
721}
722
723void PathTessellator::tessellatePath(const SkPath &path, const SkPaint* paint,
Chris Craikd6b65f62014-01-01 14:45:21 -0800724 const mat4& transform, VertexBuffer& vertexBuffer) {
Chris Craik65cd6122012-12-10 17:56:27 -0800725 ATRACE_CALL();
726
727 const PaintInfo paintInfo(paint, transform);
728
John Reck272a6852015-07-29 16:48:58 -0700729 std::vector<Vertex> tempVertices;
Chris Craik65cd6122012-12-10 17:56:27 -0800730 float threshInvScaleX = paintInfo.inverseScaleX;
731 float threshInvScaleY = paintInfo.inverseScaleY;
732 if (paintInfo.style == SkPaint::kStroke_Style) {
733 // alter the bezier recursion threshold values we calculate in order to compensate for
734 // expansion done after the path vertices are found
735 SkRect bounds = path.getBounds();
736 if (!bounds.isEmpty()) {
737 threshInvScaleX *= bounds.width() / (bounds.width() + paint->getStrokeWidth());
738 threshInvScaleY *= bounds.height() / (bounds.height() + paint->getStrokeWidth());
739 }
740 }
741
742 // force close if we're filling the path, since fill path expects closed perimeter.
743 bool forceClose = paintInfo.style != SkPaint::kStroke_Style;
ztenghui21ef8202015-05-28 16:06:28 -0700744 PathApproximationInfo approximationInfo(threshInvScaleX, threshInvScaleY,
745 OUTLINE_REFINE_THRESHOLD);
Chris Craik65cd6122012-12-10 17:56:27 -0800746 bool wasClosed = approximatePathOutlineVertices(path, forceClose,
ztenghui21ef8202015-05-28 16:06:28 -0700747 approximationInfo, tempVertices);
Chris Craik65cd6122012-12-10 17:56:27 -0800748
749 if (!tempVertices.size()) {
750 // path was empty, return without allocating vertex buffer
751 return;
752 }
753
754#if VERTEX_DEBUG
755 for (unsigned int i = 0; i < tempVertices.size(); i++) {
756 ALOGD("orig path: point at %f %f",
Romain Guy3380cfd2013-08-15 16:57:57 -0700757 tempVertices[i].x, tempVertices[i].y);
Chris Craik65cd6122012-12-10 17:56:27 -0800758 }
759#endif
760
761 if (paintInfo.style == SkPaint::kStroke_Style) {
762 if (!paintInfo.isAA) {
763 if (wasClosed) {
764 getStrokeVerticesFromPerimeter(paintInfo, tempVertices, vertexBuffer);
765 } else {
766 getStrokeVerticesFromUnclosedVertices(paintInfo, tempVertices, vertexBuffer);
767 }
768
769 } else {
770 if (wasClosed) {
771 getStrokeVerticesFromPerimeterAA(paintInfo, tempVertices, vertexBuffer);
772 } else {
773 getStrokeVerticesFromUnclosedVerticesAA(paintInfo, tempVertices, vertexBuffer);
774 }
775 }
776 } else {
777 // For kStrokeAndFill style, the path should be adjusted externally.
778 // It will be treated as a fill here.
779 if (!paintInfo.isAA) {
780 getFillVerticesFromPerimeter(tempVertices, vertexBuffer);
781 } else {
782 getFillVerticesFromPerimeterAA(paintInfo, tempVertices, vertexBuffer);
783 }
784 }
Chris Craik05f3d6e2014-06-02 16:27:04 -0700785
786 Rect bounds(path.getBounds());
787 paintInfo.expandBoundsForStroke(&bounds);
788 vertexBuffer.setBounds(bounds);
Chris Craik117bdbc2015-02-05 10:12:38 -0800789 vertexBuffer.setMeshFeatureFlags(paintInfo.isAA ? VertexBuffer::kAlpha : VertexBuffer::kNone);
Chris Craik65cd6122012-12-10 17:56:27 -0800790}
791
Chris Craik6d29c8d2013-05-08 18:35:44 -0700792template <class TYPE>
793static void instanceVertices(VertexBuffer& srcBuffer, VertexBuffer& dstBuffer,
Chris Craik05f3d6e2014-06-02 16:27:04 -0700794 const float* points, int count, Rect& bounds) {
Chris Craik6d29c8d2013-05-08 18:35:44 -0700795 bounds.set(points[0], points[1], points[0], points[1]);
796
797 int numPoints = count / 2;
798 int verticesPerPoint = srcBuffer.getVertexCount();
799 dstBuffer.alloc<TYPE>(numPoints * verticesPerPoint + (numPoints - 1) * 2);
800
801 for (int i = 0; i < count; i += 2) {
Chris Craik15c3f192015-12-03 12:16:56 -0800802 bounds.expandToCover(points[i + 0], points[i + 1]);
Chris Craik6d29c8d2013-05-08 18:35:44 -0700803 dstBuffer.copyInto<TYPE>(srcBuffer, points[i + 0], points[i + 1]);
804 }
805 dstBuffer.createDegenerateSeparators<TYPE>(verticesPerPoint);
806}
807
Chris Craikd218a922014-01-02 17:13:34 -0800808void PathTessellator::tessellatePoints(const float* points, int count, const SkPaint* paint,
Chris Craik05f3d6e2014-06-02 16:27:04 -0700809 const mat4& transform, VertexBuffer& vertexBuffer) {
Chris Craik6d29c8d2013-05-08 18:35:44 -0700810 const PaintInfo paintInfo(paint, transform);
811
812 // determine point shape
813 SkPath path;
814 float radius = paintInfo.halfStrokeWidth;
Rob Tsukb7c26562014-11-03 16:29:03 -0800815 if (radius == 0.0f) radius = 0.5f;
Chris Craik6d29c8d2013-05-08 18:35:44 -0700816
817 if (paintInfo.cap == SkPaint::kRound_Cap) {
818 path.addCircle(0, 0, radius);
819 } else {
820 path.addRect(-radius, -radius, radius, radius);
821 }
822
823 // calculate outline
John Reck272a6852015-07-29 16:48:58 -0700824 std::vector<Vertex> outlineVertices;
ztenghui21ef8202015-05-28 16:06:28 -0700825 PathApproximationInfo approximationInfo(paintInfo.inverseScaleX, paintInfo.inverseScaleY,
826 OUTLINE_REFINE_THRESHOLD);
827 approximatePathOutlineVertices(path, true, approximationInfo, outlineVertices);
Chris Craik6d29c8d2013-05-08 18:35:44 -0700828
829 if (!outlineVertices.size()) return;
830
Chris Craik05f3d6e2014-06-02 16:27:04 -0700831 Rect bounds;
Chris Craik6d29c8d2013-05-08 18:35:44 -0700832 // tessellate, then duplicate outline across points
Chris Craik6d29c8d2013-05-08 18:35:44 -0700833 VertexBuffer tempBuffer;
834 if (!paintInfo.isAA) {
835 getFillVerticesFromPerimeter(outlineVertices, tempBuffer);
836 instanceVertices<Vertex>(tempBuffer, vertexBuffer, points, count, bounds);
837 } else {
Chris Craikf0a59072013-11-19 18:00:46 -0800838 // note: pass maxAlpha directly, since we want fill to be alpha modulated
839 getFillVerticesFromPerimeterAA(paintInfo, outlineVertices, tempBuffer, paintInfo.maxAlpha);
Chris Craik6d29c8d2013-05-08 18:35:44 -0700840 instanceVertices<AlphaVertex>(tempBuffer, vertexBuffer, points, count, bounds);
841 }
842
Chris Craikf0a59072013-11-19 18:00:46 -0800843 // expand bounds from vertex coords to pixel data
Chris Craik05f3d6e2014-06-02 16:27:04 -0700844 paintInfo.expandBoundsForStroke(&bounds);
845 vertexBuffer.setBounds(bounds);
Chris Craik117bdbc2015-02-05 10:12:38 -0800846 vertexBuffer.setMeshFeatureFlags(paintInfo.isAA ? VertexBuffer::kAlpha : VertexBuffer::kNone);
Chris Craik65cd6122012-12-10 17:56:27 -0800847}
848
Chris Craikd218a922014-01-02 17:13:34 -0800849void PathTessellator::tessellateLines(const float* points, int count, const SkPaint* paint,
Chris Craik05f3d6e2014-06-02 16:27:04 -0700850 const mat4& transform, VertexBuffer& vertexBuffer) {
Chris Craik65cd6122012-12-10 17:56:27 -0800851 ATRACE_CALL();
852 const PaintInfo paintInfo(paint, transform);
853
854 const int extra = paintInfo.capExtraDivisions();
855 int numLines = count / 4;
856 int lineAllocSize;
857 // pre-allocate space for lines in the buffer, and degenerate tris in between
858 if (paintInfo.isAA) {
859 lineAllocSize = 6 * (2) + 2 + 6 * extra;
860 vertexBuffer.alloc<AlphaVertex>(numLines * lineAllocSize + (numLines - 1) * 2);
861 } else {
862 lineAllocSize = 2 * ((2) + extra);
863 vertexBuffer.alloc<Vertex>(numLines * lineAllocSize + (numLines - 1) * 2);
864 }
865
John Reck272a6852015-07-29 16:48:58 -0700866 std::vector<Vertex> tempVertices(2);
867 Vertex* tempVerticesData = &tempVertices.front();
Chris Craik05f3d6e2014-06-02 16:27:04 -0700868 Rect bounds;
Chris Craik65cd6122012-12-10 17:56:27 -0800869 bounds.set(points[0], points[1], points[0], points[1]);
870 for (int i = 0; i < count; i += 4) {
871 Vertex::set(&(tempVerticesData[0]), points[i + 0], points[i + 1]);
872 Vertex::set(&(tempVerticesData[1]), points[i + 2], points[i + 3]);
873
874 if (paintInfo.isAA) {
875 getStrokeVerticesFromUnclosedVerticesAA(paintInfo, tempVertices, vertexBuffer);
876 } else {
877 getStrokeVerticesFromUnclosedVertices(paintInfo, tempVertices, vertexBuffer);
878 }
879
880 // calculate bounds
Chris Craik15c3f192015-12-03 12:16:56 -0800881 bounds.expandToCover(tempVerticesData[0].x, tempVerticesData[0].y);
882 bounds.expandToCover(tempVerticesData[1].x, tempVerticesData[1].y);
Chris Craik65cd6122012-12-10 17:56:27 -0800883 }
884
Chris Craik65cd6122012-12-10 17:56:27 -0800885 // since multiple objects tessellated into buffer, separate them with degen tris
886 if (paintInfo.isAA) {
887 vertexBuffer.createDegenerateSeparators<AlphaVertex>(lineAllocSize);
888 } else {
889 vertexBuffer.createDegenerateSeparators<Vertex>(lineAllocSize);
890 }
Chris Craikf0a59072013-11-19 18:00:46 -0800891
892 // expand bounds from vertex coords to pixel data
Chris Craik05f3d6e2014-06-02 16:27:04 -0700893 paintInfo.expandBoundsForStroke(&bounds);
894 vertexBuffer.setBounds(bounds);
Chris Craik117bdbc2015-02-05 10:12:38 -0800895 vertexBuffer.setMeshFeatureFlags(paintInfo.isAA ? VertexBuffer::kAlpha : VertexBuffer::kNone);
Chris Craik65cd6122012-12-10 17:56:27 -0800896}
897
898///////////////////////////////////////////////////////////////////////////////
899// Simple path line approximation
900///////////////////////////////////////////////////////////////////////////////
901
ztenghui21ef8202015-05-28 16:06:28 -0700902bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, float threshold,
John Reck272a6852015-07-29 16:48:58 -0700903 std::vector<Vertex>& outputVertices) {
ztenghui21ef8202015-05-28 16:06:28 -0700904 PathApproximationInfo approximationInfo(1.0f, 1.0f, threshold);
905 return approximatePathOutlineVertices(path, true, approximationInfo, outputVertices);
Chris Craik15a07a22014-01-26 13:43:53 -0800906}
907
Chris Craikfca52b752015-04-28 11:45:59 -0700908class ClockwiseEnforcer {
909public:
910 void addPoint(const SkPoint& point) {
911 double x = point.x();
912 double y = point.y();
913
914 if (initialized) {
915 sum += (x + lastX) * (y - lastY);
916 } else {
917 initialized = true;
918 }
919
920 lastX = x;
921 lastY = y;
922 }
John Reck272a6852015-07-29 16:48:58 -0700923 void reverseVectorIfNotClockwise(std::vector<Vertex>& vertices) {
Chris Craikfca52b752015-04-28 11:45:59 -0700924 if (sum < 0) {
925 // negative sum implies CounterClockwise
926 const int size = vertices.size();
927 for (int i = 0; i < size / 2; i++) {
928 Vertex tmp = vertices[i];
929 int k = size - 1 - i;
John Reck272a6852015-07-29 16:48:58 -0700930 vertices[i] = vertices[k];
931 vertices[k] = tmp;
Chris Craikfca52b752015-04-28 11:45:59 -0700932 }
933 }
934 }
935private:
936 bool initialized = false;
Chris Craik469fb1e2015-05-28 15:33:40 -0700937 double lastX = 0;
938 double lastY = 0;
Chris Craikfca52b752015-04-28 11:45:59 -0700939 double sum = 0;
940};
941
Chris Craik65cd6122012-12-10 17:56:27 -0800942bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool forceClose,
John Reck272a6852015-07-29 16:48:58 -0700943 const PathApproximationInfo& approximationInfo, std::vector<Vertex>& outputVertices) {
Chris Craik65cd6122012-12-10 17:56:27 -0800944 ATRACE_CALL();
945
946 // TODO: to support joins other than sharp miter, join vertices should be labelled in the
947 // perimeter, or resolved into more vertices. Reconsider forceClose-ing in that case.
948 SkPath::Iter iter(path, forceClose);
949 SkPoint pts[4];
950 SkPath::Verb v;
Chris Craikfca52b752015-04-28 11:45:59 -0700951 ClockwiseEnforcer clockwiseEnforcer;
Chris Craik65cd6122012-12-10 17:56:27 -0800952 while (SkPath::kDone_Verb != (v = iter.next(pts))) {
953 switch (v) {
954 case SkPath::kMove_Verb:
John Reck272a6852015-07-29 16:48:58 -0700955 outputVertices.push_back(Vertex{pts[0].x(), pts[0].y()});
Chris Craik65cd6122012-12-10 17:56:27 -0800956 ALOGV("Move to pos %f %f", pts[0].x(), pts[0].y());
Chris Craikfca52b752015-04-28 11:45:59 -0700957 clockwiseEnforcer.addPoint(pts[0]);
Chris Craik65cd6122012-12-10 17:56:27 -0800958 break;
959 case SkPath::kClose_Verb:
960 ALOGV("Close at pos %f %f", pts[0].x(), pts[0].y());
Chris Craikfca52b752015-04-28 11:45:59 -0700961 clockwiseEnforcer.addPoint(pts[0]);
Chris Craik65cd6122012-12-10 17:56:27 -0800962 break;
963 case SkPath::kLine_Verb:
964 ALOGV("kLine_Verb %f %f -> %f %f", pts[0].x(), pts[0].y(), pts[1].x(), pts[1].y());
John Reck272a6852015-07-29 16:48:58 -0700965 outputVertices.push_back(Vertex{pts[1].x(), pts[1].y()});
Chris Craikfca52b752015-04-28 11:45:59 -0700966 clockwiseEnforcer.addPoint(pts[1]);
Chris Craik65cd6122012-12-10 17:56:27 -0800967 break;
968 case SkPath::kQuad_Verb:
969 ALOGV("kQuad_Verb");
970 recursiveQuadraticBezierVertices(
971 pts[0].x(), pts[0].y(),
972 pts[2].x(), pts[2].y(),
973 pts[1].x(), pts[1].y(),
ztenghui21ef8202015-05-28 16:06:28 -0700974 approximationInfo, outputVertices);
Chris Craikfca52b752015-04-28 11:45:59 -0700975 clockwiseEnforcer.addPoint(pts[1]);
976 clockwiseEnforcer.addPoint(pts[2]);
Chris Craik65cd6122012-12-10 17:56:27 -0800977 break;
978 case SkPath::kCubic_Verb:
979 ALOGV("kCubic_Verb");
980 recursiveCubicBezierVertices(
981 pts[0].x(), pts[0].y(),
982 pts[1].x(), pts[1].y(),
983 pts[3].x(), pts[3].y(),
984 pts[2].x(), pts[2].y(),
ztenghui21ef8202015-05-28 16:06:28 -0700985 approximationInfo, outputVertices);
Chris Craikfca52b752015-04-28 11:45:59 -0700986 clockwiseEnforcer.addPoint(pts[1]);
987 clockwiseEnforcer.addPoint(pts[2]);
988 clockwiseEnforcer.addPoint(pts[3]);
Chris Craik65cd6122012-12-10 17:56:27 -0800989 break;
Derek Sollenbergerb0772a62015-03-16 14:35:55 -0400990 case SkPath::kConic_Verb: {
991 ALOGV("kConic_Verb");
992 SkAutoConicToQuads converter;
993 const SkPoint* quads = converter.computeQuads(pts, iter.conicWeight(),
ztenghui21ef8202015-05-28 16:06:28 -0700994 approximationInfo.thresholdForConicQuads);
Derek Sollenbergerb0772a62015-03-16 14:35:55 -0400995 for (int i = 0; i < converter.countQuads(); ++i) {
996 const int offset = 2 * i;
997 recursiveQuadraticBezierVertices(
998 quads[offset].x(), quads[offset].y(),
999 quads[offset+2].x(), quads[offset+2].y(),
1000 quads[offset+1].x(), quads[offset+1].y(),
ztenghui21ef8202015-05-28 16:06:28 -07001001 approximationInfo, outputVertices);
Derek Sollenbergerb0772a62015-03-16 14:35:55 -04001002 }
Chris Craikfca52b752015-04-28 11:45:59 -07001003 clockwiseEnforcer.addPoint(pts[1]);
1004 clockwiseEnforcer.addPoint(pts[2]);
Derek Sollenbergerb0772a62015-03-16 14:35:55 -04001005 break;
1006 }
Chris Craik65cd6122012-12-10 17:56:27 -08001007 default:
1008 break;
1009 }
1010 }
1011
Chris Craikfca52b752015-04-28 11:45:59 -07001012 bool wasClosed = false;
Chris Craik65cd6122012-12-10 17:56:27 -08001013 int size = outputVertices.size();
Romain Guy3380cfd2013-08-15 16:57:57 -07001014 if (size >= 2 && outputVertices[0].x == outputVertices[size - 1].x &&
1015 outputVertices[0].y == outputVertices[size - 1].y) {
John Reck272a6852015-07-29 16:48:58 -07001016 outputVertices.pop_back();
Chris Craikfca52b752015-04-28 11:45:59 -07001017 wasClosed = true;
Chris Craik65cd6122012-12-10 17:56:27 -08001018 }
Chris Craikfca52b752015-04-28 11:45:59 -07001019
1020 // ensure output vector is clockwise
1021 clockwiseEnforcer.reverseVectorIfNotClockwise(outputVertices);
1022 return wasClosed;
Chris Craik65cd6122012-12-10 17:56:27 -08001023}
1024
1025///////////////////////////////////////////////////////////////////////////////
1026// Bezier approximation
ztenghui21ef8202015-05-28 16:06:28 -07001027//
1028// All the inputs and outputs here are in path coordinates.
1029// We convert the error threshold from screen coordinates into path coordinates.
Chris Craik65cd6122012-12-10 17:56:27 -08001030///////////////////////////////////////////////////////////////////////////////
1031
ztenghui21ef8202015-05-28 16:06:28 -07001032// Get a threshold in path coordinates, by scaling the thresholdSquared from screen coordinates.
1033// TODO: Document the math behind this algorithm.
1034static inline float getThreshold(const PathApproximationInfo& info, float dx, float dy) {
1035 // multiplying by sqrInvScaleY/X equivalent to multiplying in dimensional scale factors
1036 float scale = (dx * dx * info.sqrInvScaleY + dy * dy * info.sqrInvScaleX);
1037 return info.thresholdSquared * scale;
1038}
1039
Chris Craik65cd6122012-12-10 17:56:27 -08001040void PathTessellator::recursiveCubicBezierVertices(
1041 float p1x, float p1y, float c1x, float c1y,
1042 float p2x, float p2y, float c2x, float c2y,
ztenghui21ef8202015-05-28 16:06:28 -07001043 const PathApproximationInfo& approximationInfo,
John Reck272a6852015-07-29 16:48:58 -07001044 std::vector<Vertex>& outputVertices, int depth) {
Chris Craik65cd6122012-12-10 17:56:27 -08001045 float dx = p2x - p1x;
1046 float dy = p2y - p1y;
1047 float d1 = fabs((c1x - p2x) * dy - (c1y - p2y) * dx);
1048 float d2 = fabs((c2x - p2x) * dy - (c2y - p2y) * dx);
1049 float d = d1 + d2;
1050
Chris Craik74cf7e62014-08-07 14:34:46 -07001051 if (depth >= MAX_DEPTH
ztenghui21ef8202015-05-28 16:06:28 -07001052 || d * d <= getThreshold(approximationInfo, dx, dy)) {
Chris Craik65cd6122012-12-10 17:56:27 -08001053 // below thresh, draw line by adding endpoint
John Reck272a6852015-07-29 16:48:58 -07001054 outputVertices.push_back(Vertex{p2x, p2y});
Chris Craik65cd6122012-12-10 17:56:27 -08001055 } else {
1056 float p1c1x = (p1x + c1x) * 0.5f;
1057 float p1c1y = (p1y + c1y) * 0.5f;
1058 float p2c2x = (p2x + c2x) * 0.5f;
1059 float p2c2y = (p2y + c2y) * 0.5f;
1060
1061 float c1c2x = (c1x + c2x) * 0.5f;
1062 float c1c2y = (c1y + c2y) * 0.5f;
1063
1064 float p1c1c2x = (p1c1x + c1c2x) * 0.5f;
1065 float p1c1c2y = (p1c1y + c1c2y) * 0.5f;
1066
1067 float p2c1c2x = (p2c2x + c1c2x) * 0.5f;
1068 float p2c1c2y = (p2c2y + c1c2y) * 0.5f;
1069
1070 float mx = (p1c1c2x + p2c1c2x) * 0.5f;
1071 float my = (p1c1c2y + p2c1c2y) * 0.5f;
1072
1073 recursiveCubicBezierVertices(
1074 p1x, p1y, p1c1x, p1c1y,
1075 mx, my, p1c1c2x, p1c1c2y,
ztenghui21ef8202015-05-28 16:06:28 -07001076 approximationInfo, outputVertices, depth + 1);
Chris Craik65cd6122012-12-10 17:56:27 -08001077 recursiveCubicBezierVertices(
1078 mx, my, p2c1c2x, p2c1c2y,
1079 p2x, p2y, p2c2x, p2c2y,
ztenghui21ef8202015-05-28 16:06:28 -07001080 approximationInfo, outputVertices, depth + 1);
Chris Craik65cd6122012-12-10 17:56:27 -08001081 }
1082}
1083
1084void PathTessellator::recursiveQuadraticBezierVertices(
1085 float ax, float ay,
1086 float bx, float by,
1087 float cx, float cy,
ztenghui21ef8202015-05-28 16:06:28 -07001088 const PathApproximationInfo& approximationInfo,
John Reck272a6852015-07-29 16:48:58 -07001089 std::vector<Vertex>& outputVertices, int depth) {
Chris Craik65cd6122012-12-10 17:56:27 -08001090 float dx = bx - ax;
1091 float dy = by - ay;
ztenghui21ef8202015-05-28 16:06:28 -07001092 // d is the cross product of vector (B-A) and (C-B).
Chris Craik65cd6122012-12-10 17:56:27 -08001093 float d = (cx - bx) * dy - (cy - by) * dx;
1094
Chris Craik74cf7e62014-08-07 14:34:46 -07001095 if (depth >= MAX_DEPTH
ztenghui21ef8202015-05-28 16:06:28 -07001096 || d * d <= getThreshold(approximationInfo, dx, dy)) {
Chris Craik65cd6122012-12-10 17:56:27 -08001097 // below thresh, draw line by adding endpoint
John Reck272a6852015-07-29 16:48:58 -07001098 outputVertices.push_back(Vertex{bx, by});
Chris Craik65cd6122012-12-10 17:56:27 -08001099 } else {
1100 float acx = (ax + cx) * 0.5f;
1101 float bcx = (bx + cx) * 0.5f;
1102 float acy = (ay + cy) * 0.5f;
1103 float bcy = (by + cy) * 0.5f;
1104
1105 // midpoint
1106 float mx = (acx + bcx) * 0.5f;
1107 float my = (acy + bcy) * 0.5f;
1108
1109 recursiveQuadraticBezierVertices(ax, ay, mx, my, acx, acy,
ztenghui21ef8202015-05-28 16:06:28 -07001110 approximationInfo, outputVertices, depth + 1);
Chris Craik65cd6122012-12-10 17:56:27 -08001111 recursiveQuadraticBezierVertices(mx, my, bx, by, bcx, bcy,
ztenghui21ef8202015-05-28 16:06:28 -07001112 approximationInfo, outputVertices, depth + 1);
Chris Craik65cd6122012-12-10 17:56:27 -08001113 }
1114}
1115
1116}; // namespace uirenderer
1117}; // namespace android