blob: 38f214ab3e5d2a8f1c38f97477945f3f17080223 [file] [log] [blame]
Chris Craik65cd6122012-12-10 17:56:27 -08001/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Chris Craikf0a59072013-11-19 18:00:46 -080017#define LOG_TAG "OpenGLRenderer"
Chris Craik65cd6122012-12-10 17:56:27 -080018#define LOG_NDEBUG 1
Chris Craik87f9df82014-03-07 14:34:42 -080019#define ATRACE_TAG ATRACE_TAG_VIEW
Chris Craik65cd6122012-12-10 17:56:27 -080020
21#define VERTEX_DEBUG 0
22
23#if VERTEX_DEBUG
24#define DEBUG_DUMP_ALPHA_BUFFER() \
25 for (unsigned int i = 0; i < vertexBuffer.getSize(); i++) { \
26 ALOGD("point %d at %f %f, alpha %f", \
Romain Guy3380cfd2013-08-15 16:57:57 -070027 i, buffer[i].x, buffer[i].y, buffer[i].alpha); \
Chris Craik65cd6122012-12-10 17:56:27 -080028 }
29#define DEBUG_DUMP_BUFFER() \
30 for (unsigned int i = 0; i < vertexBuffer.getSize(); i++) { \
Romain Guy3380cfd2013-08-15 16:57:57 -070031 ALOGD("point %d at %f %f", i, buffer[i].x, buffer[i].y); \
Chris Craik65cd6122012-12-10 17:56:27 -080032 }
33#else
34#define DEBUG_DUMP_ALPHA_BUFFER()
35#define DEBUG_DUMP_BUFFER()
36#endif
37
38#include <SkPath.h>
39#include <SkPaint.h>
Chris Craikfca52b752015-04-28 11:45:59 -070040#include <SkPoint.h>
Derek Sollenbergerb0772a62015-03-16 14:35:55 -040041#include <SkGeometry.h> // WARNING: Internal Skia Header
Chris Craik65cd6122012-12-10 17:56:27 -080042
43#include <stdlib.h>
44#include <stdint.h>
45#include <sys/types.h>
46
47#include <utils/Log.h>
48#include <utils/Trace.h>
49
50#include "PathTessellator.h"
51#include "Matrix.h"
52#include "Vector.h"
53#include "Vertex.h"
Chris Craik74cf7e62014-08-07 14:34:46 -070054#include "utils/MathUtils.h"
Chris Craik65cd6122012-12-10 17:56:27 -080055
56namespace android {
57namespace uirenderer {
58
ztenghui21ef8202015-05-28 16:06:28 -070059#define OUTLINE_REFINE_THRESHOLD 0.5f
Chris Craik65cd6122012-12-10 17:56:27 -080060#define ROUND_CAP_THRESH 0.25f
61#define PI 3.1415926535897932f
Chris Craik74cf7e62014-08-07 14:34:46 -070062#define MAX_DEPTH 15
Chris Craik65cd6122012-12-10 17:56:27 -080063
Chris Craik74cf7e62014-08-07 14:34:46 -070064/**
65 * Extracts the x and y scale from the transform as positive values, and clamps them
66 */
Chris Craik05f3d6e2014-06-02 16:27:04 -070067void PathTessellator::extractTessellationScales(const Matrix4& transform,
68 float* scaleX, float* scaleY) {
Chris Craikfe02b4b2014-06-16 16:34:29 -070069 if (CC_LIKELY(transform.isPureTranslate())) {
70 *scaleX = 1.0f;
71 *scaleY = 1.0f;
72 } else {
Chris Craik05f3d6e2014-06-02 16:27:04 -070073 float m00 = transform.data[Matrix4::kScaleX];
74 float m01 = transform.data[Matrix4::kSkewY];
75 float m10 = transform.data[Matrix4::kSkewX];
76 float m11 = transform.data[Matrix4::kScaleY];
Chris Craik74cf7e62014-08-07 14:34:46 -070077 *scaleX = MathUtils::clampTessellationScale(sqrt(m00 * m00 + m01 * m01));
78 *scaleY = MathUtils::clampTessellationScale(sqrt(m10 * m10 + m11 * m11));
Chris Craik65cd6122012-12-10 17:56:27 -080079 }
80}
81
Chris Craik65cd6122012-12-10 17:56:27 -080082/**
83 * Produces a pseudo-normal for a vertex, given the normals of the two incoming lines. If the offset
84 * from each vertex in a perimeter is calculated, the resultant lines connecting the offset vertices
85 * will be offset by 1.0
86 *
87 * Note that we can't add and normalize the two vectors, that would result in a rectangle having an
88 * offset of (sqrt(2)/2, sqrt(2)/2) at each corner, instead of (1, 1)
89 *
90 * NOTE: assumes angles between normals 90 degrees or less
91 */
John Reck1aa5d2d2014-07-24 13:38:28 -070092inline static Vector2 totalOffsetFromNormals(const Vector2& normalA, const Vector2& normalB) {
Chris Craik65cd6122012-12-10 17:56:27 -080093 return (normalA + normalB) / (1 + fabs(normalA.dot(normalB)));
94}
95
96/**
97 * Structure used for storing useful information about the SkPaint and scale used for tessellating
98 */
99struct PaintInfo {
100public:
Chris Craikd6b65f62014-01-01 14:45:21 -0800101 PaintInfo(const SkPaint* paint, const mat4& transform) :
Chris Craik65cd6122012-12-10 17:56:27 -0800102 style(paint->getStyle()), cap(paint->getStrokeCap()), isAA(paint->isAntiAlias()),
Chris Craik65cd6122012-12-10 17:56:27 -0800103 halfStrokeWidth(paint->getStrokeWidth() * 0.5f), maxAlpha(1.0f) {
104 // compute inverse scales
Chris Craikfe02b4b2014-06-16 16:34:29 -0700105 if (CC_LIKELY(transform.isPureTranslate())) {
106 inverseScaleX = 1.0f;
107 inverseScaleY = 1.0f;
108 } else {
Chris Craik05f3d6e2014-06-02 16:27:04 -0700109 float scaleX, scaleY;
110 PathTessellator::extractTessellationScales(transform, &scaleX, &scaleY);
Chris Craik74cf7e62014-08-07 14:34:46 -0700111 inverseScaleX = 1.0f / scaleX;
112 inverseScaleY = 1.0f / scaleY;
Chris Craik65cd6122012-12-10 17:56:27 -0800113 }
114
115 if (isAA && halfStrokeWidth != 0 && inverseScaleX == inverseScaleY &&
Chris Craik19a390b2013-02-27 15:43:26 -0800116 2 * halfStrokeWidth < inverseScaleX) {
Chris Craik05f3d6e2014-06-02 16:27:04 -0700117 // AA, with non-hairline stroke, width < 1 pixel. Scale alpha and treat as hairline.
Chris Craik65cd6122012-12-10 17:56:27 -0800118 maxAlpha *= (2 * halfStrokeWidth) / inverseScaleX;
119 halfStrokeWidth = 0.0f;
120 }
121 }
122
123 SkPaint::Style style;
124 SkPaint::Cap cap;
125 bool isAA;
126 float inverseScaleX;
127 float inverseScaleY;
128 float halfStrokeWidth;
129 float maxAlpha;
130
John Reck1aa5d2d2014-07-24 13:38:28 -0700131 inline void scaleOffsetForStrokeWidth(Vector2& offset) const {
Chris Craik65cd6122012-12-10 17:56:27 -0800132 if (halfStrokeWidth == 0.0f) {
133 // hairline - compensate for scale
134 offset.x *= 0.5f * inverseScaleX;
135 offset.y *= 0.5f * inverseScaleY;
136 } else {
137 offset *= halfStrokeWidth;
138 }
139 }
140
141 /**
142 * NOTE: the input will not always be a normal, especially for sharp edges - it should be the
143 * result of totalOffsetFromNormals (see documentation there)
144 */
John Reck1aa5d2d2014-07-24 13:38:28 -0700145 inline Vector2 deriveAAOffset(const Vector2& offset) const {
146 return (Vector2){offset.x * 0.5f * inverseScaleX, offset.y * 0.5f * inverseScaleY};
Chris Craik65cd6122012-12-10 17:56:27 -0800147 }
148
149 /**
150 * Returns the number of cap divisions beyond the minimum 2 (kButt_Cap/kSquareCap will return 0)
151 * Should only be used when stroking and drawing caps
152 */
153 inline int capExtraDivisions() const {
154 if (cap == SkPaint::kRound_Cap) {
Chris Craikff29b5a2015-05-27 18:17:03 -0700155 // always use 2 points for hairline
Chris Craik65cd6122012-12-10 17:56:27 -0800156 if (halfStrokeWidth == 0.0f) return 2;
157
Chris Craikff29b5a2015-05-27 18:17:03 -0700158 float threshold = MathUtils::min(inverseScaleX, inverseScaleY) * ROUND_CAP_THRESH;
159 return MathUtils::divisionsNeededToApproximateArc(halfStrokeWidth, PI, threshold);
Chris Craik65cd6122012-12-10 17:56:27 -0800160 }
161 return 0;
162 }
Chris Craikf0a59072013-11-19 18:00:46 -0800163
164 /**
Chris Craik21bcfc4f2014-09-08 18:43:30 -0700165 * Outset the bounds of point data (for line endpoints or points) to account for stroke
Chris Craikf0a59072013-11-19 18:00:46 -0800166 * geometry.
Chris Craik21bcfc4f2014-09-08 18:43:30 -0700167 *
168 * bounds are in pre-scaled space.
Chris Craikf0a59072013-11-19 18:00:46 -0800169 */
Chris Craik05f3d6e2014-06-02 16:27:04 -0700170 void expandBoundsForStroke(Rect* bounds) const {
Chris Craik21bcfc4f2014-09-08 18:43:30 -0700171 if (halfStrokeWidth == 0) {
172 // hairline, outset by (0.5f + fudge factor) in post-scaling space
173 bounds->outset(fabs(inverseScaleX) * (0.5f + Vertex::GeometryFudgeFactor()),
174 fabs(inverseScaleY) * (0.5f + Vertex::GeometryFudgeFactor()));
175 } else {
176 // non hairline, outset by half stroke width pre-scaled, and fudge factor post scaled
177 bounds->outset(halfStrokeWidth + fabs(inverseScaleX) * Vertex::GeometryFudgeFactor(),
178 halfStrokeWidth + fabs(inverseScaleY) * Vertex::GeometryFudgeFactor());
179 }
Chris Craikf0a59072013-11-19 18:00:46 -0800180 }
Chris Craik65cd6122012-12-10 17:56:27 -0800181};
182
183void getFillVerticesFromPerimeter(const Vector<Vertex>& perimeter, VertexBuffer& vertexBuffer) {
184 Vertex* buffer = vertexBuffer.alloc<Vertex>(perimeter.size());
185
186 int currentIndex = 0;
187 // zig zag between all previous points on the inside of the hull to create a
188 // triangle strip that fills the hull
189 int srcAindex = 0;
190 int srcBindex = perimeter.size() - 1;
191 while (srcAindex <= srcBindex) {
Chris Craik11a75672013-12-16 17:08:15 -0800192 buffer[currentIndex++] = perimeter[srcAindex];
Chris Craik65cd6122012-12-10 17:56:27 -0800193 if (srcAindex == srcBindex) break;
Chris Craik11a75672013-12-16 17:08:15 -0800194 buffer[currentIndex++] = perimeter[srcBindex];
Chris Craik65cd6122012-12-10 17:56:27 -0800195 srcAindex++;
196 srcBindex--;
197 }
198}
199
200/*
201 * Fills a vertexBuffer with non-alpha vertices, zig-zagging at each perimeter point to create a
202 * tri-strip as wide as the stroke.
203 *
204 * Uses an additional 2 vertices at the end to wrap around, closing the tri-strip
205 * (for a total of perimeter.size() * 2 + 2 vertices)
206 */
207void getStrokeVerticesFromPerimeter(const PaintInfo& paintInfo, const Vector<Vertex>& perimeter,
208 VertexBuffer& vertexBuffer) {
209 Vertex* buffer = vertexBuffer.alloc<Vertex>(perimeter.size() * 2 + 2);
210
211 int currentIndex = 0;
212 const Vertex* last = &(perimeter[perimeter.size() - 1]);
213 const Vertex* current = &(perimeter[0]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700214 Vector2 lastNormal = {current->y - last->y, last->x - current->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800215 lastNormal.normalize();
216 for (unsigned int i = 0; i < perimeter.size(); i++) {
217 const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700218 Vector2 nextNormal = {next->y - current->y, current->x - next->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800219 nextNormal.normalize();
220
John Reck1aa5d2d2014-07-24 13:38:28 -0700221 Vector2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
Chris Craik65cd6122012-12-10 17:56:27 -0800222 paintInfo.scaleOffsetForStrokeWidth(totalOffset);
223
224 Vertex::set(&buffer[currentIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700225 current->x + totalOffset.x,
226 current->y + totalOffset.y);
Chris Craik65cd6122012-12-10 17:56:27 -0800227
228 Vertex::set(&buffer[currentIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700229 current->x - totalOffset.x,
230 current->y - totalOffset.y);
Chris Craik65cd6122012-12-10 17:56:27 -0800231
Chris Craik65cd6122012-12-10 17:56:27 -0800232 current = next;
233 lastNormal = nextNormal;
234 }
235
236 // wrap around to beginning
Chris Craik11a75672013-12-16 17:08:15 -0800237 buffer[currentIndex++] = buffer[0];
238 buffer[currentIndex++] = buffer[1];
Chris Craik65cd6122012-12-10 17:56:27 -0800239
240 DEBUG_DUMP_BUFFER();
241}
242
Chris Craik6d29c8d2013-05-08 18:35:44 -0700243static inline void storeBeginEnd(const PaintInfo& paintInfo, const Vertex& center,
John Reck1aa5d2d2014-07-24 13:38:28 -0700244 const Vector2& normal, Vertex* buffer, int& currentIndex, bool begin) {
245 Vector2 strokeOffset = normal;
Chris Craik6d29c8d2013-05-08 18:35:44 -0700246 paintInfo.scaleOffsetForStrokeWidth(strokeOffset);
247
John Reck1aa5d2d2014-07-24 13:38:28 -0700248 Vector2 referencePoint = {center.x, center.y};
Chris Craik6d29c8d2013-05-08 18:35:44 -0700249 if (paintInfo.cap == SkPaint::kSquare_Cap) {
John Reck1aa5d2d2014-07-24 13:38:28 -0700250 Vector2 rotated = {-strokeOffset.y, strokeOffset.x};
251 referencePoint += rotated * (begin ? -1 : 1);
Chris Craik6d29c8d2013-05-08 18:35:44 -0700252 }
253
254 Vertex::set(&buffer[currentIndex++], referencePoint + strokeOffset);
255 Vertex::set(&buffer[currentIndex++], referencePoint - strokeOffset);
256}
257
Chris Craik65cd6122012-12-10 17:56:27 -0800258/**
259 * Fills a vertexBuffer with non-alpha vertices similar to getStrokeVerticesFromPerimeter, except:
260 *
261 * 1 - Doesn't need to wrap around, since the input vertices are unclosed
262 *
263 * 2 - can zig-zag across 'extra' vertices at either end, to create round caps
264 */
265void getStrokeVerticesFromUnclosedVertices(const PaintInfo& paintInfo,
266 const Vector<Vertex>& vertices, VertexBuffer& vertexBuffer) {
267 const int extra = paintInfo.capExtraDivisions();
268 const int allocSize = (vertices.size() + extra) * 2;
Chris Craik65cd6122012-12-10 17:56:27 -0800269 Vertex* buffer = vertexBuffer.alloc<Vertex>(allocSize);
270
Chris Craik6d29c8d2013-05-08 18:35:44 -0700271 const int lastIndex = vertices.size() - 1;
Chris Craik65cd6122012-12-10 17:56:27 -0800272 if (extra > 0) {
273 // tessellate both round caps
Chris Craik65cd6122012-12-10 17:56:27 -0800274 float beginTheta = atan2(
Romain Guy3380cfd2013-08-15 16:57:57 -0700275 - (vertices[0].x - vertices[1].x),
276 vertices[0].y - vertices[1].y);
Chris Craik65cd6122012-12-10 17:56:27 -0800277 float endTheta = atan2(
Romain Guy3380cfd2013-08-15 16:57:57 -0700278 - (vertices[lastIndex].x - vertices[lastIndex - 1].x),
279 vertices[lastIndex].y - vertices[lastIndex - 1].y);
Chris Craik65cd6122012-12-10 17:56:27 -0800280 const float dTheta = PI / (extra + 1);
Chris Craik65cd6122012-12-10 17:56:27 -0800281
282 int capOffset;
283 for (int i = 0; i < extra; i++) {
284 if (i < extra / 2) {
285 capOffset = extra - 2 * i - 1;
286 } else {
287 capOffset = 2 * i - extra;
288 }
289
290 beginTheta += dTheta;
Dan Albertff1d8a62014-11-11 19:31:26 -0800291 Vector2 beginRadialOffset = {cosf(beginTheta), sinf(beginTheta)};
Chris Craik65cd6122012-12-10 17:56:27 -0800292 paintInfo.scaleOffsetForStrokeWidth(beginRadialOffset);
293 Vertex::set(&buffer[capOffset],
Romain Guy3380cfd2013-08-15 16:57:57 -0700294 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],
Romain Guy3380cfd2013-08-15 16:57:57 -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 Reck1aa5d2d2014-07-24 13:38:28 -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 */
345void getFillVerticesFromPerimeterAA(const PaintInfo& paintInfo, const Vector<Vertex>& perimeter,
Chris Craikf0a59072013-11-19 18:00:46 -0800346 VertexBuffer& vertexBuffer, float maxAlpha = 1.0f) {
Chris Craik65cd6122012-12-10 17:56:27 -0800347 AlphaVertex* buffer = vertexBuffer.alloc<AlphaVertex>(perimeter.size() * 3 + 2);
348
349 // generate alpha points - fill Alpha vertex gaps in between each point with
350 // alpha 0 vertex, offset by a scaled normal.
351 int currentIndex = 0;
352 const Vertex* last = &(perimeter[perimeter.size() - 1]);
353 const Vertex* current = &(perimeter[0]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700354 Vector2 lastNormal = {current->y - last->y, last->x - current->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800355 lastNormal.normalize();
356 for (unsigned int i = 0; i < perimeter.size(); i++) {
357 const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700358 Vector2 nextNormal = {next->y - current->y, current->x - next->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800359 nextNormal.normalize();
360
361 // AA point offset from original point is that point's normal, such that each side is offset
362 // by .5 pixels
John Reck1aa5d2d2014-07-24 13:38:28 -0700363 Vector2 totalOffset = paintInfo.deriveAAOffset(totalOffsetFromNormals(lastNormal, nextNormal));
Chris Craik65cd6122012-12-10 17:56:27 -0800364
365 AlphaVertex::set(&buffer[currentIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700366 current->x + totalOffset.x,
367 current->y + totalOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800368 0.0f);
369 AlphaVertex::set(&buffer[currentIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700370 current->x - totalOffset.x,
371 current->y - totalOffset.y,
Chris Craikf0a59072013-11-19 18:00:46 -0800372 maxAlpha);
Chris Craik65cd6122012-12-10 17:56:27 -0800373
Chris Craik65cd6122012-12-10 17:56:27 -0800374 current = next;
375 lastNormal = nextNormal;
376 }
377
378 // wrap around to beginning
Chris Craik11a75672013-12-16 17:08:15 -0800379 buffer[currentIndex++] = buffer[0];
380 buffer[currentIndex++] = buffer[1];
Chris Craik65cd6122012-12-10 17:56:27 -0800381
382 // zig zag between all previous points on the inside of the hull to create a
383 // triangle strip that fills the hull, repeating the first inner point to
384 // create degenerate tris to start inside path
385 int srcAindex = 0;
386 int srcBindex = perimeter.size() - 1;
387 while (srcAindex <= srcBindex) {
Chris Craik11a75672013-12-16 17:08:15 -0800388 buffer[currentIndex++] = buffer[srcAindex * 2 + 1];
Chris Craik65cd6122012-12-10 17:56:27 -0800389 if (srcAindex == srcBindex) break;
Chris Craik11a75672013-12-16 17:08:15 -0800390 buffer[currentIndex++] = buffer[srcBindex * 2 + 1];
Chris Craik65cd6122012-12-10 17:56:27 -0800391 srcAindex++;
392 srcBindex--;
393 }
394
395 DEBUG_DUMP_BUFFER();
396}
397
398/**
399 * Stores geometry for a single, AA-perimeter (potentially rounded) cap
400 *
401 * For explanation of constants and general methodoloyg, see comments for
402 * getStrokeVerticesFromUnclosedVerticesAA() below.
403 */
Chris Craik6d29c8d2013-05-08 18:35:44 -0700404inline static void storeCapAA(const PaintInfo& paintInfo, const Vector<Vertex>& vertices,
John Reck1aa5d2d2014-07-24 13:38:28 -0700405 AlphaVertex* buffer, bool isFirst, Vector2 normal, int offset) {
Chris Craik65cd6122012-12-10 17:56:27 -0800406 const int extra = paintInfo.capExtraDivisions();
407 const int extraOffset = (extra + 1) / 2;
408 const int capIndex = isFirst
409 ? 2 * offset + 6 + 2 * (extra + extraOffset)
410 : offset + 2 + 2 * extraOffset;
411 if (isFirst) normal *= -1;
412
413 // TODO: this normal should be scaled by radialScale if extra != 0, see totalOffsetFromNormals()
John Reck1aa5d2d2014-07-24 13:38:28 -0700414 Vector2 AAOffset = paintInfo.deriveAAOffset(normal);
Chris Craik65cd6122012-12-10 17:56:27 -0800415
John Reck1aa5d2d2014-07-24 13:38:28 -0700416 Vector2 strokeOffset = normal;
Chris Craik65cd6122012-12-10 17:56:27 -0800417 paintInfo.scaleOffsetForStrokeWidth(strokeOffset);
John Reck1aa5d2d2014-07-24 13:38:28 -0700418 Vector2 outerOffset = strokeOffset + AAOffset;
419 Vector2 innerOffset = strokeOffset - AAOffset;
Chris Craik65cd6122012-12-10 17:56:27 -0800420
John Reck1aa5d2d2014-07-24 13:38:28 -0700421 Vector2 capAAOffset = {0, 0};
Chris Craik65cd6122012-12-10 17:56:27 -0800422 if (paintInfo.cap != SkPaint::kRound_Cap) {
423 // if the cap is square or butt, the inside primary cap vertices will be inset in two
424 // directions - both normal to the stroke, and parallel to it.
John Reck1aa5d2d2014-07-24 13:38:28 -0700425 capAAOffset = (Vector2){-AAOffset.y, AAOffset.x};
Chris Craik65cd6122012-12-10 17:56:27 -0800426 }
427
428 // determine referencePoint, the center point for the 4 primary cap vertices
429 const Vertex* point = isFirst ? vertices.begin() : (vertices.end() - 1);
John Reck1aa5d2d2014-07-24 13:38:28 -0700430 Vector2 referencePoint = {point->x, point->y};
Chris Craik65cd6122012-12-10 17:56:27 -0800431 if (paintInfo.cap == SkPaint::kSquare_Cap) {
432 // To account for square cap, move the primary cap vertices (that create the AA edge) by the
433 // stroke offset vector (rotated to be parallel to the stroke)
John Reck1aa5d2d2014-07-24 13:38:28 -0700434 Vector2 rotated = {-strokeOffset.y, strokeOffset.x};
435 referencePoint += rotated;
Chris Craik65cd6122012-12-10 17:56:27 -0800436 }
437
438 AlphaVertex::set(&buffer[capIndex + 0],
439 referencePoint.x + outerOffset.x + capAAOffset.x,
440 referencePoint.y + outerOffset.y + capAAOffset.y,
441 0.0f);
442 AlphaVertex::set(&buffer[capIndex + 1],
443 referencePoint.x + innerOffset.x - capAAOffset.x,
444 referencePoint.y + innerOffset.y - capAAOffset.y,
445 paintInfo.maxAlpha);
446
447 bool isRound = paintInfo.cap == SkPaint::kRound_Cap;
448
449 const int postCapIndex = (isRound && isFirst) ? (2 * extraOffset - 2) : capIndex + (2 * extra);
450 AlphaVertex::set(&buffer[postCapIndex + 2],
451 referencePoint.x - outerOffset.x + capAAOffset.x,
452 referencePoint.y - outerOffset.y + capAAOffset.y,
453 0.0f);
454 AlphaVertex::set(&buffer[postCapIndex + 3],
455 referencePoint.x - innerOffset.x - capAAOffset.x,
456 referencePoint.y - innerOffset.y - capAAOffset.y,
457 paintInfo.maxAlpha);
458
459 if (isRound) {
460 const float dTheta = PI / (extra + 1);
461 const float radialScale = 2.0f / (1 + cos(dTheta));
462 float theta = atan2(normal.y, normal.x);
463 int capPerimIndex = capIndex + 2;
464
465 for (int i = 0; i < extra; i++) {
466 theta += dTheta;
467
Dan Albertff1d8a62014-11-11 19:31:26 -0800468 Vector2 radialOffset = {cosf(theta), sinf(theta)};
Chris Craik65cd6122012-12-10 17:56:27 -0800469
470 // scale to compensate for pinching at sharp angles, see totalOffsetFromNormals()
471 radialOffset *= radialScale;
472
473 AAOffset = paintInfo.deriveAAOffset(radialOffset);
474 paintInfo.scaleOffsetForStrokeWidth(radialOffset);
475 AlphaVertex::set(&buffer[capPerimIndex++],
476 referencePoint.x + radialOffset.x + AAOffset.x,
477 referencePoint.y + radialOffset.y + AAOffset.y,
478 0.0f);
479 AlphaVertex::set(&buffer[capPerimIndex++],
480 referencePoint.x + radialOffset.x - AAOffset.x,
481 referencePoint.y + radialOffset.y - AAOffset.y,
482 paintInfo.maxAlpha);
483
484 if (isFirst && i == extra - extraOffset) {
485 //copy most recent two points to first two points
Chris Craik11a75672013-12-16 17:08:15 -0800486 buffer[0] = buffer[capPerimIndex - 2];
487 buffer[1] = buffer[capPerimIndex - 1];
Chris Craik65cd6122012-12-10 17:56:27 -0800488
489 capPerimIndex = 2; // start writing the rest of the round cap at index 2
490 }
491 }
492
493 if (isFirst) {
494 const int startCapFillIndex = capIndex + 2 * (extra - extraOffset) + 4;
495 int capFillIndex = startCapFillIndex;
496 for (int i = 0; i < extra + 2; i += 2) {
Chris Craik11a75672013-12-16 17:08:15 -0800497 buffer[capFillIndex++] = buffer[1 + i];
Chris Craik65cd6122012-12-10 17:56:27 -0800498 // TODO: to support odd numbers of divisions, break here on the last iteration
Chris Craik11a75672013-12-16 17:08:15 -0800499 buffer[capFillIndex++] = buffer[startCapFillIndex - 3 - i];
Chris Craik65cd6122012-12-10 17:56:27 -0800500 }
501 } else {
502 int capFillIndex = 6 * vertices.size() + 2 + 6 * extra - (extra + 2);
503 for (int i = 0; i < extra + 2; i += 2) {
Chris Craik11a75672013-12-16 17:08:15 -0800504 buffer[capFillIndex++] = buffer[capIndex + 1 + i];
Chris Craik65cd6122012-12-10 17:56:27 -0800505 // TODO: to support odd numbers of divisions, break here on the last iteration
Chris Craik11a75672013-12-16 17:08:15 -0800506 buffer[capFillIndex++] = buffer[capIndex + 3 + 2 * extra - i];
Chris Craik65cd6122012-12-10 17:56:27 -0800507 }
508 }
509 return;
510 }
511 if (isFirst) {
Chris Craik11a75672013-12-16 17:08:15 -0800512 buffer[0] = buffer[postCapIndex + 2];
513 buffer[1] = buffer[postCapIndex + 3];
514 buffer[postCapIndex + 4] = buffer[1]; // degenerate tris (the only two!)
515 buffer[postCapIndex + 5] = buffer[postCapIndex + 1];
Chris Craik65cd6122012-12-10 17:56:27 -0800516 } else {
Chris Craik11a75672013-12-16 17:08:15 -0800517 buffer[6 * vertices.size()] = buffer[postCapIndex + 1];
518 buffer[6 * vertices.size() + 1] = buffer[postCapIndex + 3];
Chris Craik65cd6122012-12-10 17:56:27 -0800519 }
520}
521
522/*
523the geometry for an aa, capped stroke consists of the following:
524
525 # vertices | function
526----------------------------------------------------------------------
527a) 2 | Start AA perimeter
528b) 2, 2 * roundDivOff | First half of begin cap's perimeter
529 |
530 2 * middlePts | 'Outer' or 'Top' AA perimeter half (between caps)
531 |
532a) 4 | End cap's
533b) 2, 2 * roundDivs, 2 | AA perimeter
534 |
535 2 * middlePts | 'Inner' or 'bottom' AA perimeter half
536 |
537a) 6 | Begin cap's perimeter
538b) 2, 2*(rD - rDO + 1), | Last half of begin cap's perimeter
539 roundDivs, 2 |
540 |
541 2 * middlePts | Stroke's full opacity center strip
542 |
543a) 2 | end stroke
544b) 2, roundDivs | (and end cap fill, for round)
545
546Notes:
547* rows starting with 'a)' denote the Butt or Square cap vertex use, 'b)' denote Round
548
549* 'middlePts' is (number of points in the unclosed input vertex list, minus 2) times two
550
551* 'roundDivs' or 'rD' is the number of extra vertices (beyond the minimum of 2) that define the
552 round cap's shape, and is at least two. This will increase with cap size to sufficiently
553 define the cap's level of tessellation.
554
555* 'roundDivOffset' or 'rDO' is the point about halfway along the start cap's round perimeter, where
556 the stream of vertices for the AA perimeter starts. By starting and ending the perimeter at
557 this offset, the fill of the stroke is drawn from this point with minimal extra vertices.
558
559This means the outer perimeter starts at:
560 outerIndex = (2) OR (2 + 2 * roundDivOff)
561the inner perimeter (since it is filled in reverse) starts at:
562 innerIndex = outerIndex + (4 * middlePts) + ((4) OR (4 + 2 * roundDivs)) - 1
563the stroke starts at:
564 strokeIndex = innerIndex + 1 + ((6) OR (6 + 3 * roundDivs - 2 * roundDivOffset))
565
566The total needed allocated space is either:
567 2 + 4 + 6 + 2 + 3 * (2 * middlePts) = 14 + 6 * middlePts = 2 + 6 * pts
568or, for rounded caps:
569 (2 + 2 * rDO) + (4 + 2 * rD) + (2 * (rD - rDO + 1)
570 + roundDivs + 4) + (2 + roundDivs) + 3 * (2 * middlePts)
571 = 14 + 6 * middlePts + 6 * roundDivs
572 = 2 + 6 * pts + 6 * roundDivs
573 */
574void getStrokeVerticesFromUnclosedVerticesAA(const PaintInfo& paintInfo,
575 const Vector<Vertex>& vertices, VertexBuffer& vertexBuffer) {
576
577 const int extra = paintInfo.capExtraDivisions();
578 const int allocSize = 6 * vertices.size() + 2 + 6 * extra;
579
580 AlphaVertex* buffer = vertexBuffer.alloc<AlphaVertex>(allocSize);
581
582 const int extraOffset = (extra + 1) / 2;
583 int offset = 2 * (vertices.size() - 2);
584 // there is no outer/inner here, using them for consistency with below approach
585 int currentAAOuterIndex = 2 + 2 * extraOffset;
586 int currentAAInnerIndex = currentAAOuterIndex + (2 * offset) + 3 + (2 * extra);
587 int currentStrokeIndex = currentAAInnerIndex + 7 + (3 * extra - 2 * extraOffset);
588
589 const Vertex* last = &(vertices[0]);
590 const Vertex* current = &(vertices[1]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700591 Vector2 lastNormal = {current->y - last->y, last->x - current->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800592 lastNormal.normalize();
593
594 // TODO: use normal from bezier traversal for cap, instead of from vertices
595 storeCapAA(paintInfo, vertices, buffer, true, lastNormal, offset);
596
597 for (unsigned int i = 1; i < vertices.size() - 1; i++) {
598 const Vertex* next = &(vertices[i + 1]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700599 Vector2 nextNormal = {next->y - current->y, current->x - next->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800600 nextNormal.normalize();
601
John Reck1aa5d2d2014-07-24 13:38:28 -0700602 Vector2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
603 Vector2 AAOffset = paintInfo.deriveAAOffset(totalOffset);
Chris Craik65cd6122012-12-10 17:56:27 -0800604
John Reck1aa5d2d2014-07-24 13:38:28 -0700605 Vector2 innerOffset = totalOffset;
Chris Craik65cd6122012-12-10 17:56:27 -0800606 paintInfo.scaleOffsetForStrokeWidth(innerOffset);
John Reck1aa5d2d2014-07-24 13:38:28 -0700607 Vector2 outerOffset = innerOffset + AAOffset;
Chris Craik65cd6122012-12-10 17:56:27 -0800608 innerOffset -= AAOffset;
609
610 AlphaVertex::set(&buffer[currentAAOuterIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700611 current->x + outerOffset.x,
612 current->y + outerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800613 0.0f);
614 AlphaVertex::set(&buffer[currentAAOuterIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700615 current->x + innerOffset.x,
616 current->y + innerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800617 paintInfo.maxAlpha);
618
619 AlphaVertex::set(&buffer[currentStrokeIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700620 current->x + innerOffset.x,
621 current->y + innerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800622 paintInfo.maxAlpha);
623 AlphaVertex::set(&buffer[currentStrokeIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700624 current->x - innerOffset.x,
625 current->y - innerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800626 paintInfo.maxAlpha);
627
628 AlphaVertex::set(&buffer[currentAAInnerIndex--],
Romain Guy3380cfd2013-08-15 16:57:57 -0700629 current->x - innerOffset.x,
630 current->y - innerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800631 paintInfo.maxAlpha);
632 AlphaVertex::set(&buffer[currentAAInnerIndex--],
Romain Guy3380cfd2013-08-15 16:57:57 -0700633 current->x - outerOffset.x,
634 current->y - outerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800635 0.0f);
636
637 current = next;
638 lastNormal = nextNormal;
639 }
640
641 // TODO: use normal from bezier traversal for cap, instead of from vertices
642 storeCapAA(paintInfo, vertices, buffer, false, lastNormal, offset);
643
644 DEBUG_DUMP_ALPHA_BUFFER();
645}
646
647
648void getStrokeVerticesFromPerimeterAA(const PaintInfo& paintInfo, const Vector<Vertex>& perimeter,
649 VertexBuffer& vertexBuffer) {
650 AlphaVertex* buffer = vertexBuffer.alloc<AlphaVertex>(6 * perimeter.size() + 8);
651
652 int offset = 2 * perimeter.size() + 3;
653 int currentAAOuterIndex = 0;
654 int currentStrokeIndex = offset;
655 int currentAAInnerIndex = offset * 2;
656
657 const Vertex* last = &(perimeter[perimeter.size() - 1]);
658 const Vertex* current = &(perimeter[0]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700659 Vector2 lastNormal = {current->y - last->y, last->x - current->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800660 lastNormal.normalize();
661 for (unsigned int i = 0; i < perimeter.size(); i++) {
662 const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700663 Vector2 nextNormal = {next->y - current->y, current->x - next->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800664 nextNormal.normalize();
665
John Reck1aa5d2d2014-07-24 13:38:28 -0700666 Vector2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
667 Vector2 AAOffset = paintInfo.deriveAAOffset(totalOffset);
Chris Craik65cd6122012-12-10 17:56:27 -0800668
John Reck1aa5d2d2014-07-24 13:38:28 -0700669 Vector2 innerOffset = totalOffset;
Chris Craik65cd6122012-12-10 17:56:27 -0800670 paintInfo.scaleOffsetForStrokeWidth(innerOffset);
John Reck1aa5d2d2014-07-24 13:38:28 -0700671 Vector2 outerOffset = innerOffset + AAOffset;
Chris Craik65cd6122012-12-10 17:56:27 -0800672 innerOffset -= AAOffset;
673
674 AlphaVertex::set(&buffer[currentAAOuterIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700675 current->x + outerOffset.x,
676 current->y + outerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800677 0.0f);
678 AlphaVertex::set(&buffer[currentAAOuterIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700679 current->x + innerOffset.x,
680 current->y + innerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800681 paintInfo.maxAlpha);
682
683 AlphaVertex::set(&buffer[currentStrokeIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700684 current->x + innerOffset.x,
685 current->y + innerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800686 paintInfo.maxAlpha);
687 AlphaVertex::set(&buffer[currentStrokeIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700688 current->x - innerOffset.x,
689 current->y - innerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800690 paintInfo.maxAlpha);
691
692 AlphaVertex::set(&buffer[currentAAInnerIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700693 current->x - innerOffset.x,
694 current->y - innerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800695 paintInfo.maxAlpha);
696 AlphaVertex::set(&buffer[currentAAInnerIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700697 current->x - outerOffset.x,
698 current->y - outerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800699 0.0f);
700
Chris Craik65cd6122012-12-10 17:56:27 -0800701 current = next;
702 lastNormal = nextNormal;
703 }
704
705 // wrap each strip around to beginning, creating degenerate tris to bridge strips
Chris Craik11a75672013-12-16 17:08:15 -0800706 buffer[currentAAOuterIndex++] = buffer[0];
707 buffer[currentAAOuterIndex++] = buffer[1];
708 buffer[currentAAOuterIndex++] = buffer[1];
Chris Craik65cd6122012-12-10 17:56:27 -0800709
Chris Craik11a75672013-12-16 17:08:15 -0800710 buffer[currentStrokeIndex++] = buffer[offset];
711 buffer[currentStrokeIndex++] = buffer[offset + 1];
712 buffer[currentStrokeIndex++] = buffer[offset + 1];
Chris Craik65cd6122012-12-10 17:56:27 -0800713
Chris Craik11a75672013-12-16 17:08:15 -0800714 buffer[currentAAInnerIndex++] = buffer[2 * offset];
715 buffer[currentAAInnerIndex++] = buffer[2 * offset + 1];
Chris Craik65cd6122012-12-10 17:56:27 -0800716 // don't need to create last degenerate tri
717
718 DEBUG_DUMP_ALPHA_BUFFER();
719}
720
721void PathTessellator::tessellatePath(const SkPath &path, const SkPaint* paint,
Chris Craikd6b65f62014-01-01 14:45:21 -0800722 const mat4& transform, VertexBuffer& vertexBuffer) {
Chris Craik65cd6122012-12-10 17:56:27 -0800723 ATRACE_CALL();
724
725 const PaintInfo paintInfo(paint, transform);
726
727 Vector<Vertex> tempVertices;
728 float threshInvScaleX = paintInfo.inverseScaleX;
729 float threshInvScaleY = paintInfo.inverseScaleY;
730 if (paintInfo.style == SkPaint::kStroke_Style) {
731 // alter the bezier recursion threshold values we calculate in order to compensate for
732 // expansion done after the path vertices are found
733 SkRect bounds = path.getBounds();
734 if (!bounds.isEmpty()) {
735 threshInvScaleX *= bounds.width() / (bounds.width() + paint->getStrokeWidth());
736 threshInvScaleY *= bounds.height() / (bounds.height() + paint->getStrokeWidth());
737 }
738 }
739
740 // force close if we're filling the path, since fill path expects closed perimeter.
741 bool forceClose = paintInfo.style != SkPaint::kStroke_Style;
ztenghui21ef8202015-05-28 16:06:28 -0700742 PathApproximationInfo approximationInfo(threshInvScaleX, threshInvScaleY,
743 OUTLINE_REFINE_THRESHOLD);
Chris Craik65cd6122012-12-10 17:56:27 -0800744 bool wasClosed = approximatePathOutlineVertices(path, forceClose,
ztenghui21ef8202015-05-28 16:06:28 -0700745 approximationInfo, tempVertices);
Chris Craik65cd6122012-12-10 17:56:27 -0800746
747 if (!tempVertices.size()) {
748 // path was empty, return without allocating vertex buffer
749 return;
750 }
751
752#if VERTEX_DEBUG
753 for (unsigned int i = 0; i < tempVertices.size(); i++) {
754 ALOGD("orig path: point at %f %f",
Romain Guy3380cfd2013-08-15 16:57:57 -0700755 tempVertices[i].x, tempVertices[i].y);
Chris Craik65cd6122012-12-10 17:56:27 -0800756 }
757#endif
758
759 if (paintInfo.style == SkPaint::kStroke_Style) {
760 if (!paintInfo.isAA) {
761 if (wasClosed) {
762 getStrokeVerticesFromPerimeter(paintInfo, tempVertices, vertexBuffer);
763 } else {
764 getStrokeVerticesFromUnclosedVertices(paintInfo, tempVertices, vertexBuffer);
765 }
766
767 } else {
768 if (wasClosed) {
769 getStrokeVerticesFromPerimeterAA(paintInfo, tempVertices, vertexBuffer);
770 } else {
771 getStrokeVerticesFromUnclosedVerticesAA(paintInfo, tempVertices, vertexBuffer);
772 }
773 }
774 } else {
775 // For kStrokeAndFill style, the path should be adjusted externally.
776 // It will be treated as a fill here.
777 if (!paintInfo.isAA) {
778 getFillVerticesFromPerimeter(tempVertices, vertexBuffer);
779 } else {
780 getFillVerticesFromPerimeterAA(paintInfo, tempVertices, vertexBuffer);
781 }
782 }
Chris Craik05f3d6e2014-06-02 16:27:04 -0700783
784 Rect bounds(path.getBounds());
785 paintInfo.expandBoundsForStroke(&bounds);
786 vertexBuffer.setBounds(bounds);
Chris Craik117bdbc2015-02-05 10:12:38 -0800787 vertexBuffer.setMeshFeatureFlags(paintInfo.isAA ? VertexBuffer::kAlpha : VertexBuffer::kNone);
Chris Craik65cd6122012-12-10 17:56:27 -0800788}
789
Chris Craik6d29c8d2013-05-08 18:35:44 -0700790template <class TYPE>
791static void instanceVertices(VertexBuffer& srcBuffer, VertexBuffer& dstBuffer,
Chris Craik05f3d6e2014-06-02 16:27:04 -0700792 const float* points, int count, Rect& bounds) {
Chris Craik6d29c8d2013-05-08 18:35:44 -0700793 bounds.set(points[0], points[1], points[0], points[1]);
794
795 int numPoints = count / 2;
796 int verticesPerPoint = srcBuffer.getVertexCount();
797 dstBuffer.alloc<TYPE>(numPoints * verticesPerPoint + (numPoints - 1) * 2);
798
799 for (int i = 0; i < count; i += 2) {
Chris Craikc93e45c2014-07-16 10:15:56 -0700800 bounds.expandToCoverVertex(points[i + 0], points[i + 1]);
Chris Craik6d29c8d2013-05-08 18:35:44 -0700801 dstBuffer.copyInto<TYPE>(srcBuffer, points[i + 0], points[i + 1]);
802 }
803 dstBuffer.createDegenerateSeparators<TYPE>(verticesPerPoint);
804}
805
Chris Craikd218a922014-01-02 17:13:34 -0800806void PathTessellator::tessellatePoints(const float* points, int count, const SkPaint* paint,
Chris Craik05f3d6e2014-06-02 16:27:04 -0700807 const mat4& transform, VertexBuffer& vertexBuffer) {
Chris Craik6d29c8d2013-05-08 18:35:44 -0700808 const PaintInfo paintInfo(paint, transform);
809
810 // determine point shape
811 SkPath path;
812 float radius = paintInfo.halfStrokeWidth;
Rob Tsukb7c26562014-11-03 16:29:03 -0800813 if (radius == 0.0f) radius = 0.5f;
Chris Craik6d29c8d2013-05-08 18:35:44 -0700814
815 if (paintInfo.cap == SkPaint::kRound_Cap) {
816 path.addCircle(0, 0, radius);
817 } else {
818 path.addRect(-radius, -radius, radius, radius);
819 }
820
821 // calculate outline
822 Vector<Vertex> outlineVertices;
ztenghui21ef8202015-05-28 16:06:28 -0700823 PathApproximationInfo approximationInfo(paintInfo.inverseScaleX, paintInfo.inverseScaleY,
824 OUTLINE_REFINE_THRESHOLD);
825 approximatePathOutlineVertices(path, true, approximationInfo, outlineVertices);
Chris Craik6d29c8d2013-05-08 18:35:44 -0700826
827 if (!outlineVertices.size()) return;
828
Chris Craik05f3d6e2014-06-02 16:27:04 -0700829 Rect bounds;
Chris Craik6d29c8d2013-05-08 18:35:44 -0700830 // tessellate, then duplicate outline across points
Chris Craik6d29c8d2013-05-08 18:35:44 -0700831 VertexBuffer tempBuffer;
832 if (!paintInfo.isAA) {
833 getFillVerticesFromPerimeter(outlineVertices, tempBuffer);
834 instanceVertices<Vertex>(tempBuffer, vertexBuffer, points, count, bounds);
835 } else {
Chris Craikf0a59072013-11-19 18:00:46 -0800836 // note: pass maxAlpha directly, since we want fill to be alpha modulated
837 getFillVerticesFromPerimeterAA(paintInfo, outlineVertices, tempBuffer, paintInfo.maxAlpha);
Chris Craik6d29c8d2013-05-08 18:35:44 -0700838 instanceVertices<AlphaVertex>(tempBuffer, vertexBuffer, points, count, bounds);
839 }
840
Chris Craikf0a59072013-11-19 18:00:46 -0800841 // expand bounds from vertex coords to pixel data
Chris Craik05f3d6e2014-06-02 16:27:04 -0700842 paintInfo.expandBoundsForStroke(&bounds);
843 vertexBuffer.setBounds(bounds);
Chris Craik117bdbc2015-02-05 10:12:38 -0800844 vertexBuffer.setMeshFeatureFlags(paintInfo.isAA ? VertexBuffer::kAlpha : VertexBuffer::kNone);
Chris Craik65cd6122012-12-10 17:56:27 -0800845}
846
Chris Craikd218a922014-01-02 17:13:34 -0800847void PathTessellator::tessellateLines(const float* points, int count, const SkPaint* paint,
Chris Craik05f3d6e2014-06-02 16:27:04 -0700848 const mat4& transform, VertexBuffer& vertexBuffer) {
Chris Craik65cd6122012-12-10 17:56:27 -0800849 ATRACE_CALL();
850 const PaintInfo paintInfo(paint, transform);
851
852 const int extra = paintInfo.capExtraDivisions();
853 int numLines = count / 4;
854 int lineAllocSize;
855 // pre-allocate space for lines in the buffer, and degenerate tris in between
856 if (paintInfo.isAA) {
857 lineAllocSize = 6 * (2) + 2 + 6 * extra;
858 vertexBuffer.alloc<AlphaVertex>(numLines * lineAllocSize + (numLines - 1) * 2);
859 } else {
860 lineAllocSize = 2 * ((2) + extra);
861 vertexBuffer.alloc<Vertex>(numLines * lineAllocSize + (numLines - 1) * 2);
862 }
863
864 Vector<Vertex> tempVertices;
865 tempVertices.push();
866 tempVertices.push();
867 Vertex* tempVerticesData = tempVertices.editArray();
Chris Craik05f3d6e2014-06-02 16:27:04 -0700868 Rect bounds;
Chris Craik65cd6122012-12-10 17:56:27 -0800869 bounds.set(points[0], points[1], points[0], points[1]);
870 for (int i = 0; i < count; i += 4) {
871 Vertex::set(&(tempVerticesData[0]), points[i + 0], points[i + 1]);
872 Vertex::set(&(tempVerticesData[1]), points[i + 2], points[i + 3]);
873
874 if (paintInfo.isAA) {
875 getStrokeVerticesFromUnclosedVerticesAA(paintInfo, tempVertices, vertexBuffer);
876 } else {
877 getStrokeVerticesFromUnclosedVertices(paintInfo, tempVertices, vertexBuffer);
878 }
879
880 // calculate bounds
Chris Craikc93e45c2014-07-16 10:15:56 -0700881 bounds.expandToCoverVertex(tempVerticesData[0].x, tempVerticesData[0].y);
882 bounds.expandToCoverVertex(tempVerticesData[1].x, tempVerticesData[1].y);
Chris Craik65cd6122012-12-10 17:56:27 -0800883 }
884
Chris Craik65cd6122012-12-10 17:56:27 -0800885 // since multiple objects tessellated into buffer, separate them with degen tris
886 if (paintInfo.isAA) {
887 vertexBuffer.createDegenerateSeparators<AlphaVertex>(lineAllocSize);
888 } else {
889 vertexBuffer.createDegenerateSeparators<Vertex>(lineAllocSize);
890 }
Chris Craikf0a59072013-11-19 18:00:46 -0800891
892 // expand bounds from vertex coords to pixel data
Chris Craik05f3d6e2014-06-02 16:27:04 -0700893 paintInfo.expandBoundsForStroke(&bounds);
894 vertexBuffer.setBounds(bounds);
Chris Craik117bdbc2015-02-05 10:12:38 -0800895 vertexBuffer.setMeshFeatureFlags(paintInfo.isAA ? VertexBuffer::kAlpha : VertexBuffer::kNone);
Chris Craik65cd6122012-12-10 17:56:27 -0800896}
897
898///////////////////////////////////////////////////////////////////////////////
899// Simple path line approximation
900///////////////////////////////////////////////////////////////////////////////
901
ztenghui21ef8202015-05-28 16:06:28 -0700902bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, float threshold,
Chris Craik15a07a22014-01-26 13:43:53 -0800903 Vector<Vertex>& outputVertices) {
ztenghui21ef8202015-05-28 16:06:28 -0700904 PathApproximationInfo approximationInfo(1.0f, 1.0f, threshold);
905 return approximatePathOutlineVertices(path, true, approximationInfo, outputVertices);
Chris Craik15a07a22014-01-26 13:43:53 -0800906}
907
Chris Craik65cd6122012-12-10 17:56:27 -0800908void pushToVector(Vector<Vertex>& vertices, float x, float y) {
909 // TODO: make this not yuck
910 vertices.push();
911 Vertex* newVertex = &(vertices.editArray()[vertices.size() - 1]);
912 Vertex::set(newVertex, x, y);
913}
914
Chris Craikfca52b752015-04-28 11:45:59 -0700915class ClockwiseEnforcer {
916public:
917 void addPoint(const SkPoint& point) {
918 double x = point.x();
919 double y = point.y();
920
921 if (initialized) {
922 sum += (x + lastX) * (y - lastY);
923 } else {
924 initialized = true;
925 }
926
927 lastX = x;
928 lastY = y;
929 }
930 void reverseVectorIfNotClockwise(Vector<Vertex>& vertices) {
931 if (sum < 0) {
932 // negative sum implies CounterClockwise
933 const int size = vertices.size();
934 for (int i = 0; i < size / 2; i++) {
935 Vertex tmp = vertices[i];
936 int k = size - 1 - i;
937 vertices.replaceAt(vertices[k], i);
938 vertices.replaceAt(tmp, k);
939 }
940 }
941 }
942private:
943 bool initialized = false;
Chris Craik469fb1e2015-05-28 15:33:40 -0700944 double lastX = 0;
945 double lastY = 0;
Chris Craikfca52b752015-04-28 11:45:59 -0700946 double sum = 0;
947};
948
Chris Craik65cd6122012-12-10 17:56:27 -0800949bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool forceClose,
ztenghui21ef8202015-05-28 16:06:28 -0700950 const PathApproximationInfo& approximationInfo, Vector<Vertex>& outputVertices) {
Chris Craik65cd6122012-12-10 17:56:27 -0800951 ATRACE_CALL();
952
953 // TODO: to support joins other than sharp miter, join vertices should be labelled in the
954 // perimeter, or resolved into more vertices. Reconsider forceClose-ing in that case.
955 SkPath::Iter iter(path, forceClose);
956 SkPoint pts[4];
957 SkPath::Verb v;
Chris Craikfca52b752015-04-28 11:45:59 -0700958 ClockwiseEnforcer clockwiseEnforcer;
Chris Craik65cd6122012-12-10 17:56:27 -0800959 while (SkPath::kDone_Verb != (v = iter.next(pts))) {
960 switch (v) {
961 case SkPath::kMove_Verb:
962 pushToVector(outputVertices, pts[0].x(), pts[0].y());
963 ALOGV("Move to pos %f %f", pts[0].x(), pts[0].y());
Chris Craikfca52b752015-04-28 11:45:59 -0700964 clockwiseEnforcer.addPoint(pts[0]);
Chris Craik65cd6122012-12-10 17:56:27 -0800965 break;
966 case SkPath::kClose_Verb:
967 ALOGV("Close at pos %f %f", pts[0].x(), pts[0].y());
Chris Craikfca52b752015-04-28 11:45:59 -0700968 clockwiseEnforcer.addPoint(pts[0]);
Chris Craik65cd6122012-12-10 17:56:27 -0800969 break;
970 case SkPath::kLine_Verb:
971 ALOGV("kLine_Verb %f %f -> %f %f", pts[0].x(), pts[0].y(), pts[1].x(), pts[1].y());
972 pushToVector(outputVertices, pts[1].x(), pts[1].y());
Chris Craikfca52b752015-04-28 11:45:59 -0700973 clockwiseEnforcer.addPoint(pts[1]);
Chris Craik65cd6122012-12-10 17:56:27 -0800974 break;
975 case SkPath::kQuad_Verb:
976 ALOGV("kQuad_Verb");
977 recursiveQuadraticBezierVertices(
978 pts[0].x(), pts[0].y(),
979 pts[2].x(), pts[2].y(),
980 pts[1].x(), pts[1].y(),
ztenghui21ef8202015-05-28 16:06:28 -0700981 approximationInfo, outputVertices);
Chris Craikfca52b752015-04-28 11:45:59 -0700982 clockwiseEnforcer.addPoint(pts[1]);
983 clockwiseEnforcer.addPoint(pts[2]);
Chris Craik65cd6122012-12-10 17:56:27 -0800984 break;
985 case SkPath::kCubic_Verb:
986 ALOGV("kCubic_Verb");
987 recursiveCubicBezierVertices(
988 pts[0].x(), pts[0].y(),
989 pts[1].x(), pts[1].y(),
990 pts[3].x(), pts[3].y(),
991 pts[2].x(), pts[2].y(),
ztenghui21ef8202015-05-28 16:06:28 -0700992 approximationInfo, outputVertices);
Chris Craikfca52b752015-04-28 11:45:59 -0700993 clockwiseEnforcer.addPoint(pts[1]);
994 clockwiseEnforcer.addPoint(pts[2]);
995 clockwiseEnforcer.addPoint(pts[3]);
Chris Craik65cd6122012-12-10 17:56:27 -0800996 break;
Derek Sollenbergerb0772a62015-03-16 14:35:55 -0400997 case SkPath::kConic_Verb: {
998 ALOGV("kConic_Verb");
999 SkAutoConicToQuads converter;
1000 const SkPoint* quads = converter.computeQuads(pts, iter.conicWeight(),
ztenghui21ef8202015-05-28 16:06:28 -07001001 approximationInfo.thresholdForConicQuads);
Derek Sollenbergerb0772a62015-03-16 14:35:55 -04001002 for (int i = 0; i < converter.countQuads(); ++i) {
1003 const int offset = 2 * i;
1004 recursiveQuadraticBezierVertices(
1005 quads[offset].x(), quads[offset].y(),
1006 quads[offset+2].x(), quads[offset+2].y(),
1007 quads[offset+1].x(), quads[offset+1].y(),
ztenghui21ef8202015-05-28 16:06:28 -07001008 approximationInfo, outputVertices);
Derek Sollenbergerb0772a62015-03-16 14:35:55 -04001009 }
Chris Craikfca52b752015-04-28 11:45:59 -07001010 clockwiseEnforcer.addPoint(pts[1]);
1011 clockwiseEnforcer.addPoint(pts[2]);
Derek Sollenbergerb0772a62015-03-16 14:35:55 -04001012 break;
1013 }
Chris Craik65cd6122012-12-10 17:56:27 -08001014 default:
1015 break;
1016 }
1017 }
1018
Chris Craikfca52b752015-04-28 11:45:59 -07001019 bool wasClosed = false;
Chris Craik65cd6122012-12-10 17:56:27 -08001020 int size = outputVertices.size();
Romain Guy3380cfd2013-08-15 16:57:57 -07001021 if (size >= 2 && outputVertices[0].x == outputVertices[size - 1].x &&
1022 outputVertices[0].y == outputVertices[size - 1].y) {
Chris Craik65cd6122012-12-10 17:56:27 -08001023 outputVertices.pop();
Chris Craikfca52b752015-04-28 11:45:59 -07001024 wasClosed = true;
Chris Craik65cd6122012-12-10 17:56:27 -08001025 }
Chris Craikfca52b752015-04-28 11:45:59 -07001026
1027 // ensure output vector is clockwise
1028 clockwiseEnforcer.reverseVectorIfNotClockwise(outputVertices);
1029 return wasClosed;
Chris Craik65cd6122012-12-10 17:56:27 -08001030}
1031
1032///////////////////////////////////////////////////////////////////////////////
1033// Bezier approximation
ztenghui21ef8202015-05-28 16:06:28 -07001034//
1035// All the inputs and outputs here are in path coordinates.
1036// We convert the error threshold from screen coordinates into path coordinates.
Chris Craik65cd6122012-12-10 17:56:27 -08001037///////////////////////////////////////////////////////////////////////////////
1038
ztenghui21ef8202015-05-28 16:06:28 -07001039// Get a threshold in path coordinates, by scaling the thresholdSquared from screen coordinates.
1040// TODO: Document the math behind this algorithm.
1041static inline float getThreshold(const PathApproximationInfo& info, float dx, float dy) {
1042 // multiplying by sqrInvScaleY/X equivalent to multiplying in dimensional scale factors
1043 float scale = (dx * dx * info.sqrInvScaleY + dy * dy * info.sqrInvScaleX);
1044 return info.thresholdSquared * scale;
1045}
1046
Chris Craik65cd6122012-12-10 17:56:27 -08001047void PathTessellator::recursiveCubicBezierVertices(
1048 float p1x, float p1y, float c1x, float c1y,
1049 float p2x, float p2y, float c2x, float c2y,
ztenghui21ef8202015-05-28 16:06:28 -07001050 const PathApproximationInfo& approximationInfo,
Chris Craik9c3dd622014-06-11 17:24:51 -07001051 Vector<Vertex>& outputVertices, int depth) {
Chris Craik65cd6122012-12-10 17:56:27 -08001052 float dx = p2x - p1x;
1053 float dy = p2y - p1y;
1054 float d1 = fabs((c1x - p2x) * dy - (c1y - p2y) * dx);
1055 float d2 = fabs((c2x - p2x) * dy - (c2y - p2y) * dx);
1056 float d = d1 + d2;
1057
Chris Craik74cf7e62014-08-07 14:34:46 -07001058 if (depth >= MAX_DEPTH
ztenghui21ef8202015-05-28 16:06:28 -07001059 || d * d <= getThreshold(approximationInfo, dx, dy)) {
Chris Craik65cd6122012-12-10 17:56:27 -08001060 // below thresh, draw line by adding endpoint
1061 pushToVector(outputVertices, p2x, p2y);
1062 } else {
1063 float p1c1x = (p1x + c1x) * 0.5f;
1064 float p1c1y = (p1y + c1y) * 0.5f;
1065 float p2c2x = (p2x + c2x) * 0.5f;
1066 float p2c2y = (p2y + c2y) * 0.5f;
1067
1068 float c1c2x = (c1x + c2x) * 0.5f;
1069 float c1c2y = (c1y + c2y) * 0.5f;
1070
1071 float p1c1c2x = (p1c1x + c1c2x) * 0.5f;
1072 float p1c1c2y = (p1c1y + c1c2y) * 0.5f;
1073
1074 float p2c1c2x = (p2c2x + c1c2x) * 0.5f;
1075 float p2c1c2y = (p2c2y + c1c2y) * 0.5f;
1076
1077 float mx = (p1c1c2x + p2c1c2x) * 0.5f;
1078 float my = (p1c1c2y + p2c1c2y) * 0.5f;
1079
1080 recursiveCubicBezierVertices(
1081 p1x, p1y, p1c1x, p1c1y,
1082 mx, my, p1c1c2x, p1c1c2y,
ztenghui21ef8202015-05-28 16:06:28 -07001083 approximationInfo, outputVertices, depth + 1);
Chris Craik65cd6122012-12-10 17:56:27 -08001084 recursiveCubicBezierVertices(
1085 mx, my, p2c1c2x, p2c1c2y,
1086 p2x, p2y, p2c2x, p2c2y,
ztenghui21ef8202015-05-28 16:06:28 -07001087 approximationInfo, outputVertices, depth + 1);
Chris Craik65cd6122012-12-10 17:56:27 -08001088 }
1089}
1090
1091void PathTessellator::recursiveQuadraticBezierVertices(
1092 float ax, float ay,
1093 float bx, float by,
1094 float cx, float cy,
ztenghui21ef8202015-05-28 16:06:28 -07001095 const PathApproximationInfo& approximationInfo,
Chris Craik9c3dd622014-06-11 17:24:51 -07001096 Vector<Vertex>& outputVertices, int depth) {
Chris Craik65cd6122012-12-10 17:56:27 -08001097 float dx = bx - ax;
1098 float dy = by - ay;
ztenghui21ef8202015-05-28 16:06:28 -07001099 // d is the cross product of vector (B-A) and (C-B).
Chris Craik65cd6122012-12-10 17:56:27 -08001100 float d = (cx - bx) * dy - (cy - by) * dx;
1101
Chris Craik74cf7e62014-08-07 14:34:46 -07001102 if (depth >= MAX_DEPTH
ztenghui21ef8202015-05-28 16:06:28 -07001103 || d * d <= getThreshold(approximationInfo, dx, dy)) {
Chris Craik65cd6122012-12-10 17:56:27 -08001104 // below thresh, draw line by adding endpoint
1105 pushToVector(outputVertices, bx, by);
1106 } else {
1107 float acx = (ax + cx) * 0.5f;
1108 float bcx = (bx + cx) * 0.5f;
1109 float acy = (ay + cy) * 0.5f;
1110 float bcy = (by + cy) * 0.5f;
1111
1112 // midpoint
1113 float mx = (acx + bcx) * 0.5f;
1114 float my = (acy + bcy) * 0.5f;
1115
1116 recursiveQuadraticBezierVertices(ax, ay, mx, my, acx, acy,
ztenghui21ef8202015-05-28 16:06:28 -07001117 approximationInfo, outputVertices, depth + 1);
Chris Craik65cd6122012-12-10 17:56:27 -08001118 recursiveQuadraticBezierVertices(mx, my, bx, by, bcx, bcy,
ztenghui21ef8202015-05-28 16:06:28 -07001119 approximationInfo, outputVertices, depth + 1);
Chris Craik65cd6122012-12-10 17:56:27 -08001120 }
1121}
1122
1123}; // namespace uirenderer
1124}; // namespace android