blob: 8fa187c1e5305aa0397b523011182f271d8dbc14 [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
35#include <SkPath.h>
36#include <SkPaint.h>
Chris Craikfca52b752015-04-28 11:45:59 -070037#include <SkPoint.h>
Derek Sollenbergerb0772a62015-03-16 14:35:55 -040038#include <SkGeometry.h> // WARNING: Internal Skia Header
Chris Craik65cd6122012-12-10 17:56:27 -080039
40#include <stdlib.h>
41#include <stdint.h>
42#include <sys/types.h>
43
44#include <utils/Log.h>
45#include <utils/Trace.h>
46
47#include "PathTessellator.h"
48#include "Matrix.h"
49#include "Vector.h"
50#include "Vertex.h"
Chris Craik74cf7e62014-08-07 14:34:46 -070051#include "utils/MathUtils.h"
Chris Craik65cd6122012-12-10 17:56:27 -080052
53namespace android {
54namespace uirenderer {
55
ztenghui21ef8202015-05-28 16:06:28 -070056#define OUTLINE_REFINE_THRESHOLD 0.5f
Chris Craik65cd6122012-12-10 17:56:27 -080057#define ROUND_CAP_THRESH 0.25f
58#define PI 3.1415926535897932f
Chris Craik74cf7e62014-08-07 14:34:46 -070059#define MAX_DEPTH 15
Chris Craik65cd6122012-12-10 17:56:27 -080060
Chris Craik74cf7e62014-08-07 14:34:46 -070061/**
62 * Extracts the x and y scale from the transform as positive values, and clamps them
63 */
Chris Craik05f3d6e2014-06-02 16:27:04 -070064void PathTessellator::extractTessellationScales(const Matrix4& transform,
65 float* scaleX, float* scaleY) {
Chris Craikfe02b4b2014-06-16 16:34:29 -070066 if (CC_LIKELY(transform.isPureTranslate())) {
67 *scaleX = 1.0f;
68 *scaleY = 1.0f;
69 } else {
Chris Craik05f3d6e2014-06-02 16:27:04 -070070 float m00 = transform.data[Matrix4::kScaleX];
71 float m01 = transform.data[Matrix4::kSkewY];
72 float m10 = transform.data[Matrix4::kSkewX];
73 float m11 = transform.data[Matrix4::kScaleY];
Chris Craik74cf7e62014-08-07 14:34:46 -070074 *scaleX = MathUtils::clampTessellationScale(sqrt(m00 * m00 + m01 * m01));
75 *scaleY = MathUtils::clampTessellationScale(sqrt(m10 * m10 + m11 * m11));
Chris Craik65cd6122012-12-10 17:56:27 -080076 }
77}
78
Chris Craik65cd6122012-12-10 17:56:27 -080079/**
80 * Produces a pseudo-normal for a vertex, given the normals of the two incoming lines. If the offset
81 * from each vertex in a perimeter is calculated, the resultant lines connecting the offset vertices
82 * will be offset by 1.0
83 *
84 * Note that we can't add and normalize the two vectors, that would result in a rectangle having an
85 * offset of (sqrt(2)/2, sqrt(2)/2) at each corner, instead of (1, 1)
86 *
87 * NOTE: assumes angles between normals 90 degrees or less
88 */
John Reck1aa5d2d2014-07-24 13:38:28 -070089inline static Vector2 totalOffsetFromNormals(const Vector2& normalA, const Vector2& normalB) {
Chris Craik65cd6122012-12-10 17:56:27 -080090 return (normalA + normalB) / (1 + fabs(normalA.dot(normalB)));
91}
92
93/**
94 * Structure used for storing useful information about the SkPaint and scale used for tessellating
95 */
96struct PaintInfo {
97public:
Chris Craikd6b65f62014-01-01 14:45:21 -080098 PaintInfo(const SkPaint* paint, const mat4& transform) :
Chris Craik65cd6122012-12-10 17:56:27 -080099 style(paint->getStyle()), cap(paint->getStrokeCap()), isAA(paint->isAntiAlias()),
Chris Craik65cd6122012-12-10 17:56:27 -0800100 halfStrokeWidth(paint->getStrokeWidth() * 0.5f), maxAlpha(1.0f) {
101 // compute inverse scales
Chris Craikfe02b4b2014-06-16 16:34:29 -0700102 if (CC_LIKELY(transform.isPureTranslate())) {
103 inverseScaleX = 1.0f;
104 inverseScaleY = 1.0f;
105 } else {
Chris Craik05f3d6e2014-06-02 16:27:04 -0700106 float scaleX, scaleY;
107 PathTessellator::extractTessellationScales(transform, &scaleX, &scaleY);
Chris Craik74cf7e62014-08-07 14:34:46 -0700108 inverseScaleX = 1.0f / scaleX;
109 inverseScaleY = 1.0f / scaleY;
Chris Craik65cd6122012-12-10 17:56:27 -0800110 }
111
112 if (isAA && halfStrokeWidth != 0 && inverseScaleX == inverseScaleY &&
Chris Craik19a390b2013-02-27 15:43:26 -0800113 2 * halfStrokeWidth < inverseScaleX) {
Chris Craik05f3d6e2014-06-02 16:27:04 -0700114 // AA, with non-hairline stroke, width < 1 pixel. Scale alpha and treat as hairline.
Chris Craik65cd6122012-12-10 17:56:27 -0800115 maxAlpha *= (2 * halfStrokeWidth) / inverseScaleX;
116 halfStrokeWidth = 0.0f;
117 }
118 }
119
120 SkPaint::Style style;
121 SkPaint::Cap cap;
122 bool isAA;
123 float inverseScaleX;
124 float inverseScaleY;
125 float halfStrokeWidth;
126 float maxAlpha;
127
John Reck1aa5d2d2014-07-24 13:38:28 -0700128 inline void scaleOffsetForStrokeWidth(Vector2& offset) const {
Chris Craik65cd6122012-12-10 17:56:27 -0800129 if (halfStrokeWidth == 0.0f) {
130 // hairline - compensate for scale
131 offset.x *= 0.5f * inverseScaleX;
132 offset.y *= 0.5f * inverseScaleY;
133 } else {
134 offset *= halfStrokeWidth;
135 }
136 }
137
138 /**
139 * NOTE: the input will not always be a normal, especially for sharp edges - it should be the
140 * result of totalOffsetFromNormals (see documentation there)
141 */
John Reck1aa5d2d2014-07-24 13:38:28 -0700142 inline Vector2 deriveAAOffset(const Vector2& offset) const {
143 return (Vector2){offset.x * 0.5f * inverseScaleX, offset.y * 0.5f * inverseScaleY};
Chris Craik65cd6122012-12-10 17:56:27 -0800144 }
145
146 /**
147 * Returns the number of cap divisions beyond the minimum 2 (kButt_Cap/kSquareCap will return 0)
148 * Should only be used when stroking and drawing caps
149 */
150 inline int capExtraDivisions() const {
151 if (cap == SkPaint::kRound_Cap) {
Chris Craikff29b5a2015-05-27 18:17:03 -0700152 // always use 2 points for hairline
Chris Craik65cd6122012-12-10 17:56:27 -0800153 if (halfStrokeWidth == 0.0f) return 2;
154
Chris Craikff29b5a2015-05-27 18:17:03 -0700155 float threshold = MathUtils::min(inverseScaleX, inverseScaleY) * ROUND_CAP_THRESH;
156 return MathUtils::divisionsNeededToApproximateArc(halfStrokeWidth, PI, threshold);
Chris Craik65cd6122012-12-10 17:56:27 -0800157 }
158 return 0;
159 }
Chris Craikf0a59072013-11-19 18:00:46 -0800160
161 /**
Chris Craik21bcfc4f2014-09-08 18:43:30 -0700162 * Outset the bounds of point data (for line endpoints or points) to account for stroke
Chris Craikf0a59072013-11-19 18:00:46 -0800163 * geometry.
Chris Craik21bcfc4f2014-09-08 18:43:30 -0700164 *
165 * bounds are in pre-scaled space.
Chris Craikf0a59072013-11-19 18:00:46 -0800166 */
Chris Craik05f3d6e2014-06-02 16:27:04 -0700167 void expandBoundsForStroke(Rect* bounds) const {
Chris Craik21bcfc4f2014-09-08 18:43:30 -0700168 if (halfStrokeWidth == 0) {
169 // hairline, outset by (0.5f + fudge factor) in post-scaling space
170 bounds->outset(fabs(inverseScaleX) * (0.5f + Vertex::GeometryFudgeFactor()),
171 fabs(inverseScaleY) * (0.5f + Vertex::GeometryFudgeFactor()));
172 } else {
173 // non hairline, outset by half stroke width pre-scaled, and fudge factor post scaled
174 bounds->outset(halfStrokeWidth + fabs(inverseScaleX) * Vertex::GeometryFudgeFactor(),
175 halfStrokeWidth + fabs(inverseScaleY) * Vertex::GeometryFudgeFactor());
176 }
Chris Craikf0a59072013-11-19 18:00:46 -0800177 }
Chris Craik65cd6122012-12-10 17:56:27 -0800178};
179
John Reck272a6852015-07-29 16:48:58 -0700180void getFillVerticesFromPerimeter(const std::vector<Vertex>& perimeter,
181 VertexBuffer& vertexBuffer) {
Chris Craik65cd6122012-12-10 17:56:27 -0800182 Vertex* buffer = vertexBuffer.alloc<Vertex>(perimeter.size());
183
184 int currentIndex = 0;
185 // zig zag between all previous points on the inside of the hull to create a
186 // triangle strip that fills the hull
187 int srcAindex = 0;
188 int srcBindex = perimeter.size() - 1;
189 while (srcAindex <= srcBindex) {
Chris Craik11a75672013-12-16 17:08:15 -0800190 buffer[currentIndex++] = perimeter[srcAindex];
Chris Craik65cd6122012-12-10 17:56:27 -0800191 if (srcAindex == srcBindex) break;
Chris Craik11a75672013-12-16 17:08:15 -0800192 buffer[currentIndex++] = perimeter[srcBindex];
Chris Craik65cd6122012-12-10 17:56:27 -0800193 srcAindex++;
194 srcBindex--;
195 }
196}
197
198/*
199 * Fills a vertexBuffer with non-alpha vertices, zig-zagging at each perimeter point to create a
200 * tri-strip as wide as the stroke.
201 *
202 * Uses an additional 2 vertices at the end to wrap around, closing the tri-strip
203 * (for a total of perimeter.size() * 2 + 2 vertices)
204 */
John Reck272a6852015-07-29 16:48:58 -0700205void getStrokeVerticesFromPerimeter(const PaintInfo& paintInfo,
206 const std::vector<Vertex>& perimeter, VertexBuffer& vertexBuffer) {
Chris Craik65cd6122012-12-10 17:56:27 -0800207 Vertex* buffer = vertexBuffer.alloc<Vertex>(perimeter.size() * 2 + 2);
208
209 int currentIndex = 0;
210 const Vertex* last = &(perimeter[perimeter.size() - 1]);
211 const Vertex* current = &(perimeter[0]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700212 Vector2 lastNormal = {current->y - last->y, last->x - current->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800213 lastNormal.normalize();
214 for (unsigned int i = 0; i < perimeter.size(); i++) {
215 const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700216 Vector2 nextNormal = {next->y - current->y, current->x - next->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800217 nextNormal.normalize();
218
John Reck1aa5d2d2014-07-24 13:38:28 -0700219 Vector2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
Chris Craik65cd6122012-12-10 17:56:27 -0800220 paintInfo.scaleOffsetForStrokeWidth(totalOffset);
221
222 Vertex::set(&buffer[currentIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700223 current->x + totalOffset.x,
224 current->y + totalOffset.y);
Chris Craik65cd6122012-12-10 17:56:27 -0800225
226 Vertex::set(&buffer[currentIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700227 current->x - totalOffset.x,
228 current->y - totalOffset.y);
Chris Craik65cd6122012-12-10 17:56:27 -0800229
Chris Craik65cd6122012-12-10 17:56:27 -0800230 current = next;
231 lastNormal = nextNormal;
232 }
233
234 // wrap around to beginning
Chris Craik11a75672013-12-16 17:08:15 -0800235 buffer[currentIndex++] = buffer[0];
236 buffer[currentIndex++] = buffer[1];
Chris Craik65cd6122012-12-10 17:56:27 -0800237
238 DEBUG_DUMP_BUFFER();
239}
240
Chris Craik6d29c8d2013-05-08 18:35:44 -0700241static inline void storeBeginEnd(const PaintInfo& paintInfo, const Vertex& center,
John Reck1aa5d2d2014-07-24 13:38:28 -0700242 const Vector2& normal, Vertex* buffer, int& currentIndex, bool begin) {
243 Vector2 strokeOffset = normal;
Chris Craik6d29c8d2013-05-08 18:35:44 -0700244 paintInfo.scaleOffsetForStrokeWidth(strokeOffset);
245
John Reck1aa5d2d2014-07-24 13:38:28 -0700246 Vector2 referencePoint = {center.x, center.y};
Chris Craik6d29c8d2013-05-08 18:35:44 -0700247 if (paintInfo.cap == SkPaint::kSquare_Cap) {
John Reck1aa5d2d2014-07-24 13:38:28 -0700248 Vector2 rotated = {-strokeOffset.y, strokeOffset.x};
249 referencePoint += rotated * (begin ? -1 : 1);
Chris Craik6d29c8d2013-05-08 18:35:44 -0700250 }
251
252 Vertex::set(&buffer[currentIndex++], referencePoint + strokeOffset);
253 Vertex::set(&buffer[currentIndex++], referencePoint - strokeOffset);
254}
255
Chris Craik65cd6122012-12-10 17:56:27 -0800256/**
257 * Fills a vertexBuffer with non-alpha vertices similar to getStrokeVerticesFromPerimeter, except:
258 *
259 * 1 - Doesn't need to wrap around, since the input vertices are unclosed
260 *
261 * 2 - can zig-zag across 'extra' vertices at either end, to create round caps
262 */
263void getStrokeVerticesFromUnclosedVertices(const PaintInfo& paintInfo,
John Reck272a6852015-07-29 16:48:58 -0700264 const std::vector<Vertex>& vertices, VertexBuffer& vertexBuffer) {
Chris Craik65cd6122012-12-10 17:56:27 -0800265 const int extra = paintInfo.capExtraDivisions();
266 const int allocSize = (vertices.size() + extra) * 2;
Chris Craik65cd6122012-12-10 17:56:27 -0800267 Vertex* buffer = vertexBuffer.alloc<Vertex>(allocSize);
268
Chris Craik6d29c8d2013-05-08 18:35:44 -0700269 const int lastIndex = vertices.size() - 1;
Chris Craik65cd6122012-12-10 17:56:27 -0800270 if (extra > 0) {
271 // tessellate both round caps
Chris Craik65cd6122012-12-10 17:56:27 -0800272 float beginTheta = atan2(
Romain Guy3380cfd2013-08-15 16:57:57 -0700273 - (vertices[0].x - vertices[1].x),
274 vertices[0].y - vertices[1].y);
Chris Craik65cd6122012-12-10 17:56:27 -0800275 float endTheta = atan2(
Romain Guy3380cfd2013-08-15 16:57:57 -0700276 - (vertices[lastIndex].x - vertices[lastIndex - 1].x),
277 vertices[lastIndex].y - vertices[lastIndex - 1].y);
Chris Craik65cd6122012-12-10 17:56:27 -0800278 const float dTheta = PI / (extra + 1);
Chris Craik65cd6122012-12-10 17:56:27 -0800279
280 int capOffset;
281 for (int i = 0; i < extra; i++) {
282 if (i < extra / 2) {
283 capOffset = extra - 2 * i - 1;
284 } else {
285 capOffset = 2 * i - extra;
286 }
287
288 beginTheta += dTheta;
Dan Albertff1d8a62014-11-11 19:31:26 -0800289 Vector2 beginRadialOffset = {cosf(beginTheta), sinf(beginTheta)};
Chris Craik65cd6122012-12-10 17:56:27 -0800290 paintInfo.scaleOffsetForStrokeWidth(beginRadialOffset);
291 Vertex::set(&buffer[capOffset],
Romain Guy3380cfd2013-08-15 16:57:57 -0700292 vertices[0].x + beginRadialOffset.x,
293 vertices[0].y + beginRadialOffset.y);
Chris Craik65cd6122012-12-10 17:56:27 -0800294
295 endTheta += dTheta;
Dan Albertff1d8a62014-11-11 19:31:26 -0800296 Vector2 endRadialOffset = {cosf(endTheta), sinf(endTheta)};
Chris Craik65cd6122012-12-10 17:56:27 -0800297 paintInfo.scaleOffsetForStrokeWidth(endRadialOffset);
298 Vertex::set(&buffer[allocSize - 1 - capOffset],
Romain Guy3380cfd2013-08-15 16:57:57 -0700299 vertices[lastIndex].x + endRadialOffset.x,
300 vertices[lastIndex].y + endRadialOffset.y);
Chris Craik65cd6122012-12-10 17:56:27 -0800301 }
302 }
303
304 int currentIndex = extra;
Chris Craik6d29c8d2013-05-08 18:35:44 -0700305 const Vertex* last = &(vertices[0]);
306 const Vertex* current = &(vertices[1]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700307 Vector2 lastNormal = {current->y - last->y, last->x - current->x};
Chris Craik6d29c8d2013-05-08 18:35:44 -0700308 lastNormal.normalize();
309
310 storeBeginEnd(paintInfo, vertices[0], lastNormal, buffer, currentIndex, true);
311
312 for (unsigned int i = 1; i < vertices.size() - 1; i++) {
Chris Craik65cd6122012-12-10 17:56:27 -0800313 const Vertex* next = &(vertices[i + 1]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700314 Vector2 nextNormal = {next->y - current->y, current->x - next->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800315 nextNormal.normalize();
316
John Reck1aa5d2d2014-07-24 13:38:28 -0700317 Vector2 strokeOffset = totalOffsetFromNormals(lastNormal, nextNormal);
Chris Craik6d29c8d2013-05-08 18:35:44 -0700318 paintInfo.scaleOffsetForStrokeWidth(strokeOffset);
Chris Craik65cd6122012-12-10 17:56:27 -0800319
John Reck1aa5d2d2014-07-24 13:38:28 -0700320 Vector2 center = {current->x, current->y};
Chris Craik6d29c8d2013-05-08 18:35:44 -0700321 Vertex::set(&buffer[currentIndex++], center + strokeOffset);
322 Vertex::set(&buffer[currentIndex++], center - strokeOffset);
Chris Craik65cd6122012-12-10 17:56:27 -0800323
324 current = next;
325 lastNormal = nextNormal;
326 }
327
Chris Craik6d29c8d2013-05-08 18:35:44 -0700328 storeBeginEnd(paintInfo, vertices[lastIndex], lastNormal, buffer, currentIndex, false);
Chris Craik65cd6122012-12-10 17:56:27 -0800329
330 DEBUG_DUMP_BUFFER();
331}
332
333/**
334 * Populates a vertexBuffer with AlphaVertices to create an anti-aliased fill shape tessellation
Chris Craik6d29c8d2013-05-08 18:35:44 -0700335 *
Chris Craik65cd6122012-12-10 17:56:27 -0800336 * 1 - create the AA perimeter of unit width, by zig-zagging at each point around the perimeter of
337 * the shape (using 2 * perimeter.size() vertices)
338 *
339 * 2 - wrap around to the beginning to complete the perimeter (2 vertices)
340 *
341 * 3 - zig zag back and forth inside the shape to fill it (using perimeter.size() vertices)
342 */
John Reck272a6852015-07-29 16:48:58 -0700343void getFillVerticesFromPerimeterAA(const PaintInfo& paintInfo,
344 const std::vector<Vertex>& perimeter, VertexBuffer& vertexBuffer,
345 float maxAlpha = 1.0f) {
Chris Craik65cd6122012-12-10 17:56:27 -0800346 AlphaVertex* buffer = vertexBuffer.alloc<AlphaVertex>(perimeter.size() * 3 + 2);
347
348 // generate alpha points - fill Alpha vertex gaps in between each point with
349 // alpha 0 vertex, offset by a scaled normal.
350 int currentIndex = 0;
351 const Vertex* last = &(perimeter[perimeter.size() - 1]);
352 const Vertex* current = &(perimeter[0]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700353 Vector2 lastNormal = {current->y - last->y, last->x - current->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800354 lastNormal.normalize();
355 for (unsigned int i = 0; i < perimeter.size(); i++) {
356 const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700357 Vector2 nextNormal = {next->y - current->y, current->x - next->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800358 nextNormal.normalize();
359
360 // AA point offset from original point is that point's normal, such that each side is offset
361 // by .5 pixels
John Reck1aa5d2d2014-07-24 13:38:28 -0700362 Vector2 totalOffset = paintInfo.deriveAAOffset(totalOffsetFromNormals(lastNormal, nextNormal));
Chris Craik65cd6122012-12-10 17:56:27 -0800363
364 AlphaVertex::set(&buffer[currentIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700365 current->x + totalOffset.x,
366 current->y + totalOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800367 0.0f);
368 AlphaVertex::set(&buffer[currentIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700369 current->x - totalOffset.x,
370 current->y - totalOffset.y,
Chris Craikf0a59072013-11-19 18:00:46 -0800371 maxAlpha);
Chris Craik65cd6122012-12-10 17:56:27 -0800372
Chris Craik65cd6122012-12-10 17:56:27 -0800373 current = next;
374 lastNormal = nextNormal;
375 }
376
377 // wrap around to beginning
Chris Craik11a75672013-12-16 17:08:15 -0800378 buffer[currentIndex++] = buffer[0];
379 buffer[currentIndex++] = buffer[1];
Chris Craik65cd6122012-12-10 17:56:27 -0800380
381 // zig zag between all previous points on the inside of the hull to create a
382 // triangle strip that fills the hull, repeating the first inner point to
383 // create degenerate tris to start inside path
384 int srcAindex = 0;
385 int srcBindex = perimeter.size() - 1;
386 while (srcAindex <= srcBindex) {
Chris Craik11a75672013-12-16 17:08:15 -0800387 buffer[currentIndex++] = buffer[srcAindex * 2 + 1];
Chris Craik65cd6122012-12-10 17:56:27 -0800388 if (srcAindex == srcBindex) break;
Chris Craik11a75672013-12-16 17:08:15 -0800389 buffer[currentIndex++] = buffer[srcBindex * 2 + 1];
Chris Craik65cd6122012-12-10 17:56:27 -0800390 srcAindex++;
391 srcBindex--;
392 }
393
394 DEBUG_DUMP_BUFFER();
395}
396
397/**
398 * Stores geometry for a single, AA-perimeter (potentially rounded) cap
399 *
400 * For explanation of constants and general methodoloyg, see comments for
401 * getStrokeVerticesFromUnclosedVerticesAA() below.
402 */
John Reck272a6852015-07-29 16:48:58 -0700403inline static void storeCapAA(const PaintInfo& paintInfo, const std::vector<Vertex>& vertices,
John Reck1aa5d2d2014-07-24 13:38:28 -0700404 AlphaVertex* buffer, bool isFirst, Vector2 normal, int offset) {
Chris Craik65cd6122012-12-10 17:56:27 -0800405 const int extra = paintInfo.capExtraDivisions();
406 const int extraOffset = (extra + 1) / 2;
407 const int capIndex = isFirst
408 ? 2 * offset + 6 + 2 * (extra + extraOffset)
409 : offset + 2 + 2 * extraOffset;
410 if (isFirst) normal *= -1;
411
412 // TODO: this normal should be scaled by radialScale if extra != 0, see totalOffsetFromNormals()
John Reck1aa5d2d2014-07-24 13:38:28 -0700413 Vector2 AAOffset = paintInfo.deriveAAOffset(normal);
Chris Craik65cd6122012-12-10 17:56:27 -0800414
John Reck1aa5d2d2014-07-24 13:38:28 -0700415 Vector2 strokeOffset = normal;
Chris Craik65cd6122012-12-10 17:56:27 -0800416 paintInfo.scaleOffsetForStrokeWidth(strokeOffset);
John Reck1aa5d2d2014-07-24 13:38:28 -0700417 Vector2 outerOffset = strokeOffset + AAOffset;
418 Vector2 innerOffset = strokeOffset - AAOffset;
Chris Craik65cd6122012-12-10 17:56:27 -0800419
John Reck1aa5d2d2014-07-24 13:38:28 -0700420 Vector2 capAAOffset = {0, 0};
Chris Craik65cd6122012-12-10 17:56:27 -0800421 if (paintInfo.cap != SkPaint::kRound_Cap) {
422 // if the cap is square or butt, the inside primary cap vertices will be inset in two
423 // directions - both normal to the stroke, and parallel to it.
John Reck1aa5d2d2014-07-24 13:38:28 -0700424 capAAOffset = (Vector2){-AAOffset.y, AAOffset.x};
Chris Craik65cd6122012-12-10 17:56:27 -0800425 }
426
427 // determine referencePoint, the center point for the 4 primary cap vertices
John Reck272a6852015-07-29 16:48:58 -0700428 const Vertex& point = isFirst ? vertices.front() : vertices.back();
429 Vector2 referencePoint = {point.x, point.y};
Chris Craik65cd6122012-12-10 17:56:27 -0800430 if (paintInfo.cap == SkPaint::kSquare_Cap) {
431 // To account for square cap, move the primary cap vertices (that create the AA edge) by the
432 // stroke offset vector (rotated to be parallel to the stroke)
John Reck1aa5d2d2014-07-24 13:38:28 -0700433 Vector2 rotated = {-strokeOffset.y, strokeOffset.x};
434 referencePoint += rotated;
Chris Craik65cd6122012-12-10 17:56:27 -0800435 }
436
437 AlphaVertex::set(&buffer[capIndex + 0],
438 referencePoint.x + outerOffset.x + capAAOffset.x,
439 referencePoint.y + outerOffset.y + capAAOffset.y,
440 0.0f);
441 AlphaVertex::set(&buffer[capIndex + 1],
442 referencePoint.x + innerOffset.x - capAAOffset.x,
443 referencePoint.y + innerOffset.y - capAAOffset.y,
444 paintInfo.maxAlpha);
445
446 bool isRound = paintInfo.cap == SkPaint::kRound_Cap;
447
448 const int postCapIndex = (isRound && isFirst) ? (2 * extraOffset - 2) : capIndex + (2 * extra);
449 AlphaVertex::set(&buffer[postCapIndex + 2],
450 referencePoint.x - outerOffset.x + capAAOffset.x,
451 referencePoint.y - outerOffset.y + capAAOffset.y,
452 0.0f);
453 AlphaVertex::set(&buffer[postCapIndex + 3],
454 referencePoint.x - innerOffset.x - capAAOffset.x,
455 referencePoint.y - innerOffset.y - capAAOffset.y,
456 paintInfo.maxAlpha);
457
458 if (isRound) {
459 const float dTheta = PI / (extra + 1);
460 const float radialScale = 2.0f / (1 + cos(dTheta));
461 float theta = atan2(normal.y, normal.x);
462 int capPerimIndex = capIndex + 2;
463
464 for (int i = 0; i < extra; i++) {
465 theta += dTheta;
466
Dan Albertff1d8a62014-11-11 19:31:26 -0800467 Vector2 radialOffset = {cosf(theta), sinf(theta)};
Chris Craik65cd6122012-12-10 17:56:27 -0800468
469 // scale to compensate for pinching at sharp angles, see totalOffsetFromNormals()
470 radialOffset *= radialScale;
471
472 AAOffset = paintInfo.deriveAAOffset(radialOffset);
473 paintInfo.scaleOffsetForStrokeWidth(radialOffset);
474 AlphaVertex::set(&buffer[capPerimIndex++],
475 referencePoint.x + radialOffset.x + AAOffset.x,
476 referencePoint.y + radialOffset.y + AAOffset.y,
477 0.0f);
478 AlphaVertex::set(&buffer[capPerimIndex++],
479 referencePoint.x + radialOffset.x - AAOffset.x,
480 referencePoint.y + radialOffset.y - AAOffset.y,
481 paintInfo.maxAlpha);
482
483 if (isFirst && i == extra - extraOffset) {
484 //copy most recent two points to first two points
Chris Craik11a75672013-12-16 17:08:15 -0800485 buffer[0] = buffer[capPerimIndex - 2];
486 buffer[1] = buffer[capPerimIndex - 1];
Chris Craik65cd6122012-12-10 17:56:27 -0800487
488 capPerimIndex = 2; // start writing the rest of the round cap at index 2
489 }
490 }
491
492 if (isFirst) {
493 const int startCapFillIndex = capIndex + 2 * (extra - extraOffset) + 4;
494 int capFillIndex = startCapFillIndex;
495 for (int i = 0; i < extra + 2; i += 2) {
Chris Craik11a75672013-12-16 17:08:15 -0800496 buffer[capFillIndex++] = buffer[1 + i];
Chris Craik65cd6122012-12-10 17:56:27 -0800497 // TODO: to support odd numbers of divisions, break here on the last iteration
Chris Craik11a75672013-12-16 17:08:15 -0800498 buffer[capFillIndex++] = buffer[startCapFillIndex - 3 - i];
Chris Craik65cd6122012-12-10 17:56:27 -0800499 }
500 } else {
501 int capFillIndex = 6 * vertices.size() + 2 + 6 * extra - (extra + 2);
502 for (int i = 0; i < extra + 2; i += 2) {
Chris Craik11a75672013-12-16 17:08:15 -0800503 buffer[capFillIndex++] = buffer[capIndex + 1 + i];
Chris Craik65cd6122012-12-10 17:56:27 -0800504 // TODO: to support odd numbers of divisions, break here on the last iteration
Chris Craik11a75672013-12-16 17:08:15 -0800505 buffer[capFillIndex++] = buffer[capIndex + 3 + 2 * extra - i];
Chris Craik65cd6122012-12-10 17:56:27 -0800506 }
507 }
508 return;
509 }
510 if (isFirst) {
Chris Craik11a75672013-12-16 17:08:15 -0800511 buffer[0] = buffer[postCapIndex + 2];
512 buffer[1] = buffer[postCapIndex + 3];
513 buffer[postCapIndex + 4] = buffer[1]; // degenerate tris (the only two!)
514 buffer[postCapIndex + 5] = buffer[postCapIndex + 1];
Chris Craik65cd6122012-12-10 17:56:27 -0800515 } else {
Chris Craik11a75672013-12-16 17:08:15 -0800516 buffer[6 * vertices.size()] = buffer[postCapIndex + 1];
517 buffer[6 * vertices.size() + 1] = buffer[postCapIndex + 3];
Chris Craik65cd6122012-12-10 17:56:27 -0800518 }
519}
520
521/*
522the geometry for an aa, capped stroke consists of the following:
523
524 # vertices | function
525----------------------------------------------------------------------
526a) 2 | Start AA perimeter
527b) 2, 2 * roundDivOff | First half of begin cap's perimeter
528 |
529 2 * middlePts | 'Outer' or 'Top' AA perimeter half (between caps)
530 |
531a) 4 | End cap's
532b) 2, 2 * roundDivs, 2 | AA perimeter
533 |
534 2 * middlePts | 'Inner' or 'bottom' AA perimeter half
535 |
536a) 6 | Begin cap's perimeter
537b) 2, 2*(rD - rDO + 1), | Last half of begin cap's perimeter
538 roundDivs, 2 |
539 |
540 2 * middlePts | Stroke's full opacity center strip
541 |
542a) 2 | end stroke
543b) 2, roundDivs | (and end cap fill, for round)
544
545Notes:
546* rows starting with 'a)' denote the Butt or Square cap vertex use, 'b)' denote Round
547
548* 'middlePts' is (number of points in the unclosed input vertex list, minus 2) times two
549
550* 'roundDivs' or 'rD' is the number of extra vertices (beyond the minimum of 2) that define the
551 round cap's shape, and is at least two. This will increase with cap size to sufficiently
552 define the cap's level of tessellation.
553
554* 'roundDivOffset' or 'rDO' is the point about halfway along the start cap's round perimeter, where
555 the stream of vertices for the AA perimeter starts. By starting and ending the perimeter at
556 this offset, the fill of the stroke is drawn from this point with minimal extra vertices.
557
558This means the outer perimeter starts at:
559 outerIndex = (2) OR (2 + 2 * roundDivOff)
560the inner perimeter (since it is filled in reverse) starts at:
561 innerIndex = outerIndex + (4 * middlePts) + ((4) OR (4 + 2 * roundDivs)) - 1
562the stroke starts at:
563 strokeIndex = innerIndex + 1 + ((6) OR (6 + 3 * roundDivs - 2 * roundDivOffset))
564
565The total needed allocated space is either:
566 2 + 4 + 6 + 2 + 3 * (2 * middlePts) = 14 + 6 * middlePts = 2 + 6 * pts
567or, for rounded caps:
568 (2 + 2 * rDO) + (4 + 2 * rD) + (2 * (rD - rDO + 1)
569 + roundDivs + 4) + (2 + roundDivs) + 3 * (2 * middlePts)
570 = 14 + 6 * middlePts + 6 * roundDivs
571 = 2 + 6 * pts + 6 * roundDivs
572 */
573void getStrokeVerticesFromUnclosedVerticesAA(const PaintInfo& paintInfo,
John Reck272a6852015-07-29 16:48:58 -0700574 const std::vector<Vertex>& vertices, VertexBuffer& vertexBuffer) {
Chris Craik65cd6122012-12-10 17:56:27 -0800575
576 const int extra = paintInfo.capExtraDivisions();
577 const int allocSize = 6 * vertices.size() + 2 + 6 * extra;
578
579 AlphaVertex* buffer = vertexBuffer.alloc<AlphaVertex>(allocSize);
580
581 const int extraOffset = (extra + 1) / 2;
582 int offset = 2 * (vertices.size() - 2);
583 // there is no outer/inner here, using them for consistency with below approach
584 int currentAAOuterIndex = 2 + 2 * extraOffset;
585 int currentAAInnerIndex = currentAAOuterIndex + (2 * offset) + 3 + (2 * extra);
586 int currentStrokeIndex = currentAAInnerIndex + 7 + (3 * extra - 2 * extraOffset);
587
588 const Vertex* last = &(vertices[0]);
589 const Vertex* current = &(vertices[1]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700590 Vector2 lastNormal = {current->y - last->y, last->x - current->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800591 lastNormal.normalize();
592
593 // TODO: use normal from bezier traversal for cap, instead of from vertices
594 storeCapAA(paintInfo, vertices, buffer, true, lastNormal, offset);
595
596 for (unsigned int i = 1; i < vertices.size() - 1; i++) {
597 const Vertex* next = &(vertices[i + 1]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700598 Vector2 nextNormal = {next->y - current->y, current->x - next->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800599 nextNormal.normalize();
600
John Reck1aa5d2d2014-07-24 13:38:28 -0700601 Vector2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
602 Vector2 AAOffset = paintInfo.deriveAAOffset(totalOffset);
Chris Craik65cd6122012-12-10 17:56:27 -0800603
John Reck1aa5d2d2014-07-24 13:38:28 -0700604 Vector2 innerOffset = totalOffset;
Chris Craik65cd6122012-12-10 17:56:27 -0800605 paintInfo.scaleOffsetForStrokeWidth(innerOffset);
John Reck1aa5d2d2014-07-24 13:38:28 -0700606 Vector2 outerOffset = innerOffset + AAOffset;
Chris Craik65cd6122012-12-10 17:56:27 -0800607 innerOffset -= AAOffset;
608
609 AlphaVertex::set(&buffer[currentAAOuterIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700610 current->x + outerOffset.x,
611 current->y + outerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800612 0.0f);
613 AlphaVertex::set(&buffer[currentAAOuterIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700614 current->x + innerOffset.x,
615 current->y + innerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800616 paintInfo.maxAlpha);
617
618 AlphaVertex::set(&buffer[currentStrokeIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700619 current->x + innerOffset.x,
620 current->y + innerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800621 paintInfo.maxAlpha);
622 AlphaVertex::set(&buffer[currentStrokeIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700623 current->x - innerOffset.x,
624 current->y - innerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800625 paintInfo.maxAlpha);
626
627 AlphaVertex::set(&buffer[currentAAInnerIndex--],
Romain Guy3380cfd2013-08-15 16:57:57 -0700628 current->x - innerOffset.x,
629 current->y - innerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800630 paintInfo.maxAlpha);
631 AlphaVertex::set(&buffer[currentAAInnerIndex--],
Romain Guy3380cfd2013-08-15 16:57:57 -0700632 current->x - outerOffset.x,
633 current->y - outerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800634 0.0f);
635
636 current = next;
637 lastNormal = nextNormal;
638 }
639
640 // TODO: use normal from bezier traversal for cap, instead of from vertices
641 storeCapAA(paintInfo, vertices, buffer, false, lastNormal, offset);
642
643 DEBUG_DUMP_ALPHA_BUFFER();
644}
645
646
John Reck272a6852015-07-29 16:48:58 -0700647void getStrokeVerticesFromPerimeterAA(const PaintInfo& paintInfo,
648 const std::vector<Vertex>& perimeter, VertexBuffer& vertexBuffer) {
Chris Craik65cd6122012-12-10 17:56:27 -0800649 AlphaVertex* buffer = vertexBuffer.alloc<AlphaVertex>(6 * perimeter.size() + 8);
650
651 int offset = 2 * perimeter.size() + 3;
652 int currentAAOuterIndex = 0;
653 int currentStrokeIndex = offset;
654 int currentAAInnerIndex = offset * 2;
655
656 const Vertex* last = &(perimeter[perimeter.size() - 1]);
657 const Vertex* current = &(perimeter[0]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700658 Vector2 lastNormal = {current->y - last->y, last->x - current->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800659 lastNormal.normalize();
660 for (unsigned int i = 0; i < perimeter.size(); i++) {
661 const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700662 Vector2 nextNormal = {next->y - current->y, current->x - next->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800663 nextNormal.normalize();
664
John Reck1aa5d2d2014-07-24 13:38:28 -0700665 Vector2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
666 Vector2 AAOffset = paintInfo.deriveAAOffset(totalOffset);
Chris Craik65cd6122012-12-10 17:56:27 -0800667
John Reck1aa5d2d2014-07-24 13:38:28 -0700668 Vector2 innerOffset = totalOffset;
Chris Craik65cd6122012-12-10 17:56:27 -0800669 paintInfo.scaleOffsetForStrokeWidth(innerOffset);
John Reck1aa5d2d2014-07-24 13:38:28 -0700670 Vector2 outerOffset = innerOffset + AAOffset;
Chris Craik65cd6122012-12-10 17:56:27 -0800671 innerOffset -= AAOffset;
672
673 AlphaVertex::set(&buffer[currentAAOuterIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700674 current->x + outerOffset.x,
675 current->y + outerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800676 0.0f);
677 AlphaVertex::set(&buffer[currentAAOuterIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700678 current->x + innerOffset.x,
679 current->y + innerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800680 paintInfo.maxAlpha);
681
682 AlphaVertex::set(&buffer[currentStrokeIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700683 current->x + innerOffset.x,
684 current->y + innerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800685 paintInfo.maxAlpha);
686 AlphaVertex::set(&buffer[currentStrokeIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700687 current->x - innerOffset.x,
688 current->y - innerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800689 paintInfo.maxAlpha);
690
691 AlphaVertex::set(&buffer[currentAAInnerIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700692 current->x - innerOffset.x,
693 current->y - innerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800694 paintInfo.maxAlpha);
695 AlphaVertex::set(&buffer[currentAAInnerIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700696 current->x - outerOffset.x,
697 current->y - outerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800698 0.0f);
699
Chris Craik65cd6122012-12-10 17:56:27 -0800700 current = next;
701 lastNormal = nextNormal;
702 }
703
704 // wrap each strip around to beginning, creating degenerate tris to bridge strips
Chris Craik11a75672013-12-16 17:08:15 -0800705 buffer[currentAAOuterIndex++] = buffer[0];
706 buffer[currentAAOuterIndex++] = buffer[1];
707 buffer[currentAAOuterIndex++] = buffer[1];
Chris Craik65cd6122012-12-10 17:56:27 -0800708
Chris Craik11a75672013-12-16 17:08:15 -0800709 buffer[currentStrokeIndex++] = buffer[offset];
710 buffer[currentStrokeIndex++] = buffer[offset + 1];
711 buffer[currentStrokeIndex++] = buffer[offset + 1];
Chris Craik65cd6122012-12-10 17:56:27 -0800712
Chris Craik11a75672013-12-16 17:08:15 -0800713 buffer[currentAAInnerIndex++] = buffer[2 * offset];
714 buffer[currentAAInnerIndex++] = buffer[2 * offset + 1];
Chris Craik65cd6122012-12-10 17:56:27 -0800715 // don't need to create last degenerate tri
716
717 DEBUG_DUMP_ALPHA_BUFFER();
718}
719
720void PathTessellator::tessellatePath(const SkPath &path, const SkPaint* paint,
Chris Craikd6b65f62014-01-01 14:45:21 -0800721 const mat4& transform, VertexBuffer& vertexBuffer) {
Chris Craik65cd6122012-12-10 17:56:27 -0800722 ATRACE_CALL();
723
724 const PaintInfo paintInfo(paint, transform);
725
John Reck272a6852015-07-29 16:48:58 -0700726 std::vector<Vertex> tempVertices;
Chris Craik65cd6122012-12-10 17:56:27 -0800727 float threshInvScaleX = paintInfo.inverseScaleX;
728 float threshInvScaleY = paintInfo.inverseScaleY;
729 if (paintInfo.style == SkPaint::kStroke_Style) {
730 // alter the bezier recursion threshold values we calculate in order to compensate for
731 // expansion done after the path vertices are found
732 SkRect bounds = path.getBounds();
733 if (!bounds.isEmpty()) {
734 threshInvScaleX *= bounds.width() / (bounds.width() + paint->getStrokeWidth());
735 threshInvScaleY *= bounds.height() / (bounds.height() + paint->getStrokeWidth());
736 }
737 }
738
739 // force close if we're filling the path, since fill path expects closed perimeter.
740 bool forceClose = paintInfo.style != SkPaint::kStroke_Style;
ztenghui21ef8202015-05-28 16:06:28 -0700741 PathApproximationInfo approximationInfo(threshInvScaleX, threshInvScaleY,
742 OUTLINE_REFINE_THRESHOLD);
Chris Craik65cd6122012-12-10 17:56:27 -0800743 bool wasClosed = approximatePathOutlineVertices(path, forceClose,
ztenghui21ef8202015-05-28 16:06:28 -0700744 approximationInfo, tempVertices);
Chris Craik65cd6122012-12-10 17:56:27 -0800745
746 if (!tempVertices.size()) {
747 // path was empty, return without allocating vertex buffer
748 return;
749 }
750
751#if VERTEX_DEBUG
752 for (unsigned int i = 0; i < tempVertices.size(); i++) {
753 ALOGD("orig path: point at %f %f",
Romain Guy3380cfd2013-08-15 16:57:57 -0700754 tempVertices[i].x, tempVertices[i].y);
Chris Craik65cd6122012-12-10 17:56:27 -0800755 }
756#endif
757
758 if (paintInfo.style == SkPaint::kStroke_Style) {
759 if (!paintInfo.isAA) {
760 if (wasClosed) {
761 getStrokeVerticesFromPerimeter(paintInfo, tempVertices, vertexBuffer);
762 } else {
763 getStrokeVerticesFromUnclosedVertices(paintInfo, tempVertices, vertexBuffer);
764 }
765
766 } else {
767 if (wasClosed) {
768 getStrokeVerticesFromPerimeterAA(paintInfo, tempVertices, vertexBuffer);
769 } else {
770 getStrokeVerticesFromUnclosedVerticesAA(paintInfo, tempVertices, vertexBuffer);
771 }
772 }
773 } else {
774 // For kStrokeAndFill style, the path should be adjusted externally.
775 // It will be treated as a fill here.
776 if (!paintInfo.isAA) {
777 getFillVerticesFromPerimeter(tempVertices, vertexBuffer);
778 } else {
779 getFillVerticesFromPerimeterAA(paintInfo, tempVertices, vertexBuffer);
780 }
781 }
Chris Craik05f3d6e2014-06-02 16:27:04 -0700782
783 Rect bounds(path.getBounds());
784 paintInfo.expandBoundsForStroke(&bounds);
785 vertexBuffer.setBounds(bounds);
Chris Craik117bdbc2015-02-05 10:12:38 -0800786 vertexBuffer.setMeshFeatureFlags(paintInfo.isAA ? VertexBuffer::kAlpha : VertexBuffer::kNone);
Chris Craik65cd6122012-12-10 17:56:27 -0800787}
788
Chris Craik6d29c8d2013-05-08 18:35:44 -0700789template <class TYPE>
790static void instanceVertices(VertexBuffer& srcBuffer, VertexBuffer& dstBuffer,
Chris Craik05f3d6e2014-06-02 16:27:04 -0700791 const float* points, int count, Rect& bounds) {
Chris Craik6d29c8d2013-05-08 18:35:44 -0700792 bounds.set(points[0], points[1], points[0], points[1]);
793
794 int numPoints = count / 2;
795 int verticesPerPoint = srcBuffer.getVertexCount();
796 dstBuffer.alloc<TYPE>(numPoints * verticesPerPoint + (numPoints - 1) * 2);
797
798 for (int i = 0; i < count; i += 2) {
Chris Craikc93e45c2014-07-16 10:15:56 -0700799 bounds.expandToCoverVertex(points[i + 0], points[i + 1]);
Chris Craik6d29c8d2013-05-08 18:35:44 -0700800 dstBuffer.copyInto<TYPE>(srcBuffer, points[i + 0], points[i + 1]);
801 }
802 dstBuffer.createDegenerateSeparators<TYPE>(verticesPerPoint);
803}
804
Chris Craikd218a922014-01-02 17:13:34 -0800805void PathTessellator::tessellatePoints(const float* points, int count, const SkPaint* paint,
Chris Craik05f3d6e2014-06-02 16:27:04 -0700806 const mat4& transform, VertexBuffer& vertexBuffer) {
Chris Craik6d29c8d2013-05-08 18:35:44 -0700807 const PaintInfo paintInfo(paint, transform);
808
809 // determine point shape
810 SkPath path;
811 float radius = paintInfo.halfStrokeWidth;
Rob Tsukb7c26562014-11-03 16:29:03 -0800812 if (radius == 0.0f) radius = 0.5f;
Chris Craik6d29c8d2013-05-08 18:35:44 -0700813
814 if (paintInfo.cap == SkPaint::kRound_Cap) {
815 path.addCircle(0, 0, radius);
816 } else {
817 path.addRect(-radius, -radius, radius, radius);
818 }
819
820 // calculate outline
John Reck272a6852015-07-29 16:48:58 -0700821 std::vector<Vertex> outlineVertices;
ztenghui21ef8202015-05-28 16:06:28 -0700822 PathApproximationInfo approximationInfo(paintInfo.inverseScaleX, paintInfo.inverseScaleY,
823 OUTLINE_REFINE_THRESHOLD);
824 approximatePathOutlineVertices(path, true, approximationInfo, outlineVertices);
Chris Craik6d29c8d2013-05-08 18:35:44 -0700825
826 if (!outlineVertices.size()) return;
827
Chris Craik05f3d6e2014-06-02 16:27:04 -0700828 Rect bounds;
Chris Craik6d29c8d2013-05-08 18:35:44 -0700829 // tessellate, then duplicate outline across points
Chris Craik6d29c8d2013-05-08 18:35:44 -0700830 VertexBuffer tempBuffer;
831 if (!paintInfo.isAA) {
832 getFillVerticesFromPerimeter(outlineVertices, tempBuffer);
833 instanceVertices<Vertex>(tempBuffer, vertexBuffer, points, count, bounds);
834 } else {
Chris Craikf0a59072013-11-19 18:00:46 -0800835 // note: pass maxAlpha directly, since we want fill to be alpha modulated
836 getFillVerticesFromPerimeterAA(paintInfo, outlineVertices, tempBuffer, paintInfo.maxAlpha);
Chris Craik6d29c8d2013-05-08 18:35:44 -0700837 instanceVertices<AlphaVertex>(tempBuffer, vertexBuffer, points, count, bounds);
838 }
839
Chris Craikf0a59072013-11-19 18:00:46 -0800840 // expand bounds from vertex coords to pixel data
Chris Craik05f3d6e2014-06-02 16:27:04 -0700841 paintInfo.expandBoundsForStroke(&bounds);
842 vertexBuffer.setBounds(bounds);
Chris Craik117bdbc2015-02-05 10:12:38 -0800843 vertexBuffer.setMeshFeatureFlags(paintInfo.isAA ? VertexBuffer::kAlpha : VertexBuffer::kNone);
Chris Craik65cd6122012-12-10 17:56:27 -0800844}
845
Chris Craikd218a922014-01-02 17:13:34 -0800846void PathTessellator::tessellateLines(const float* points, int count, const SkPaint* paint,
Chris Craik05f3d6e2014-06-02 16:27:04 -0700847 const mat4& transform, VertexBuffer& vertexBuffer) {
Chris Craik65cd6122012-12-10 17:56:27 -0800848 ATRACE_CALL();
849 const PaintInfo paintInfo(paint, transform);
850
851 const int extra = paintInfo.capExtraDivisions();
852 int numLines = count / 4;
853 int lineAllocSize;
854 // pre-allocate space for lines in the buffer, and degenerate tris in between
855 if (paintInfo.isAA) {
856 lineAllocSize = 6 * (2) + 2 + 6 * extra;
857 vertexBuffer.alloc<AlphaVertex>(numLines * lineAllocSize + (numLines - 1) * 2);
858 } else {
859 lineAllocSize = 2 * ((2) + extra);
860 vertexBuffer.alloc<Vertex>(numLines * lineAllocSize + (numLines - 1) * 2);
861 }
862
John Reck272a6852015-07-29 16:48:58 -0700863 std::vector<Vertex> tempVertices(2);
864 Vertex* tempVerticesData = &tempVertices.front();
Chris Craik05f3d6e2014-06-02 16:27:04 -0700865 Rect bounds;
Chris Craik65cd6122012-12-10 17:56:27 -0800866 bounds.set(points[0], points[1], points[0], points[1]);
867 for (int i = 0; i < count; i += 4) {
868 Vertex::set(&(tempVerticesData[0]), points[i + 0], points[i + 1]);
869 Vertex::set(&(tempVerticesData[1]), points[i + 2], points[i + 3]);
870
871 if (paintInfo.isAA) {
872 getStrokeVerticesFromUnclosedVerticesAA(paintInfo, tempVertices, vertexBuffer);
873 } else {
874 getStrokeVerticesFromUnclosedVertices(paintInfo, tempVertices, vertexBuffer);
875 }
876
877 // calculate bounds
Chris Craikc93e45c2014-07-16 10:15:56 -0700878 bounds.expandToCoverVertex(tempVerticesData[0].x, tempVerticesData[0].y);
879 bounds.expandToCoverVertex(tempVerticesData[1].x, tempVerticesData[1].y);
Chris Craik65cd6122012-12-10 17:56:27 -0800880 }
881
Chris Craik65cd6122012-12-10 17:56:27 -0800882 // since multiple objects tessellated into buffer, separate them with degen tris
883 if (paintInfo.isAA) {
884 vertexBuffer.createDegenerateSeparators<AlphaVertex>(lineAllocSize);
885 } else {
886 vertexBuffer.createDegenerateSeparators<Vertex>(lineAllocSize);
887 }
Chris Craikf0a59072013-11-19 18:00:46 -0800888
889 // expand bounds from vertex coords to pixel data
Chris Craik05f3d6e2014-06-02 16:27:04 -0700890 paintInfo.expandBoundsForStroke(&bounds);
891 vertexBuffer.setBounds(bounds);
Chris Craik117bdbc2015-02-05 10:12:38 -0800892 vertexBuffer.setMeshFeatureFlags(paintInfo.isAA ? VertexBuffer::kAlpha : VertexBuffer::kNone);
Chris Craik65cd6122012-12-10 17:56:27 -0800893}
894
895///////////////////////////////////////////////////////////////////////////////
896// Simple path line approximation
897///////////////////////////////////////////////////////////////////////////////
898
ztenghui21ef8202015-05-28 16:06:28 -0700899bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, float threshold,
John Reck272a6852015-07-29 16:48:58 -0700900 std::vector<Vertex>& outputVertices) {
ztenghui21ef8202015-05-28 16:06:28 -0700901 PathApproximationInfo approximationInfo(1.0f, 1.0f, threshold);
902 return approximatePathOutlineVertices(path, true, approximationInfo, outputVertices);
Chris Craik15a07a22014-01-26 13:43:53 -0800903}
904
Chris Craikfca52b752015-04-28 11:45:59 -0700905class ClockwiseEnforcer {
906public:
907 void addPoint(const SkPoint& point) {
908 double x = point.x();
909 double y = point.y();
910
911 if (initialized) {
912 sum += (x + lastX) * (y - lastY);
913 } else {
914 initialized = true;
915 }
916
917 lastX = x;
918 lastY = y;
919 }
John Reck272a6852015-07-29 16:48:58 -0700920 void reverseVectorIfNotClockwise(std::vector<Vertex>& vertices) {
Chris Craikfca52b752015-04-28 11:45:59 -0700921 if (sum < 0) {
922 // negative sum implies CounterClockwise
923 const int size = vertices.size();
924 for (int i = 0; i < size / 2; i++) {
925 Vertex tmp = vertices[i];
926 int k = size - 1 - i;
John Reck272a6852015-07-29 16:48:58 -0700927 vertices[i] = vertices[k];
928 vertices[k] = tmp;
Chris Craikfca52b752015-04-28 11:45:59 -0700929 }
930 }
931 }
932private:
933 bool initialized = false;
Chris Craik469fb1e2015-05-28 15:33:40 -0700934 double lastX = 0;
935 double lastY = 0;
Chris Craikfca52b752015-04-28 11:45:59 -0700936 double sum = 0;
937};
938
Chris Craik65cd6122012-12-10 17:56:27 -0800939bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool forceClose,
John Reck272a6852015-07-29 16:48:58 -0700940 const PathApproximationInfo& approximationInfo, std::vector<Vertex>& outputVertices) {
Chris Craik65cd6122012-12-10 17:56:27 -0800941 ATRACE_CALL();
942
943 // TODO: to support joins other than sharp miter, join vertices should be labelled in the
944 // perimeter, or resolved into more vertices. Reconsider forceClose-ing in that case.
945 SkPath::Iter iter(path, forceClose);
946 SkPoint pts[4];
947 SkPath::Verb v;
Chris Craikfca52b752015-04-28 11:45:59 -0700948 ClockwiseEnforcer clockwiseEnforcer;
Chris Craik65cd6122012-12-10 17:56:27 -0800949 while (SkPath::kDone_Verb != (v = iter.next(pts))) {
950 switch (v) {
951 case SkPath::kMove_Verb:
John Reck272a6852015-07-29 16:48:58 -0700952 outputVertices.push_back(Vertex{pts[0].x(), pts[0].y()});
Chris Craik65cd6122012-12-10 17:56:27 -0800953 ALOGV("Move to pos %f %f", pts[0].x(), pts[0].y());
Chris Craikfca52b752015-04-28 11:45:59 -0700954 clockwiseEnforcer.addPoint(pts[0]);
Chris Craik65cd6122012-12-10 17:56:27 -0800955 break;
956 case SkPath::kClose_Verb:
957 ALOGV("Close at pos %f %f", pts[0].x(), pts[0].y());
Chris Craikfca52b752015-04-28 11:45:59 -0700958 clockwiseEnforcer.addPoint(pts[0]);
Chris Craik65cd6122012-12-10 17:56:27 -0800959 break;
960 case SkPath::kLine_Verb:
961 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 -0700962 outputVertices.push_back(Vertex{pts[1].x(), pts[1].y()});
Chris Craikfca52b752015-04-28 11:45:59 -0700963 clockwiseEnforcer.addPoint(pts[1]);
Chris Craik65cd6122012-12-10 17:56:27 -0800964 break;
965 case SkPath::kQuad_Verb:
966 ALOGV("kQuad_Verb");
967 recursiveQuadraticBezierVertices(
968 pts[0].x(), pts[0].y(),
969 pts[2].x(), pts[2].y(),
970 pts[1].x(), pts[1].y(),
ztenghui21ef8202015-05-28 16:06:28 -0700971 approximationInfo, outputVertices);
Chris Craikfca52b752015-04-28 11:45:59 -0700972 clockwiseEnforcer.addPoint(pts[1]);
973 clockwiseEnforcer.addPoint(pts[2]);
Chris Craik65cd6122012-12-10 17:56:27 -0800974 break;
975 case SkPath::kCubic_Verb:
976 ALOGV("kCubic_Verb");
977 recursiveCubicBezierVertices(
978 pts[0].x(), pts[0].y(),
979 pts[1].x(), pts[1].y(),
980 pts[3].x(), pts[3].y(),
981 pts[2].x(), pts[2].y(),
ztenghui21ef8202015-05-28 16:06:28 -0700982 approximationInfo, outputVertices);
Chris Craikfca52b752015-04-28 11:45:59 -0700983 clockwiseEnforcer.addPoint(pts[1]);
984 clockwiseEnforcer.addPoint(pts[2]);
985 clockwiseEnforcer.addPoint(pts[3]);
Chris Craik65cd6122012-12-10 17:56:27 -0800986 break;
Derek Sollenbergerb0772a62015-03-16 14:35:55 -0400987 case SkPath::kConic_Verb: {
988 ALOGV("kConic_Verb");
989 SkAutoConicToQuads converter;
990 const SkPoint* quads = converter.computeQuads(pts, iter.conicWeight(),
ztenghui21ef8202015-05-28 16:06:28 -0700991 approximationInfo.thresholdForConicQuads);
Derek Sollenbergerb0772a62015-03-16 14:35:55 -0400992 for (int i = 0; i < converter.countQuads(); ++i) {
993 const int offset = 2 * i;
994 recursiveQuadraticBezierVertices(
995 quads[offset].x(), quads[offset].y(),
996 quads[offset+2].x(), quads[offset+2].y(),
997 quads[offset+1].x(), quads[offset+1].y(),
ztenghui21ef8202015-05-28 16:06:28 -0700998 approximationInfo, outputVertices);
Derek Sollenbergerb0772a62015-03-16 14:35:55 -0400999 }
Chris Craikfca52b752015-04-28 11:45:59 -07001000 clockwiseEnforcer.addPoint(pts[1]);
1001 clockwiseEnforcer.addPoint(pts[2]);
Derek Sollenbergerb0772a62015-03-16 14:35:55 -04001002 break;
1003 }
Chris Craik65cd6122012-12-10 17:56:27 -08001004 default:
1005 break;
1006 }
1007 }
1008
Chris Craikfca52b752015-04-28 11:45:59 -07001009 bool wasClosed = false;
Chris Craik65cd6122012-12-10 17:56:27 -08001010 int size = outputVertices.size();
Romain Guy3380cfd2013-08-15 16:57:57 -07001011 if (size >= 2 && outputVertices[0].x == outputVertices[size - 1].x &&
1012 outputVertices[0].y == outputVertices[size - 1].y) {
John Reck272a6852015-07-29 16:48:58 -07001013 outputVertices.pop_back();
Chris Craikfca52b752015-04-28 11:45:59 -07001014 wasClosed = true;
Chris Craik65cd6122012-12-10 17:56:27 -08001015 }
Chris Craikfca52b752015-04-28 11:45:59 -07001016
1017 // ensure output vector is clockwise
1018 clockwiseEnforcer.reverseVectorIfNotClockwise(outputVertices);
1019 return wasClosed;
Chris Craik65cd6122012-12-10 17:56:27 -08001020}
1021
1022///////////////////////////////////////////////////////////////////////////////
1023// Bezier approximation
ztenghui21ef8202015-05-28 16:06:28 -07001024//
1025// All the inputs and outputs here are in path coordinates.
1026// We convert the error threshold from screen coordinates into path coordinates.
Chris Craik65cd6122012-12-10 17:56:27 -08001027///////////////////////////////////////////////////////////////////////////////
1028
ztenghui21ef8202015-05-28 16:06:28 -07001029// Get a threshold in path coordinates, by scaling the thresholdSquared from screen coordinates.
1030// TODO: Document the math behind this algorithm.
1031static inline float getThreshold(const PathApproximationInfo& info, float dx, float dy) {
1032 // multiplying by sqrInvScaleY/X equivalent to multiplying in dimensional scale factors
1033 float scale = (dx * dx * info.sqrInvScaleY + dy * dy * info.sqrInvScaleX);
1034 return info.thresholdSquared * scale;
1035}
1036
Chris Craik65cd6122012-12-10 17:56:27 -08001037void PathTessellator::recursiveCubicBezierVertices(
1038 float p1x, float p1y, float c1x, float c1y,
1039 float p2x, float p2y, float c2x, float c2y,
ztenghui21ef8202015-05-28 16:06:28 -07001040 const PathApproximationInfo& approximationInfo,
John Reck272a6852015-07-29 16:48:58 -07001041 std::vector<Vertex>& outputVertices, int depth) {
Chris Craik65cd6122012-12-10 17:56:27 -08001042 float dx = p2x - p1x;
1043 float dy = p2y - p1y;
1044 float d1 = fabs((c1x - p2x) * dy - (c1y - p2y) * dx);
1045 float d2 = fabs((c2x - p2x) * dy - (c2y - p2y) * dx);
1046 float d = d1 + d2;
1047
Chris Craik74cf7e62014-08-07 14:34:46 -07001048 if (depth >= MAX_DEPTH
ztenghui21ef8202015-05-28 16:06:28 -07001049 || d * d <= getThreshold(approximationInfo, dx, dy)) {
Chris Craik65cd6122012-12-10 17:56:27 -08001050 // below thresh, draw line by adding endpoint
John Reck272a6852015-07-29 16:48:58 -07001051 outputVertices.push_back(Vertex{p2x, p2y});
Chris Craik65cd6122012-12-10 17:56:27 -08001052 } else {
1053 float p1c1x = (p1x + c1x) * 0.5f;
1054 float p1c1y = (p1y + c1y) * 0.5f;
1055 float p2c2x = (p2x + c2x) * 0.5f;
1056 float p2c2y = (p2y + c2y) * 0.5f;
1057
1058 float c1c2x = (c1x + c2x) * 0.5f;
1059 float c1c2y = (c1y + c2y) * 0.5f;
1060
1061 float p1c1c2x = (p1c1x + c1c2x) * 0.5f;
1062 float p1c1c2y = (p1c1y + c1c2y) * 0.5f;
1063
1064 float p2c1c2x = (p2c2x + c1c2x) * 0.5f;
1065 float p2c1c2y = (p2c2y + c1c2y) * 0.5f;
1066
1067 float mx = (p1c1c2x + p2c1c2x) * 0.5f;
1068 float my = (p1c1c2y + p2c1c2y) * 0.5f;
1069
1070 recursiveCubicBezierVertices(
1071 p1x, p1y, p1c1x, p1c1y,
1072 mx, my, p1c1c2x, p1c1c2y,
ztenghui21ef8202015-05-28 16:06:28 -07001073 approximationInfo, outputVertices, depth + 1);
Chris Craik65cd6122012-12-10 17:56:27 -08001074 recursiveCubicBezierVertices(
1075 mx, my, p2c1c2x, p2c1c2y,
1076 p2x, p2y, p2c2x, p2c2y,
ztenghui21ef8202015-05-28 16:06:28 -07001077 approximationInfo, outputVertices, depth + 1);
Chris Craik65cd6122012-12-10 17:56:27 -08001078 }
1079}
1080
1081void PathTessellator::recursiveQuadraticBezierVertices(
1082 float ax, float ay,
1083 float bx, float by,
1084 float cx, float cy,
ztenghui21ef8202015-05-28 16:06:28 -07001085 const PathApproximationInfo& approximationInfo,
John Reck272a6852015-07-29 16:48:58 -07001086 std::vector<Vertex>& outputVertices, int depth) {
Chris Craik65cd6122012-12-10 17:56:27 -08001087 float dx = bx - ax;
1088 float dy = by - ay;
ztenghui21ef8202015-05-28 16:06:28 -07001089 // d is the cross product of vector (B-A) and (C-B).
Chris Craik65cd6122012-12-10 17:56:27 -08001090 float d = (cx - bx) * dy - (cy - by) * dx;
1091
Chris Craik74cf7e62014-08-07 14:34:46 -07001092 if (depth >= MAX_DEPTH
ztenghui21ef8202015-05-28 16:06:28 -07001093 || d * d <= getThreshold(approximationInfo, dx, dy)) {
Chris Craik65cd6122012-12-10 17:56:27 -08001094 // below thresh, draw line by adding endpoint
John Reck272a6852015-07-29 16:48:58 -07001095 outputVertices.push_back(Vertex{bx, by});
Chris Craik65cd6122012-12-10 17:56:27 -08001096 } else {
1097 float acx = (ax + cx) * 0.5f;
1098 float bcx = (bx + cx) * 0.5f;
1099 float acy = (ay + cy) * 0.5f;
1100 float bcy = (by + cy) * 0.5f;
1101
1102 // midpoint
1103 float mx = (acx + bcx) * 0.5f;
1104 float my = (acy + bcy) * 0.5f;
1105
1106 recursiveQuadraticBezierVertices(ax, ay, mx, my, acx, acy,
ztenghui21ef8202015-05-28 16:06:28 -07001107 approximationInfo, outputVertices, depth + 1);
Chris Craik65cd6122012-12-10 17:56:27 -08001108 recursiveQuadraticBezierVertices(mx, my, bx, by, bcx, bcy,
ztenghui21ef8202015-05-28 16:06:28 -07001109 approximationInfo, outputVertices, depth + 1);
Chris Craik65cd6122012-12-10 17:56:27 -08001110 }
1111}
1112
1113}; // namespace uirenderer
1114}; // namespace android