blob: 973b1d14b77e51d13f3643808d8e26a2e28cc7a1 [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
John Reck1bcacfd2017-11-03 10:12:19 -070021#define DEBUG_DUMP_ALPHA_BUFFER() \
22 for (unsigned int i = 0; i < vertexBuffer.getSize(); i++) { \
23 ALOGD("point %d at %f %f, alpha %f", i, buffer[i].x, buffer[i].y, buffer[i].alpha); \
Chris Craik65cd6122012-12-10 17:56:27 -080024 }
John Reck1bcacfd2017-11-03 10:12:19 -070025#define DEBUG_DUMP_BUFFER() \
26 for (unsigned int i = 0; i < vertexBuffer.getSize(); i++) { \
Romain Guy3380cfd2013-08-15 16:57:57 -070027 ALOGD("point %d at %f %f", i, buffer[i].x, buffer[i].y); \
Chris Craik65cd6122012-12-10 17:56:27 -080028 }
29#else
30#define DEBUG_DUMP_ALPHA_BUFFER()
31#define DEBUG_DUMP_BUFFER()
32#endif
33
Chris Craik9db58c02015-08-19 15:19:18 -070034#include "PathTessellator.h"
35
36#include "Matrix.h"
37#include "Vector.h"
38#include "Vertex.h"
39#include "utils/MathUtils.h"
40
41#include <algorithm>
42
John Reck1bcacfd2017-11-03 10:12:19 -070043#include <SkGeometry.h> // WARNING: Internal Skia Header
Chris Craik65cd6122012-12-10 17:56:27 -080044#include <SkPaint.h>
John Reck1bcacfd2017-11-03 10:12:19 -070045#include <SkPath.h>
Chris Craikfca52b752015-04-28 11:45:59 -070046#include <SkPoint.h>
Chris Craik65cd6122012-12-10 17:56:27 -080047
Chris Craik65cd6122012-12-10 17:56:27 -080048#include <stdint.h>
John Reck1bcacfd2017-11-03 10:12:19 -070049#include <stdlib.h>
Chris Craik65cd6122012-12-10 17:56:27 -080050#include <sys/types.h>
51
52#include <utils/Log.h>
53#include <utils/Trace.h>
54
Chris Craik65cd6122012-12-10 17:56:27 -080055namespace android {
56namespace uirenderer {
57
ztenghui21ef8202015-05-28 16:06:28 -070058#define OUTLINE_REFINE_THRESHOLD 0.5f
Chris Craik65cd6122012-12-10 17:56:27 -080059#define ROUND_CAP_THRESH 0.25f
60#define PI 3.1415926535897932f
Chris Craik74cf7e62014-08-07 14:34:46 -070061#define MAX_DEPTH 15
Chris Craik65cd6122012-12-10 17:56:27 -080062
Chris Craik74cf7e62014-08-07 14:34:46 -070063/**
64 * Extracts the x and y scale from the transform as positive values, and clamps them
65 */
John Reck1bcacfd2017-11-03 10:12:19 -070066void PathTessellator::extractTessellationScales(const Matrix4& transform, float* scaleX,
67 float* scaleY) {
Chris Craikfe02b4b2014-06-16 16:34:29 -070068 if (CC_LIKELY(transform.isPureTranslate())) {
69 *scaleX = 1.0f;
70 *scaleY = 1.0f;
71 } else {
Chris Craik05f3d6e2014-06-02 16:27:04 -070072 float m00 = transform.data[Matrix4::kScaleX];
73 float m01 = transform.data[Matrix4::kSkewY];
74 float m10 = transform.data[Matrix4::kSkewX];
75 float m11 = transform.data[Matrix4::kScaleY];
Chris Craik74cf7e62014-08-07 14:34:46 -070076 *scaleX = MathUtils::clampTessellationScale(sqrt(m00 * m00 + m01 * m01));
77 *scaleY = MathUtils::clampTessellationScale(sqrt(m10 * m10 + m11 * m11));
Chris Craik65cd6122012-12-10 17:56:27 -080078 }
79}
80
Chris Craik65cd6122012-12-10 17:56:27 -080081/**
82 * Produces a pseudo-normal for a vertex, given the normals of the two incoming lines. If the offset
83 * from each vertex in a perimeter is calculated, the resultant lines connecting the offset vertices
84 * will be offset by 1.0
85 *
86 * Note that we can't add and normalize the two vectors, that would result in a rectangle having an
87 * offset of (sqrt(2)/2, sqrt(2)/2) at each corner, instead of (1, 1)
88 *
89 * NOTE: assumes angles between normals 90 degrees or less
90 */
John Reck1aa5d2d2014-07-24 13:38:28 -070091inline static Vector2 totalOffsetFromNormals(const Vector2& normalA, const Vector2& normalB) {
Chris Craik65cd6122012-12-10 17:56:27 -080092 return (normalA + normalB) / (1 + fabs(normalA.dot(normalB)));
93}
94
95/**
96 * Structure used for storing useful information about the SkPaint and scale used for tessellating
97 */
98struct PaintInfo {
99public:
John Reck1bcacfd2017-11-03 10:12:19 -0700100 PaintInfo(const SkPaint* paint, const mat4& transform)
101 : style(paint->getStyle())
102 , cap(paint->getStrokeCap())
103 , isAA(paint->isAntiAlias())
104 , halfStrokeWidth(paint->getStrokeWidth() * 0.5f)
105 , maxAlpha(1.0f) {
Chris Craik65cd6122012-12-10 17:56:27 -0800106 // compute inverse scales
Chris Craikfe02b4b2014-06-16 16:34:29 -0700107 if (CC_LIKELY(transform.isPureTranslate())) {
108 inverseScaleX = 1.0f;
109 inverseScaleY = 1.0f;
110 } else {
Chris Craik05f3d6e2014-06-02 16:27:04 -0700111 float scaleX, scaleY;
112 PathTessellator::extractTessellationScales(transform, &scaleX, &scaleY);
Chris Craik74cf7e62014-08-07 14:34:46 -0700113 inverseScaleX = 1.0f / scaleX;
114 inverseScaleY = 1.0f / scaleY;
Chris Craik65cd6122012-12-10 17:56:27 -0800115 }
116
117 if (isAA && halfStrokeWidth != 0 && inverseScaleX == inverseScaleY &&
John Reck1bcacfd2017-11-03 10:12:19 -0700118 2 * halfStrokeWidth < inverseScaleX) {
Chris Craik05f3d6e2014-06-02 16:27:04 -0700119 // AA, with non-hairline stroke, width < 1 pixel. Scale alpha and treat as hairline.
Chris Craik65cd6122012-12-10 17:56:27 -0800120 maxAlpha *= (2 * halfStrokeWidth) / inverseScaleX;
121 halfStrokeWidth = 0.0f;
122 }
123 }
124
125 SkPaint::Style style;
126 SkPaint::Cap cap;
127 bool isAA;
128 float inverseScaleX;
129 float inverseScaleY;
130 float halfStrokeWidth;
131 float maxAlpha;
132
John Reck1aa5d2d2014-07-24 13:38:28 -0700133 inline void scaleOffsetForStrokeWidth(Vector2& offset) const {
Chris Craik65cd6122012-12-10 17:56:27 -0800134 if (halfStrokeWidth == 0.0f) {
135 // hairline - compensate for scale
136 offset.x *= 0.5f * inverseScaleX;
137 offset.y *= 0.5f * inverseScaleY;
138 } else {
139 offset *= halfStrokeWidth;
140 }
141 }
142
143 /**
144 * NOTE: the input will not always be a normal, especially for sharp edges - it should be the
145 * result of totalOffsetFromNormals (see documentation there)
146 */
John Reck1aa5d2d2014-07-24 13:38:28 -0700147 inline Vector2 deriveAAOffset(const Vector2& offset) const {
148 return (Vector2){offset.x * 0.5f * inverseScaleX, offset.y * 0.5f * inverseScaleY};
Chris Craik65cd6122012-12-10 17:56:27 -0800149 }
150
151 /**
152 * Returns the number of cap divisions beyond the minimum 2 (kButt_Cap/kSquareCap will return 0)
153 * Should only be used when stroking and drawing caps
154 */
155 inline int capExtraDivisions() const {
156 if (cap == SkPaint::kRound_Cap) {
Chris Craikff29b5a2015-05-27 18:17:03 -0700157 // always use 2 points for hairline
Chris Craik65cd6122012-12-10 17:56:27 -0800158 if (halfStrokeWidth == 0.0f) return 2;
159
Chris Craik9db58c02015-08-19 15:19:18 -0700160 float threshold = std::min(inverseScaleX, inverseScaleY) * ROUND_CAP_THRESH;
Chris Craikff29b5a2015-05-27 18:17:03 -0700161 return MathUtils::divisionsNeededToApproximateArc(halfStrokeWidth, PI, threshold);
Chris Craik65cd6122012-12-10 17:56:27 -0800162 }
163 return 0;
164 }
Chris Craikf0a59072013-11-19 18:00:46 -0800165
166 /**
Chris Craik21bcfc4f2014-09-08 18:43:30 -0700167 * Outset the bounds of point data (for line endpoints or points) to account for stroke
Chris Craikf0a59072013-11-19 18:00:46 -0800168 * geometry.
Chris Craik21bcfc4f2014-09-08 18:43:30 -0700169 *
170 * bounds are in pre-scaled space.
Chris Craikf0a59072013-11-19 18:00:46 -0800171 */
Chris Craik05f3d6e2014-06-02 16:27:04 -0700172 void expandBoundsForStroke(Rect* bounds) const {
Chris Craik21bcfc4f2014-09-08 18:43:30 -0700173 if (halfStrokeWidth == 0) {
174 // hairline, outset by (0.5f + fudge factor) in post-scaling space
175 bounds->outset(fabs(inverseScaleX) * (0.5f + Vertex::GeometryFudgeFactor()),
John Reck1bcacfd2017-11-03 10:12:19 -0700176 fabs(inverseScaleY) * (0.5f + Vertex::GeometryFudgeFactor()));
Chris Craik21bcfc4f2014-09-08 18:43:30 -0700177 } else {
178 // non hairline, outset by half stroke width pre-scaled, and fudge factor post scaled
179 bounds->outset(halfStrokeWidth + fabs(inverseScaleX) * Vertex::GeometryFudgeFactor(),
John Reck1bcacfd2017-11-03 10:12:19 -0700180 halfStrokeWidth + fabs(inverseScaleY) * Vertex::GeometryFudgeFactor());
Chris Craik21bcfc4f2014-09-08 18:43:30 -0700181 }
Chris Craikf0a59072013-11-19 18:00:46 -0800182 }
Chris Craik65cd6122012-12-10 17:56:27 -0800183};
184
John Reck272a6852015-07-29 16:48:58 -0700185void getFillVerticesFromPerimeter(const std::vector<Vertex>& perimeter,
John Reck1bcacfd2017-11-03 10:12:19 -0700186 VertexBuffer& vertexBuffer) {
Chris Craik65cd6122012-12-10 17:56:27 -0800187 Vertex* buffer = vertexBuffer.alloc<Vertex>(perimeter.size());
188
189 int currentIndex = 0;
190 // zig zag between all previous points on the inside of the hull to create a
191 // triangle strip that fills the hull
192 int srcAindex = 0;
193 int srcBindex = perimeter.size() - 1;
194 while (srcAindex <= srcBindex) {
Chris Craik11a75672013-12-16 17:08:15 -0800195 buffer[currentIndex++] = perimeter[srcAindex];
Chris Craik65cd6122012-12-10 17:56:27 -0800196 if (srcAindex == srcBindex) break;
Chris Craik11a75672013-12-16 17:08:15 -0800197 buffer[currentIndex++] = perimeter[srcBindex];
Chris Craik65cd6122012-12-10 17:56:27 -0800198 srcAindex++;
199 srcBindex--;
200 }
201}
202
203/*
204 * Fills a vertexBuffer with non-alpha vertices, zig-zagging at each perimeter point to create a
205 * tri-strip as wide as the stroke.
206 *
207 * Uses an additional 2 vertices at the end to wrap around, closing the tri-strip
208 * (for a total of perimeter.size() * 2 + 2 vertices)
209 */
John Reck272a6852015-07-29 16:48:58 -0700210void getStrokeVerticesFromPerimeter(const PaintInfo& paintInfo,
John Reck1bcacfd2017-11-03 10:12:19 -0700211 const std::vector<Vertex>& perimeter,
212 VertexBuffer& vertexBuffer) {
Chris Craik65cd6122012-12-10 17:56:27 -0800213 Vertex* buffer = vertexBuffer.alloc<Vertex>(perimeter.size() * 2 + 2);
214
215 int currentIndex = 0;
216 const Vertex* last = &(perimeter[perimeter.size() - 1]);
217 const Vertex* current = &(perimeter[0]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700218 Vector2 lastNormal = {current->y - last->y, last->x - current->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800219 lastNormal.normalize();
220 for (unsigned int i = 0; i < perimeter.size(); i++) {
221 const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700222 Vector2 nextNormal = {next->y - current->y, current->x - next->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800223 nextNormal.normalize();
224
John Reck1aa5d2d2014-07-24 13:38:28 -0700225 Vector2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
Chris Craik65cd6122012-12-10 17:56:27 -0800226 paintInfo.scaleOffsetForStrokeWidth(totalOffset);
227
John Reck1bcacfd2017-11-03 10:12:19 -0700228 Vertex::set(&buffer[currentIndex++], current->x + totalOffset.x,
229 current->y + totalOffset.y);
Chris Craik65cd6122012-12-10 17:56:27 -0800230
John Reck1bcacfd2017-11-03 10:12:19 -0700231 Vertex::set(&buffer[currentIndex++], current->x - totalOffset.x,
232 current->y - totalOffset.y);
Chris Craik65cd6122012-12-10 17:56:27 -0800233
Chris Craik65cd6122012-12-10 17:56:27 -0800234 current = next;
235 lastNormal = nextNormal;
236 }
237
238 // wrap around to beginning
Chris Craik11a75672013-12-16 17:08:15 -0800239 buffer[currentIndex++] = buffer[0];
240 buffer[currentIndex++] = buffer[1];
Chris Craik65cd6122012-12-10 17:56:27 -0800241
242 DEBUG_DUMP_BUFFER();
243}
244
Chris Craik6d29c8d2013-05-08 18:35:44 -0700245static inline void storeBeginEnd(const PaintInfo& paintInfo, const Vertex& center,
John Reck1bcacfd2017-11-03 10:12:19 -0700246 const Vector2& normal, Vertex* buffer, int& currentIndex,
247 bool begin) {
John Reck1aa5d2d2014-07-24 13:38:28 -0700248 Vector2 strokeOffset = normal;
Chris Craik6d29c8d2013-05-08 18:35:44 -0700249 paintInfo.scaleOffsetForStrokeWidth(strokeOffset);
250
John Reck1aa5d2d2014-07-24 13:38:28 -0700251 Vector2 referencePoint = {center.x, center.y};
Chris Craik6d29c8d2013-05-08 18:35:44 -0700252 if (paintInfo.cap == SkPaint::kSquare_Cap) {
John Reck1aa5d2d2014-07-24 13:38:28 -0700253 Vector2 rotated = {-strokeOffset.y, strokeOffset.x};
254 referencePoint += rotated * (begin ? -1 : 1);
Chris Craik6d29c8d2013-05-08 18:35:44 -0700255 }
256
257 Vertex::set(&buffer[currentIndex++], referencePoint + strokeOffset);
258 Vertex::set(&buffer[currentIndex++], referencePoint - strokeOffset);
259}
260
Chris Craik65cd6122012-12-10 17:56:27 -0800261/**
262 * Fills a vertexBuffer with non-alpha vertices similar to getStrokeVerticesFromPerimeter, except:
263 *
264 * 1 - Doesn't need to wrap around, since the input vertices are unclosed
265 *
266 * 2 - can zig-zag across 'extra' vertices at either end, to create round caps
267 */
268void getStrokeVerticesFromUnclosedVertices(const PaintInfo& paintInfo,
John Reck1bcacfd2017-11-03 10:12:19 -0700269 const std::vector<Vertex>& vertices,
270 VertexBuffer& vertexBuffer) {
Chris Craik65cd6122012-12-10 17:56:27 -0800271 const int extra = paintInfo.capExtraDivisions();
272 const int allocSize = (vertices.size() + extra) * 2;
Chris Craik65cd6122012-12-10 17:56:27 -0800273 Vertex* buffer = vertexBuffer.alloc<Vertex>(allocSize);
274
Chris Craik6d29c8d2013-05-08 18:35:44 -0700275 const int lastIndex = vertices.size() - 1;
Chris Craik65cd6122012-12-10 17:56:27 -0800276 if (extra > 0) {
277 // tessellate both round caps
John Reck1bcacfd2017-11-03 10:12:19 -0700278 float beginTheta = atan2(-(vertices[0].x - vertices[1].x), vertices[0].y - vertices[1].y);
279 float endTheta = atan2(-(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);
John Reck1bcacfd2017-11-03 10:12:19 -0700294 Vertex::set(&buffer[capOffset], vertices[0].x + beginRadialOffset.x,
295 vertices[0].y + beginRadialOffset.y);
Chris Craik65cd6122012-12-10 17:56:27 -0800296
297 endTheta += dTheta;
Dan Albertff1d8a62014-11-11 19:31:26 -0800298 Vector2 endRadialOffset = {cosf(endTheta), sinf(endTheta)};
Chris Craik65cd6122012-12-10 17:56:27 -0800299 paintInfo.scaleOffsetForStrokeWidth(endRadialOffset);
300 Vertex::set(&buffer[allocSize - 1 - capOffset],
John Reck1bcacfd2017-11-03 10:12:19 -0700301 vertices[lastIndex].x + endRadialOffset.x,
302 vertices[lastIndex].y + endRadialOffset.y);
Chris Craik65cd6122012-12-10 17:56:27 -0800303 }
304 }
305
306 int currentIndex = extra;
Chris Craik6d29c8d2013-05-08 18:35:44 -0700307 const Vertex* last = &(vertices[0]);
308 const Vertex* current = &(vertices[1]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700309 Vector2 lastNormal = {current->y - last->y, last->x - current->x};
Chris Craik6d29c8d2013-05-08 18:35:44 -0700310 lastNormal.normalize();
311
312 storeBeginEnd(paintInfo, vertices[0], lastNormal, buffer, currentIndex, true);
313
314 for (unsigned int i = 1; i < vertices.size() - 1; i++) {
Chris Craik65cd6122012-12-10 17:56:27 -0800315 const Vertex* next = &(vertices[i + 1]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700316 Vector2 nextNormal = {next->y - current->y, current->x - next->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800317 nextNormal.normalize();
318
John Reck1bcacfd2017-11-03 10:12:19 -0700319 Vector2 strokeOffset = totalOffsetFromNormals(lastNormal, nextNormal);
Chris Craik6d29c8d2013-05-08 18:35:44 -0700320 paintInfo.scaleOffsetForStrokeWidth(strokeOffset);
Chris Craik65cd6122012-12-10 17:56:27 -0800321
John Reck1aa5d2d2014-07-24 13:38:28 -0700322 Vector2 center = {current->x, current->y};
Chris Craik6d29c8d2013-05-08 18:35:44 -0700323 Vertex::set(&buffer[currentIndex++], center + strokeOffset);
324 Vertex::set(&buffer[currentIndex++], center - strokeOffset);
Chris Craik65cd6122012-12-10 17:56:27 -0800325
326 current = next;
327 lastNormal = nextNormal;
328 }
329
Chris Craik6d29c8d2013-05-08 18:35:44 -0700330 storeBeginEnd(paintInfo, vertices[lastIndex], lastNormal, buffer, currentIndex, false);
Chris Craik65cd6122012-12-10 17:56:27 -0800331
332 DEBUG_DUMP_BUFFER();
333}
334
335/**
336 * Populates a vertexBuffer with AlphaVertices to create an anti-aliased fill shape tessellation
Chris Craik6d29c8d2013-05-08 18:35:44 -0700337 *
Chris Craik65cd6122012-12-10 17:56:27 -0800338 * 1 - create the AA perimeter of unit width, by zig-zagging at each point around the perimeter of
339 * the shape (using 2 * perimeter.size() vertices)
340 *
341 * 2 - wrap around to the beginning to complete the perimeter (2 vertices)
342 *
343 * 3 - zig zag back and forth inside the shape to fill it (using perimeter.size() vertices)
344 */
John Reck272a6852015-07-29 16:48:58 -0700345void getFillVerticesFromPerimeterAA(const PaintInfo& paintInfo,
John Reck1bcacfd2017-11-03 10:12:19 -0700346 const std::vector<Vertex>& perimeter,
347 VertexBuffer& vertexBuffer, float maxAlpha = 1.0f) {
Chris Craik65cd6122012-12-10 17:56:27 -0800348 AlphaVertex* buffer = vertexBuffer.alloc<AlphaVertex>(perimeter.size() * 3 + 2);
349
350 // generate alpha points - fill Alpha vertex gaps in between each point with
351 // alpha 0 vertex, offset by a scaled normal.
352 int currentIndex = 0;
353 const Vertex* last = &(perimeter[perimeter.size() - 1]);
354 const Vertex* current = &(perimeter[0]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700355 Vector2 lastNormal = {current->y - last->y, last->x - current->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800356 lastNormal.normalize();
357 for (unsigned int i = 0; i < perimeter.size(); i++) {
358 const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700359 Vector2 nextNormal = {next->y - current->y, current->x - next->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800360 nextNormal.normalize();
361
362 // AA point offset from original point is that point's normal, such that each side is offset
363 // by .5 pixels
John Reck1bcacfd2017-11-03 10:12:19 -0700364 Vector2 totalOffset =
365 paintInfo.deriveAAOffset(totalOffsetFromNormals(lastNormal, nextNormal));
Chris Craik65cd6122012-12-10 17:56:27 -0800366
John Reck1bcacfd2017-11-03 10:12:19 -0700367 AlphaVertex::set(&buffer[currentIndex++], current->x + totalOffset.x,
368 current->y + totalOffset.y, 0.0f);
369 AlphaVertex::set(&buffer[currentIndex++], current->x - totalOffset.x,
370 current->y - totalOffset.y, maxAlpha);
Chris Craik65cd6122012-12-10 17:56:27 -0800371
Chris Craik65cd6122012-12-10 17:56:27 -0800372 current = next;
373 lastNormal = nextNormal;
374 }
375
376 // wrap around to beginning
Chris Craik11a75672013-12-16 17:08:15 -0800377 buffer[currentIndex++] = buffer[0];
378 buffer[currentIndex++] = buffer[1];
Chris Craik65cd6122012-12-10 17:56:27 -0800379
380 // zig zag between all previous points on the inside of the hull to create a
381 // triangle strip that fills the hull, repeating the first inner point to
382 // create degenerate tris to start inside path
383 int srcAindex = 0;
384 int srcBindex = perimeter.size() - 1;
385 while (srcAindex <= srcBindex) {
Chris Craik11a75672013-12-16 17:08:15 -0800386 buffer[currentIndex++] = buffer[srcAindex * 2 + 1];
Chris Craik65cd6122012-12-10 17:56:27 -0800387 if (srcAindex == srcBindex) break;
Chris Craik11a75672013-12-16 17:08:15 -0800388 buffer[currentIndex++] = buffer[srcBindex * 2 + 1];
Chris Craik65cd6122012-12-10 17:56:27 -0800389 srcAindex++;
390 srcBindex--;
391 }
392
393 DEBUG_DUMP_BUFFER();
394}
395
396/**
397 * Stores geometry for a single, AA-perimeter (potentially rounded) cap
398 *
399 * For explanation of constants and general methodoloyg, see comments for
400 * getStrokeVerticesFromUnclosedVerticesAA() below.
401 */
John Reck272a6852015-07-29 16:48:58 -0700402inline static void storeCapAA(const PaintInfo& paintInfo, const std::vector<Vertex>& vertices,
John Reck1bcacfd2017-11-03 10:12:19 -0700403 AlphaVertex* buffer, bool isFirst, Vector2 normal, int offset) {
Chris Craik65cd6122012-12-10 17:56:27 -0800404 const int extra = paintInfo.capExtraDivisions();
405 const int extraOffset = (extra + 1) / 2;
John Reck1bcacfd2017-11-03 10:12:19 -0700406 const int capIndex =
407 isFirst ? 2 * offset + 6 + 2 * (extra + extraOffset) : offset + 2 + 2 * extraOffset;
Chris Craik65cd6122012-12-10 17:56:27 -0800408 if (isFirst) normal *= -1;
409
410 // TODO: this normal should be scaled by radialScale if extra != 0, see totalOffsetFromNormals()
John Reck1aa5d2d2014-07-24 13:38:28 -0700411 Vector2 AAOffset = paintInfo.deriveAAOffset(normal);
Chris Craik65cd6122012-12-10 17:56:27 -0800412
John Reck1aa5d2d2014-07-24 13:38:28 -0700413 Vector2 strokeOffset = normal;
Chris Craik65cd6122012-12-10 17:56:27 -0800414 paintInfo.scaleOffsetForStrokeWidth(strokeOffset);
John Reck1aa5d2d2014-07-24 13:38:28 -0700415 Vector2 outerOffset = strokeOffset + AAOffset;
416 Vector2 innerOffset = strokeOffset - AAOffset;
Chris Craik65cd6122012-12-10 17:56:27 -0800417
John Reck1aa5d2d2014-07-24 13:38:28 -0700418 Vector2 capAAOffset = {0, 0};
Chris Craik65cd6122012-12-10 17:56:27 -0800419 if (paintInfo.cap != SkPaint::kRound_Cap) {
420 // if the cap is square or butt, the inside primary cap vertices will be inset in two
421 // directions - both normal to the stroke, and parallel to it.
John Reck1aa5d2d2014-07-24 13:38:28 -0700422 capAAOffset = (Vector2){-AAOffset.y, AAOffset.x};
Chris Craik65cd6122012-12-10 17:56:27 -0800423 }
424
425 // determine referencePoint, the center point for the 4 primary cap vertices
John Reck272a6852015-07-29 16:48:58 -0700426 const Vertex& point = isFirst ? vertices.front() : vertices.back();
427 Vector2 referencePoint = {point.x, point.y};
Chris Craik65cd6122012-12-10 17:56:27 -0800428 if (paintInfo.cap == SkPaint::kSquare_Cap) {
429 // To account for square cap, move the primary cap vertices (that create the AA edge) by the
430 // stroke offset vector (rotated to be parallel to the stroke)
John Reck1aa5d2d2014-07-24 13:38:28 -0700431 Vector2 rotated = {-strokeOffset.y, strokeOffset.x};
432 referencePoint += rotated;
Chris Craik65cd6122012-12-10 17:56:27 -0800433 }
434
John Reck1bcacfd2017-11-03 10:12:19 -0700435 AlphaVertex::set(&buffer[capIndex + 0], referencePoint.x + outerOffset.x + capAAOffset.x,
436 referencePoint.y + outerOffset.y + capAAOffset.y, 0.0f);
437 AlphaVertex::set(&buffer[capIndex + 1], referencePoint.x + innerOffset.x - capAAOffset.x,
438 referencePoint.y + innerOffset.y - capAAOffset.y, paintInfo.maxAlpha);
Chris Craik65cd6122012-12-10 17:56:27 -0800439
440 bool isRound = paintInfo.cap == SkPaint::kRound_Cap;
441
442 const int postCapIndex = (isRound && isFirst) ? (2 * extraOffset - 2) : capIndex + (2 * extra);
John Reck1bcacfd2017-11-03 10:12:19 -0700443 AlphaVertex::set(&buffer[postCapIndex + 2], referencePoint.x - outerOffset.x + capAAOffset.x,
444 referencePoint.y - outerOffset.y + capAAOffset.y, 0.0f);
445 AlphaVertex::set(&buffer[postCapIndex + 3], referencePoint.x - innerOffset.x - capAAOffset.x,
446 referencePoint.y - innerOffset.y - capAAOffset.y, paintInfo.maxAlpha);
Chris Craik65cd6122012-12-10 17:56:27 -0800447
448 if (isRound) {
449 const float dTheta = PI / (extra + 1);
450 const float radialScale = 2.0f / (1 + cos(dTheta));
451 float theta = atan2(normal.y, normal.x);
452 int capPerimIndex = capIndex + 2;
453
454 for (int i = 0; i < extra; i++) {
455 theta += dTheta;
456
Dan Albertff1d8a62014-11-11 19:31:26 -0800457 Vector2 radialOffset = {cosf(theta), sinf(theta)};
Chris Craik65cd6122012-12-10 17:56:27 -0800458
459 // scale to compensate for pinching at sharp angles, see totalOffsetFromNormals()
460 radialOffset *= radialScale;
461
462 AAOffset = paintInfo.deriveAAOffset(radialOffset);
463 paintInfo.scaleOffsetForStrokeWidth(radialOffset);
464 AlphaVertex::set(&buffer[capPerimIndex++],
John Reck1bcacfd2017-11-03 10:12:19 -0700465 referencePoint.x + radialOffset.x + AAOffset.x,
466 referencePoint.y + radialOffset.y + AAOffset.y, 0.0f);
Chris Craik65cd6122012-12-10 17:56:27 -0800467 AlphaVertex::set(&buffer[capPerimIndex++],
John Reck1bcacfd2017-11-03 10:12:19 -0700468 referencePoint.x + radialOffset.x - AAOffset.x,
469 referencePoint.y + radialOffset.y - AAOffset.y, paintInfo.maxAlpha);
Chris Craik65cd6122012-12-10 17:56:27 -0800470
471 if (isFirst && i == extra - extraOffset) {
John Reck1bcacfd2017-11-03 10:12:19 -0700472 // copy most recent two points to first two points
Chris Craik11a75672013-12-16 17:08:15 -0800473 buffer[0] = buffer[capPerimIndex - 2];
474 buffer[1] = buffer[capPerimIndex - 1];
Chris Craik65cd6122012-12-10 17:56:27 -0800475
John Reck1bcacfd2017-11-03 10:12:19 -0700476 capPerimIndex = 2; // start writing the rest of the round cap at index 2
Chris Craik65cd6122012-12-10 17:56:27 -0800477 }
478 }
479
480 if (isFirst) {
481 const int startCapFillIndex = capIndex + 2 * (extra - extraOffset) + 4;
482 int capFillIndex = startCapFillIndex;
483 for (int i = 0; i < extra + 2; i += 2) {
Chris Craik11a75672013-12-16 17:08:15 -0800484 buffer[capFillIndex++] = buffer[1 + i];
Chris Craik65cd6122012-12-10 17:56:27 -0800485 // TODO: to support odd numbers of divisions, break here on the last iteration
Chris Craik11a75672013-12-16 17:08:15 -0800486 buffer[capFillIndex++] = buffer[startCapFillIndex - 3 - i];
Chris Craik65cd6122012-12-10 17:56:27 -0800487 }
488 } else {
489 int capFillIndex = 6 * vertices.size() + 2 + 6 * extra - (extra + 2);
490 for (int i = 0; i < extra + 2; i += 2) {
Chris Craik11a75672013-12-16 17:08:15 -0800491 buffer[capFillIndex++] = buffer[capIndex + 1 + i];
Chris Craik65cd6122012-12-10 17:56:27 -0800492 // TODO: to support odd numbers of divisions, break here on the last iteration
Chris Craik11a75672013-12-16 17:08:15 -0800493 buffer[capFillIndex++] = buffer[capIndex + 3 + 2 * extra - i];
Chris Craik65cd6122012-12-10 17:56:27 -0800494 }
495 }
496 return;
497 }
498 if (isFirst) {
Chris Craik11a75672013-12-16 17:08:15 -0800499 buffer[0] = buffer[postCapIndex + 2];
500 buffer[1] = buffer[postCapIndex + 3];
John Reck1bcacfd2017-11-03 10:12:19 -0700501 buffer[postCapIndex + 4] = buffer[1]; // degenerate tris (the only two!)
Chris Craik11a75672013-12-16 17:08:15 -0800502 buffer[postCapIndex + 5] = buffer[postCapIndex + 1];
Chris Craik65cd6122012-12-10 17:56:27 -0800503 } else {
Chris Craik11a75672013-12-16 17:08:15 -0800504 buffer[6 * vertices.size()] = buffer[postCapIndex + 1];
505 buffer[6 * vertices.size() + 1] = buffer[postCapIndex + 3];
Chris Craik65cd6122012-12-10 17:56:27 -0800506 }
507}
508
509/*
510the geometry for an aa, capped stroke consists of the following:
511
512 # vertices | function
513----------------------------------------------------------------------
514a) 2 | Start AA perimeter
515b) 2, 2 * roundDivOff | First half of begin cap's perimeter
516 |
517 2 * middlePts | 'Outer' or 'Top' AA perimeter half (between caps)
518 |
519a) 4 | End cap's
520b) 2, 2 * roundDivs, 2 | AA perimeter
521 |
522 2 * middlePts | 'Inner' or 'bottom' AA perimeter half
523 |
524a) 6 | Begin cap's perimeter
525b) 2, 2*(rD - rDO + 1), | Last half of begin cap's perimeter
526 roundDivs, 2 |
527 |
528 2 * middlePts | Stroke's full opacity center strip
529 |
530a) 2 | end stroke
531b) 2, roundDivs | (and end cap fill, for round)
532
533Notes:
534* rows starting with 'a)' denote the Butt or Square cap vertex use, 'b)' denote Round
535
536* 'middlePts' is (number of points in the unclosed input vertex list, minus 2) times two
537
538* 'roundDivs' or 'rD' is the number of extra vertices (beyond the minimum of 2) that define the
539 round cap's shape, and is at least two. This will increase with cap size to sufficiently
540 define the cap's level of tessellation.
541
542* 'roundDivOffset' or 'rDO' is the point about halfway along the start cap's round perimeter, where
543 the stream of vertices for the AA perimeter starts. By starting and ending the perimeter at
544 this offset, the fill of the stroke is drawn from this point with minimal extra vertices.
545
546This means the outer perimeter starts at:
547 outerIndex = (2) OR (2 + 2 * roundDivOff)
548the inner perimeter (since it is filled in reverse) starts at:
549 innerIndex = outerIndex + (4 * middlePts) + ((4) OR (4 + 2 * roundDivs)) - 1
550the stroke starts at:
551 strokeIndex = innerIndex + 1 + ((6) OR (6 + 3 * roundDivs - 2 * roundDivOffset))
552
553The total needed allocated space is either:
554 2 + 4 + 6 + 2 + 3 * (2 * middlePts) = 14 + 6 * middlePts = 2 + 6 * pts
555or, for rounded caps:
556 (2 + 2 * rDO) + (4 + 2 * rD) + (2 * (rD - rDO + 1)
557 + roundDivs + 4) + (2 + roundDivs) + 3 * (2 * middlePts)
558 = 14 + 6 * middlePts + 6 * roundDivs
559 = 2 + 6 * pts + 6 * roundDivs
560 */
561void getStrokeVerticesFromUnclosedVerticesAA(const PaintInfo& paintInfo,
John Reck1bcacfd2017-11-03 10:12:19 -0700562 const std::vector<Vertex>& vertices,
563 VertexBuffer& vertexBuffer) {
Chris Craik65cd6122012-12-10 17:56:27 -0800564 const int extra = paintInfo.capExtraDivisions();
565 const int allocSize = 6 * vertices.size() + 2 + 6 * extra;
566
567 AlphaVertex* buffer = vertexBuffer.alloc<AlphaVertex>(allocSize);
568
569 const int extraOffset = (extra + 1) / 2;
570 int offset = 2 * (vertices.size() - 2);
571 // there is no outer/inner here, using them for consistency with below approach
572 int currentAAOuterIndex = 2 + 2 * extraOffset;
573 int currentAAInnerIndex = currentAAOuterIndex + (2 * offset) + 3 + (2 * extra);
574 int currentStrokeIndex = currentAAInnerIndex + 7 + (3 * extra - 2 * extraOffset);
575
576 const Vertex* last = &(vertices[0]);
577 const Vertex* current = &(vertices[1]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700578 Vector2 lastNormal = {current->y - last->y, last->x - current->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800579 lastNormal.normalize();
580
581 // TODO: use normal from bezier traversal for cap, instead of from vertices
582 storeCapAA(paintInfo, vertices, buffer, true, lastNormal, offset);
583
584 for (unsigned int i = 1; i < vertices.size() - 1; i++) {
585 const Vertex* next = &(vertices[i + 1]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700586 Vector2 nextNormal = {next->y - current->y, current->x - next->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800587 nextNormal.normalize();
588
John Reck1aa5d2d2014-07-24 13:38:28 -0700589 Vector2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
590 Vector2 AAOffset = paintInfo.deriveAAOffset(totalOffset);
Chris Craik65cd6122012-12-10 17:56:27 -0800591
John Reck1aa5d2d2014-07-24 13:38:28 -0700592 Vector2 innerOffset = totalOffset;
Chris Craik65cd6122012-12-10 17:56:27 -0800593 paintInfo.scaleOffsetForStrokeWidth(innerOffset);
John Reck1aa5d2d2014-07-24 13:38:28 -0700594 Vector2 outerOffset = innerOffset + AAOffset;
Chris Craik65cd6122012-12-10 17:56:27 -0800595 innerOffset -= AAOffset;
596
John Reck1bcacfd2017-11-03 10:12:19 -0700597 AlphaVertex::set(&buffer[currentAAOuterIndex++], current->x + outerOffset.x,
598 current->y + outerOffset.y, 0.0f);
599 AlphaVertex::set(&buffer[currentAAOuterIndex++], current->x + innerOffset.x,
600 current->y + innerOffset.y, paintInfo.maxAlpha);
Chris Craik65cd6122012-12-10 17:56:27 -0800601
John Reck1bcacfd2017-11-03 10:12:19 -0700602 AlphaVertex::set(&buffer[currentStrokeIndex++], current->x + innerOffset.x,
603 current->y + innerOffset.y, paintInfo.maxAlpha);
604 AlphaVertex::set(&buffer[currentStrokeIndex++], current->x - innerOffset.x,
605 current->y - innerOffset.y, paintInfo.maxAlpha);
Chris Craik65cd6122012-12-10 17:56:27 -0800606
John Reck1bcacfd2017-11-03 10:12:19 -0700607 AlphaVertex::set(&buffer[currentAAInnerIndex--], current->x - innerOffset.x,
608 current->y - innerOffset.y, paintInfo.maxAlpha);
609 AlphaVertex::set(&buffer[currentAAInnerIndex--], current->x - outerOffset.x,
610 current->y - outerOffset.y, 0.0f);
Chris Craik65cd6122012-12-10 17:56:27 -0800611
612 current = next;
613 lastNormal = nextNormal;
614 }
615
616 // TODO: use normal from bezier traversal for cap, instead of from vertices
617 storeCapAA(paintInfo, vertices, buffer, false, lastNormal, offset);
618
619 DEBUG_DUMP_ALPHA_BUFFER();
620}
621
John Reck272a6852015-07-29 16:48:58 -0700622void getStrokeVerticesFromPerimeterAA(const PaintInfo& paintInfo,
John Reck1bcacfd2017-11-03 10:12:19 -0700623 const std::vector<Vertex>& perimeter,
624 VertexBuffer& vertexBuffer) {
Chris Craik65cd6122012-12-10 17:56:27 -0800625 AlphaVertex* buffer = vertexBuffer.alloc<AlphaVertex>(6 * perimeter.size() + 8);
626
627 int offset = 2 * perimeter.size() + 3;
628 int currentAAOuterIndex = 0;
629 int currentStrokeIndex = offset;
630 int currentAAInnerIndex = offset * 2;
631
632 const Vertex* last = &(perimeter[perimeter.size() - 1]);
633 const Vertex* current = &(perimeter[0]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700634 Vector2 lastNormal = {current->y - last->y, last->x - current->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800635 lastNormal.normalize();
636 for (unsigned int i = 0; i < perimeter.size(); i++) {
637 const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700638 Vector2 nextNormal = {next->y - current->y, current->x - next->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800639 nextNormal.normalize();
640
John Reck1aa5d2d2014-07-24 13:38:28 -0700641 Vector2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
642 Vector2 AAOffset = paintInfo.deriveAAOffset(totalOffset);
Chris Craik65cd6122012-12-10 17:56:27 -0800643
John Reck1aa5d2d2014-07-24 13:38:28 -0700644 Vector2 innerOffset = totalOffset;
Chris Craik65cd6122012-12-10 17:56:27 -0800645 paintInfo.scaleOffsetForStrokeWidth(innerOffset);
John Reck1aa5d2d2014-07-24 13:38:28 -0700646 Vector2 outerOffset = innerOffset + AAOffset;
Chris Craik65cd6122012-12-10 17:56:27 -0800647 innerOffset -= AAOffset;
648
John Reck1bcacfd2017-11-03 10:12:19 -0700649 AlphaVertex::set(&buffer[currentAAOuterIndex++], current->x + outerOffset.x,
650 current->y + outerOffset.y, 0.0f);
651 AlphaVertex::set(&buffer[currentAAOuterIndex++], current->x + innerOffset.x,
652 current->y + innerOffset.y, paintInfo.maxAlpha);
Chris Craik65cd6122012-12-10 17:56:27 -0800653
John Reck1bcacfd2017-11-03 10:12:19 -0700654 AlphaVertex::set(&buffer[currentStrokeIndex++], current->x + innerOffset.x,
655 current->y + innerOffset.y, paintInfo.maxAlpha);
656 AlphaVertex::set(&buffer[currentStrokeIndex++], current->x - innerOffset.x,
657 current->y - innerOffset.y, paintInfo.maxAlpha);
Chris Craik65cd6122012-12-10 17:56:27 -0800658
John Reck1bcacfd2017-11-03 10:12:19 -0700659 AlphaVertex::set(&buffer[currentAAInnerIndex++], current->x - innerOffset.x,
660 current->y - innerOffset.y, paintInfo.maxAlpha);
661 AlphaVertex::set(&buffer[currentAAInnerIndex++], current->x - outerOffset.x,
662 current->y - outerOffset.y, 0.0f);
Chris Craik65cd6122012-12-10 17:56:27 -0800663
Chris Craik65cd6122012-12-10 17:56:27 -0800664 current = next;
665 lastNormal = nextNormal;
666 }
667
668 // wrap each strip around to beginning, creating degenerate tris to bridge strips
Chris Craik11a75672013-12-16 17:08:15 -0800669 buffer[currentAAOuterIndex++] = buffer[0];
670 buffer[currentAAOuterIndex++] = buffer[1];
671 buffer[currentAAOuterIndex++] = buffer[1];
Chris Craik65cd6122012-12-10 17:56:27 -0800672
Chris Craik11a75672013-12-16 17:08:15 -0800673 buffer[currentStrokeIndex++] = buffer[offset];
674 buffer[currentStrokeIndex++] = buffer[offset + 1];
675 buffer[currentStrokeIndex++] = buffer[offset + 1];
Chris Craik65cd6122012-12-10 17:56:27 -0800676
Chris Craik11a75672013-12-16 17:08:15 -0800677 buffer[currentAAInnerIndex++] = buffer[2 * offset];
678 buffer[currentAAInnerIndex++] = buffer[2 * offset + 1];
Chris Craik65cd6122012-12-10 17:56:27 -0800679 // don't need to create last degenerate tri
680
681 DEBUG_DUMP_ALPHA_BUFFER();
682}
683
John Reck1bcacfd2017-11-03 10:12:19 -0700684void PathTessellator::tessellatePath(const SkPath& path, const SkPaint* paint,
685 const mat4& transform, VertexBuffer& vertexBuffer) {
Chris Craik65cd6122012-12-10 17:56:27 -0800686 ATRACE_CALL();
687
688 const PaintInfo paintInfo(paint, transform);
689
John Reck272a6852015-07-29 16:48:58 -0700690 std::vector<Vertex> tempVertices;
Chris Craik65cd6122012-12-10 17:56:27 -0800691 float threshInvScaleX = paintInfo.inverseScaleX;
692 float threshInvScaleY = paintInfo.inverseScaleY;
693 if (paintInfo.style == SkPaint::kStroke_Style) {
694 // alter the bezier recursion threshold values we calculate in order to compensate for
695 // expansion done after the path vertices are found
696 SkRect bounds = path.getBounds();
697 if (!bounds.isEmpty()) {
698 threshInvScaleX *= bounds.width() / (bounds.width() + paint->getStrokeWidth());
699 threshInvScaleY *= bounds.height() / (bounds.height() + paint->getStrokeWidth());
700 }
701 }
702
703 // force close if we're filling the path, since fill path expects closed perimeter.
704 bool forceClose = paintInfo.style != SkPaint::kStroke_Style;
ztenghui21ef8202015-05-28 16:06:28 -0700705 PathApproximationInfo approximationInfo(threshInvScaleX, threshInvScaleY,
John Reck1bcacfd2017-11-03 10:12:19 -0700706 OUTLINE_REFINE_THRESHOLD);
707 bool wasClosed =
708 approximatePathOutlineVertices(path, forceClose, approximationInfo, tempVertices);
Chris Craik65cd6122012-12-10 17:56:27 -0800709
710 if (!tempVertices.size()) {
711 // path was empty, return without allocating vertex buffer
712 return;
713 }
714
715#if VERTEX_DEBUG
716 for (unsigned int i = 0; i < tempVertices.size(); i++) {
John Reck1bcacfd2017-11-03 10:12:19 -0700717 ALOGD("orig path: point at %f %f", tempVertices[i].x, tempVertices[i].y);
Chris Craik65cd6122012-12-10 17:56:27 -0800718 }
719#endif
720
721 if (paintInfo.style == SkPaint::kStroke_Style) {
722 if (!paintInfo.isAA) {
723 if (wasClosed) {
724 getStrokeVerticesFromPerimeter(paintInfo, tempVertices, vertexBuffer);
725 } else {
726 getStrokeVerticesFromUnclosedVertices(paintInfo, tempVertices, vertexBuffer);
727 }
728
729 } else {
730 if (wasClosed) {
731 getStrokeVerticesFromPerimeterAA(paintInfo, tempVertices, vertexBuffer);
732 } else {
733 getStrokeVerticesFromUnclosedVerticesAA(paintInfo, tempVertices, vertexBuffer);
734 }
735 }
736 } else {
737 // For kStrokeAndFill style, the path should be adjusted externally.
738 // It will be treated as a fill here.
739 if (!paintInfo.isAA) {
740 getFillVerticesFromPerimeter(tempVertices, vertexBuffer);
741 } else {
742 getFillVerticesFromPerimeterAA(paintInfo, tempVertices, vertexBuffer);
743 }
744 }
Chris Craik05f3d6e2014-06-02 16:27:04 -0700745
746 Rect bounds(path.getBounds());
747 paintInfo.expandBoundsForStroke(&bounds);
748 vertexBuffer.setBounds(bounds);
Chris Craik117bdbc2015-02-05 10:12:38 -0800749 vertexBuffer.setMeshFeatureFlags(paintInfo.isAA ? VertexBuffer::kAlpha : VertexBuffer::kNone);
Chris Craik65cd6122012-12-10 17:56:27 -0800750}
751
Chris Craik6d29c8d2013-05-08 18:35:44 -0700752template <class TYPE>
John Reck1bcacfd2017-11-03 10:12:19 -0700753static void instanceVertices(VertexBuffer& srcBuffer, VertexBuffer& dstBuffer, const float* points,
754 int count, Rect& bounds) {
Chris Craik6d29c8d2013-05-08 18:35:44 -0700755 bounds.set(points[0], points[1], points[0], points[1]);
756
757 int numPoints = count / 2;
758 int verticesPerPoint = srcBuffer.getVertexCount();
759 dstBuffer.alloc<TYPE>(numPoints * verticesPerPoint + (numPoints - 1) * 2);
760
761 for (int i = 0; i < count; i += 2) {
Chris Craik15c3f192015-12-03 12:16:56 -0800762 bounds.expandToCover(points[i + 0], points[i + 1]);
Chris Craik6d29c8d2013-05-08 18:35:44 -0700763 dstBuffer.copyInto<TYPE>(srcBuffer, points[i + 0], points[i + 1]);
764 }
765 dstBuffer.createDegenerateSeparators<TYPE>(verticesPerPoint);
766}
767
Chris Craikd218a922014-01-02 17:13:34 -0800768void PathTessellator::tessellatePoints(const float* points, int count, const SkPaint* paint,
John Reck1bcacfd2017-11-03 10:12:19 -0700769 const mat4& transform, VertexBuffer& vertexBuffer) {
Chris Craik6d29c8d2013-05-08 18:35:44 -0700770 const PaintInfo paintInfo(paint, transform);
771
772 // determine point shape
773 SkPath path;
774 float radius = paintInfo.halfStrokeWidth;
Rob Tsukb7c26562014-11-03 16:29:03 -0800775 if (radius == 0.0f) radius = 0.5f;
Chris Craik6d29c8d2013-05-08 18:35:44 -0700776
777 if (paintInfo.cap == SkPaint::kRound_Cap) {
778 path.addCircle(0, 0, radius);
779 } else {
780 path.addRect(-radius, -radius, radius, radius);
781 }
782
783 // calculate outline
John Reck272a6852015-07-29 16:48:58 -0700784 std::vector<Vertex> outlineVertices;
ztenghui21ef8202015-05-28 16:06:28 -0700785 PathApproximationInfo approximationInfo(paintInfo.inverseScaleX, paintInfo.inverseScaleY,
John Reck1bcacfd2017-11-03 10:12:19 -0700786 OUTLINE_REFINE_THRESHOLD);
ztenghui21ef8202015-05-28 16:06:28 -0700787 approximatePathOutlineVertices(path, true, approximationInfo, outlineVertices);
Chris Craik6d29c8d2013-05-08 18:35:44 -0700788
789 if (!outlineVertices.size()) return;
790
Chris Craik05f3d6e2014-06-02 16:27:04 -0700791 Rect bounds;
Chris Craik6d29c8d2013-05-08 18:35:44 -0700792 // tessellate, then duplicate outline across points
Chris Craik6d29c8d2013-05-08 18:35:44 -0700793 VertexBuffer tempBuffer;
794 if (!paintInfo.isAA) {
795 getFillVerticesFromPerimeter(outlineVertices, tempBuffer);
796 instanceVertices<Vertex>(tempBuffer, vertexBuffer, points, count, bounds);
797 } else {
Chris Craikf0a59072013-11-19 18:00:46 -0800798 // note: pass maxAlpha directly, since we want fill to be alpha modulated
799 getFillVerticesFromPerimeterAA(paintInfo, outlineVertices, tempBuffer, paintInfo.maxAlpha);
Chris Craik6d29c8d2013-05-08 18:35:44 -0700800 instanceVertices<AlphaVertex>(tempBuffer, vertexBuffer, points, count, bounds);
801 }
802
Chris Craikf0a59072013-11-19 18:00:46 -0800803 // expand bounds from vertex coords to pixel data
Chris Craik05f3d6e2014-06-02 16:27:04 -0700804 paintInfo.expandBoundsForStroke(&bounds);
805 vertexBuffer.setBounds(bounds);
Chris Craik117bdbc2015-02-05 10:12:38 -0800806 vertexBuffer.setMeshFeatureFlags(paintInfo.isAA ? VertexBuffer::kAlpha : VertexBuffer::kNone);
Chris Craik65cd6122012-12-10 17:56:27 -0800807}
808
Chris Craikd218a922014-01-02 17:13:34 -0800809void PathTessellator::tessellateLines(const float* points, int count, const SkPaint* paint,
John Reck1bcacfd2017-11-03 10:12:19 -0700810 const mat4& transform, VertexBuffer& vertexBuffer) {
Chris Craik65cd6122012-12-10 17:56:27 -0800811 ATRACE_CALL();
812 const PaintInfo paintInfo(paint, transform);
813
814 const int extra = paintInfo.capExtraDivisions();
815 int numLines = count / 4;
816 int lineAllocSize;
817 // pre-allocate space for lines in the buffer, and degenerate tris in between
818 if (paintInfo.isAA) {
819 lineAllocSize = 6 * (2) + 2 + 6 * extra;
820 vertexBuffer.alloc<AlphaVertex>(numLines * lineAllocSize + (numLines - 1) * 2);
821 } else {
822 lineAllocSize = 2 * ((2) + extra);
823 vertexBuffer.alloc<Vertex>(numLines * lineAllocSize + (numLines - 1) * 2);
824 }
825
John Reck272a6852015-07-29 16:48:58 -0700826 std::vector<Vertex> tempVertices(2);
827 Vertex* tempVerticesData = &tempVertices.front();
Chris Craik05f3d6e2014-06-02 16:27:04 -0700828 Rect bounds;
Chris Craik65cd6122012-12-10 17:56:27 -0800829 bounds.set(points[0], points[1], points[0], points[1]);
830 for (int i = 0; i < count; i += 4) {
831 Vertex::set(&(tempVerticesData[0]), points[i + 0], points[i + 1]);
832 Vertex::set(&(tempVerticesData[1]), points[i + 2], points[i + 3]);
833
834 if (paintInfo.isAA) {
835 getStrokeVerticesFromUnclosedVerticesAA(paintInfo, tempVertices, vertexBuffer);
836 } else {
837 getStrokeVerticesFromUnclosedVertices(paintInfo, tempVertices, vertexBuffer);
838 }
839
840 // calculate bounds
Chris Craik15c3f192015-12-03 12:16:56 -0800841 bounds.expandToCover(tempVerticesData[0].x, tempVerticesData[0].y);
842 bounds.expandToCover(tempVerticesData[1].x, tempVerticesData[1].y);
Chris Craik65cd6122012-12-10 17:56:27 -0800843 }
844
Chris Craik65cd6122012-12-10 17:56:27 -0800845 // since multiple objects tessellated into buffer, separate them with degen tris
846 if (paintInfo.isAA) {
847 vertexBuffer.createDegenerateSeparators<AlphaVertex>(lineAllocSize);
848 } else {
849 vertexBuffer.createDegenerateSeparators<Vertex>(lineAllocSize);
850 }
Chris Craikf0a59072013-11-19 18:00:46 -0800851
852 // expand bounds from vertex coords to pixel data
Chris Craik05f3d6e2014-06-02 16:27:04 -0700853 paintInfo.expandBoundsForStroke(&bounds);
854 vertexBuffer.setBounds(bounds);
Chris Craik117bdbc2015-02-05 10:12:38 -0800855 vertexBuffer.setMeshFeatureFlags(paintInfo.isAA ? VertexBuffer::kAlpha : VertexBuffer::kNone);
Chris Craik65cd6122012-12-10 17:56:27 -0800856}
857
858///////////////////////////////////////////////////////////////////////////////
859// Simple path line approximation
860///////////////////////////////////////////////////////////////////////////////
861
ztenghui21ef8202015-05-28 16:06:28 -0700862bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, float threshold,
John Reck1bcacfd2017-11-03 10:12:19 -0700863 std::vector<Vertex>& outputVertices) {
ztenghui21ef8202015-05-28 16:06:28 -0700864 PathApproximationInfo approximationInfo(1.0f, 1.0f, threshold);
865 return approximatePathOutlineVertices(path, true, approximationInfo, outputVertices);
Chris Craik15a07a22014-01-26 13:43:53 -0800866}
867
Chris Craikfca52b752015-04-28 11:45:59 -0700868class ClockwiseEnforcer {
869public:
870 void addPoint(const SkPoint& point) {
871 double x = point.x();
872 double y = point.y();
873
874 if (initialized) {
875 sum += (x + lastX) * (y - lastY);
876 } else {
877 initialized = true;
878 }
879
880 lastX = x;
881 lastY = y;
882 }
John Reck272a6852015-07-29 16:48:58 -0700883 void reverseVectorIfNotClockwise(std::vector<Vertex>& vertices) {
Chris Craikfca52b752015-04-28 11:45:59 -0700884 if (sum < 0) {
885 // negative sum implies CounterClockwise
886 const int size = vertices.size();
887 for (int i = 0; i < size / 2; i++) {
888 Vertex tmp = vertices[i];
889 int k = size - 1 - i;
John Reck272a6852015-07-29 16:48:58 -0700890 vertices[i] = vertices[k];
891 vertices[k] = tmp;
Chris Craikfca52b752015-04-28 11:45:59 -0700892 }
893 }
894 }
John Reck1bcacfd2017-11-03 10:12:19 -0700895
Chris Craikfca52b752015-04-28 11:45:59 -0700896private:
897 bool initialized = false;
Chris Craik469fb1e2015-05-28 15:33:40 -0700898 double lastX = 0;
899 double lastY = 0;
Chris Craikfca52b752015-04-28 11:45:59 -0700900 double sum = 0;
901};
902
Chris Craik65cd6122012-12-10 17:56:27 -0800903bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool forceClose,
John Reck1bcacfd2017-11-03 10:12:19 -0700904 const PathApproximationInfo& approximationInfo,
905 std::vector<Vertex>& outputVertices) {
Chris Craik65cd6122012-12-10 17:56:27 -0800906 ATRACE_CALL();
907
908 // TODO: to support joins other than sharp miter, join vertices should be labelled in the
909 // perimeter, or resolved into more vertices. Reconsider forceClose-ing in that case.
910 SkPath::Iter iter(path, forceClose);
911 SkPoint pts[4];
912 SkPath::Verb v;
Chris Craikfca52b752015-04-28 11:45:59 -0700913 ClockwiseEnforcer clockwiseEnforcer;
Chris Craik65cd6122012-12-10 17:56:27 -0800914 while (SkPath::kDone_Verb != (v = iter.next(pts))) {
John Reck1bcacfd2017-11-03 10:12:19 -0700915 switch (v) {
Chris Craik65cd6122012-12-10 17:56:27 -0800916 case SkPath::kMove_Verb:
John Reck272a6852015-07-29 16:48:58 -0700917 outputVertices.push_back(Vertex{pts[0].x(), pts[0].y()});
Chris Craik65cd6122012-12-10 17:56:27 -0800918 ALOGV("Move to pos %f %f", pts[0].x(), pts[0].y());
Chris Craikfca52b752015-04-28 11:45:59 -0700919 clockwiseEnforcer.addPoint(pts[0]);
Chris Craik65cd6122012-12-10 17:56:27 -0800920 break;
921 case SkPath::kClose_Verb:
922 ALOGV("Close at pos %f %f", pts[0].x(), pts[0].y());
Chris Craikfca52b752015-04-28 11:45:59 -0700923 clockwiseEnforcer.addPoint(pts[0]);
Chris Craik65cd6122012-12-10 17:56:27 -0800924 break;
925 case SkPath::kLine_Verb:
926 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 -0700927 outputVertices.push_back(Vertex{pts[1].x(), pts[1].y()});
Chris Craikfca52b752015-04-28 11:45:59 -0700928 clockwiseEnforcer.addPoint(pts[1]);
Chris Craik65cd6122012-12-10 17:56:27 -0800929 break;
930 case SkPath::kQuad_Verb:
931 ALOGV("kQuad_Verb");
John Reck1bcacfd2017-11-03 10:12:19 -0700932 recursiveQuadraticBezierVertices(pts[0].x(), pts[0].y(), pts[2].x(), pts[2].y(),
933 pts[1].x(), pts[1].y(), approximationInfo,
934 outputVertices);
Chris Craikfca52b752015-04-28 11:45:59 -0700935 clockwiseEnforcer.addPoint(pts[1]);
936 clockwiseEnforcer.addPoint(pts[2]);
Chris Craik65cd6122012-12-10 17:56:27 -0800937 break;
938 case SkPath::kCubic_Verb:
939 ALOGV("kCubic_Verb");
John Reck1bcacfd2017-11-03 10:12:19 -0700940 recursiveCubicBezierVertices(pts[0].x(), pts[0].y(), pts[1].x(), pts[1].y(),
941 pts[3].x(), pts[3].y(), pts[2].x(), pts[2].y(),
942 approximationInfo, outputVertices);
Chris Craikfca52b752015-04-28 11:45:59 -0700943 clockwiseEnforcer.addPoint(pts[1]);
944 clockwiseEnforcer.addPoint(pts[2]);
945 clockwiseEnforcer.addPoint(pts[3]);
Chris Craik65cd6122012-12-10 17:56:27 -0800946 break;
Derek Sollenbergerb0772a62015-03-16 14:35:55 -0400947 case SkPath::kConic_Verb: {
948 ALOGV("kConic_Verb");
949 SkAutoConicToQuads converter;
John Reck1bcacfd2017-11-03 10:12:19 -0700950 const SkPoint* quads = converter.computeQuads(
951 pts, iter.conicWeight(), approximationInfo.thresholdForConicQuads);
Derek Sollenbergerb0772a62015-03-16 14:35:55 -0400952 for (int i = 0; i < converter.countQuads(); ++i) {
953 const int offset = 2 * i;
John Reck1bcacfd2017-11-03 10:12:19 -0700954 recursiveQuadraticBezierVertices(quads[offset].x(), quads[offset].y(),
955 quads[offset + 2].x(), quads[offset + 2].y(),
956 quads[offset + 1].x(), quads[offset + 1].y(),
957 approximationInfo, outputVertices);
Derek Sollenbergerb0772a62015-03-16 14:35:55 -0400958 }
Chris Craikfca52b752015-04-28 11:45:59 -0700959 clockwiseEnforcer.addPoint(pts[1]);
960 clockwiseEnforcer.addPoint(pts[2]);
Derek Sollenbergerb0772a62015-03-16 14:35:55 -0400961 break;
962 }
Chris Craik65cd6122012-12-10 17:56:27 -0800963 default:
John Reck1bcacfd2017-11-03 10:12:19 -0700964 static_assert(SkPath::kMove_Verb == 0 && SkPath::kLine_Verb == 1 &&
965 SkPath::kQuad_Verb == 2 && SkPath::kConic_Verb == 3 &&
966 SkPath::kCubic_Verb == 4 && SkPath::kClose_Verb == 5 &&
967 SkPath::kDone_Verb == 6,
968 "Path enum changed, new types may have been added");
Chris Craik65cd6122012-12-10 17:56:27 -0800969 break;
John Reck1bcacfd2017-11-03 10:12:19 -0700970 }
Chris Craik65cd6122012-12-10 17:56:27 -0800971 }
972
Chris Craikfca52b752015-04-28 11:45:59 -0700973 bool wasClosed = false;
Chris Craik65cd6122012-12-10 17:56:27 -0800974 int size = outputVertices.size();
Romain Guy3380cfd2013-08-15 16:57:57 -0700975 if (size >= 2 && outputVertices[0].x == outputVertices[size - 1].x &&
John Reck1bcacfd2017-11-03 10:12:19 -0700976 outputVertices[0].y == outputVertices[size - 1].y) {
John Reck272a6852015-07-29 16:48:58 -0700977 outputVertices.pop_back();
Chris Craikfca52b752015-04-28 11:45:59 -0700978 wasClosed = true;
Chris Craik65cd6122012-12-10 17:56:27 -0800979 }
Chris Craikfca52b752015-04-28 11:45:59 -0700980
981 // ensure output vector is clockwise
982 clockwiseEnforcer.reverseVectorIfNotClockwise(outputVertices);
983 return wasClosed;
Chris Craik65cd6122012-12-10 17:56:27 -0800984}
985
986///////////////////////////////////////////////////////////////////////////////
987// Bezier approximation
ztenghui21ef8202015-05-28 16:06:28 -0700988//
989// All the inputs and outputs here are in path coordinates.
990// We convert the error threshold from screen coordinates into path coordinates.
Chris Craik65cd6122012-12-10 17:56:27 -0800991///////////////////////////////////////////////////////////////////////////////
992
ztenghui21ef8202015-05-28 16:06:28 -0700993// Get a threshold in path coordinates, by scaling the thresholdSquared from screen coordinates.
994// TODO: Document the math behind this algorithm.
995static inline float getThreshold(const PathApproximationInfo& info, float dx, float dy) {
996 // multiplying by sqrInvScaleY/X equivalent to multiplying in dimensional scale factors
997 float scale = (dx * dx * info.sqrInvScaleY + dy * dy * info.sqrInvScaleX);
998 return info.thresholdSquared * scale;
999}
1000
John Reck1bcacfd2017-11-03 10:12:19 -07001001void PathTessellator::recursiveCubicBezierVertices(float p1x, float p1y, float c1x, float c1y,
1002 float p2x, float p2y, float c2x, float c2y,
1003 const PathApproximationInfo& approximationInfo,
1004 std::vector<Vertex>& outputVertices, int depth) {
Chris Craik65cd6122012-12-10 17:56:27 -08001005 float dx = p2x - p1x;
1006 float dy = p2y - p1y;
1007 float d1 = fabs((c1x - p2x) * dy - (c1y - p2y) * dx);
1008 float d2 = fabs((c2x - p2x) * dy - (c2y - p2y) * dx);
1009 float d = d1 + d2;
1010
John Reck1bcacfd2017-11-03 10:12:19 -07001011 if (depth >= MAX_DEPTH || d * d <= getThreshold(approximationInfo, dx, dy)) {
Chris Craik65cd6122012-12-10 17:56:27 -08001012 // below thresh, draw line by adding endpoint
John Reck272a6852015-07-29 16:48:58 -07001013 outputVertices.push_back(Vertex{p2x, p2y});
Chris Craik65cd6122012-12-10 17:56:27 -08001014 } else {
1015 float p1c1x = (p1x + c1x) * 0.5f;
1016 float p1c1y = (p1y + c1y) * 0.5f;
1017 float p2c2x = (p2x + c2x) * 0.5f;
1018 float p2c2y = (p2y + c2y) * 0.5f;
1019
1020 float c1c2x = (c1x + c2x) * 0.5f;
1021 float c1c2y = (c1y + c2y) * 0.5f;
1022
1023 float p1c1c2x = (p1c1x + c1c2x) * 0.5f;
1024 float p1c1c2y = (p1c1y + c1c2y) * 0.5f;
1025
1026 float p2c1c2x = (p2c2x + c1c2x) * 0.5f;
1027 float p2c1c2y = (p2c2y + c1c2y) * 0.5f;
1028
1029 float mx = (p1c1c2x + p2c1c2x) * 0.5f;
1030 float my = (p1c1c2y + p2c1c2y) * 0.5f;
1031
John Reck1bcacfd2017-11-03 10:12:19 -07001032 recursiveCubicBezierVertices(p1x, p1y, p1c1x, p1c1y, mx, my, p1c1c2x, p1c1c2y,
1033 approximationInfo, outputVertices, depth + 1);
1034 recursiveCubicBezierVertices(mx, my, p2c1c2x, p2c1c2y, p2x, p2y, p2c2x, p2c2y,
1035 approximationInfo, outputVertices, depth + 1);
Chris Craik65cd6122012-12-10 17:56:27 -08001036 }
1037}
1038
1039void PathTessellator::recursiveQuadraticBezierVertices(
John Reck1bcacfd2017-11-03 10:12:19 -07001040 float ax, float ay, float bx, float by, float cx, float cy,
1041 const PathApproximationInfo& approximationInfo, std::vector<Vertex>& outputVertices,
1042 int depth) {
Chris Craik65cd6122012-12-10 17:56:27 -08001043 float dx = bx - ax;
1044 float dy = by - ay;
ztenghui21ef8202015-05-28 16:06:28 -07001045 // d is the cross product of vector (B-A) and (C-B).
Chris Craik65cd6122012-12-10 17:56:27 -08001046 float d = (cx - bx) * dy - (cy - by) * dx;
1047
John Reck1bcacfd2017-11-03 10:12:19 -07001048 if (depth >= MAX_DEPTH || d * d <= getThreshold(approximationInfo, dx, dy)) {
Chris Craik65cd6122012-12-10 17:56:27 -08001049 // below thresh, draw line by adding endpoint
John Reck272a6852015-07-29 16:48:58 -07001050 outputVertices.push_back(Vertex{bx, by});
Chris Craik65cd6122012-12-10 17:56:27 -08001051 } else {
1052 float acx = (ax + cx) * 0.5f;
1053 float bcx = (bx + cx) * 0.5f;
1054 float acy = (ay + cy) * 0.5f;
1055 float bcy = (by + cy) * 0.5f;
1056
1057 // midpoint
1058 float mx = (acx + bcx) * 0.5f;
1059 float my = (acy + bcy) * 0.5f;
1060
John Reck1bcacfd2017-11-03 10:12:19 -07001061 recursiveQuadraticBezierVertices(ax, ay, mx, my, acx, acy, approximationInfo,
1062 outputVertices, depth + 1);
1063 recursiveQuadraticBezierVertices(mx, my, bx, by, bcx, bcy, approximationInfo,
1064 outputVertices, depth + 1);
Chris Craik65cd6122012-12-10 17:56:27 -08001065 }
1066}
1067
John Reck1bcacfd2017-11-03 10:12:19 -07001068}; // namespace uirenderer
1069}; // namespace android