blob: e30ac19e430a3d60b9f3eb071fcf8dbf4be28cc7 [file] [log] [blame]
Chris Craik65cd6122012-12-10 17:56:27 -08001/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Chris Craikf0a59072013-11-19 18:00:46 -080017#define LOG_TAG "OpenGLRenderer"
Chris Craik65cd6122012-12-10 17:56:27 -080018#define LOG_NDEBUG 1
Chris Craik87f9df82014-03-07 14:34:42 -080019#define ATRACE_TAG ATRACE_TAG_VIEW
Chris Craik65cd6122012-12-10 17:56:27 -080020
21#define VERTEX_DEBUG 0
22
23#if VERTEX_DEBUG
24#define DEBUG_DUMP_ALPHA_BUFFER() \
25 for (unsigned int i = 0; i < vertexBuffer.getSize(); i++) { \
26 ALOGD("point %d at %f %f, alpha %f", \
Romain Guy3380cfd2013-08-15 16:57:57 -070027 i, buffer[i].x, buffer[i].y, buffer[i].alpha); \
Chris Craik65cd6122012-12-10 17:56:27 -080028 }
29#define DEBUG_DUMP_BUFFER() \
30 for (unsigned int i = 0; i < vertexBuffer.getSize(); i++) { \
Romain Guy3380cfd2013-08-15 16:57:57 -070031 ALOGD("point %d at %f %f", i, buffer[i].x, buffer[i].y); \
Chris Craik65cd6122012-12-10 17:56:27 -080032 }
33#else
34#define DEBUG_DUMP_ALPHA_BUFFER()
35#define DEBUG_DUMP_BUFFER()
36#endif
37
38#include <SkPath.h>
39#include <SkPaint.h>
40
41#include <stdlib.h>
42#include <stdint.h>
43#include <sys/types.h>
44
45#include <utils/Log.h>
46#include <utils/Trace.h>
47
48#include "PathTessellator.h"
49#include "Matrix.h"
50#include "Vector.h"
51#include "Vertex.h"
Chris Craik74cf7e62014-08-07 14:34:46 -070052#include "utils/MathUtils.h"
Chris Craik65cd6122012-12-10 17:56:27 -080053
54namespace android {
55namespace uirenderer {
56
Chris Craik15a07a22014-01-26 13:43:53 -080057#define OUTLINE_REFINE_THRESHOLD_SQUARED (0.5f * 0.5f)
Chris Craik65cd6122012-12-10 17:56:27 -080058#define ROUND_CAP_THRESH 0.25f
59#define PI 3.1415926535897932f
Chris Craik74cf7e62014-08-07 14:34:46 -070060#define MAX_DEPTH 15
Chris Craik65cd6122012-12-10 17:56:27 -080061
Chris Craik74cf7e62014-08-07 14:34:46 -070062/**
63 * Extracts the x and y scale from the transform as positive values, and clamps them
64 */
Chris Craik05f3d6e2014-06-02 16:27:04 -070065void PathTessellator::extractTessellationScales(const Matrix4& transform,
66 float* scaleX, float* scaleY) {
Chris Craikfe02b4b2014-06-16 16:34:29 -070067 if (CC_LIKELY(transform.isPureTranslate())) {
68 *scaleX = 1.0f;
69 *scaleY = 1.0f;
70 } else {
Chris Craik05f3d6e2014-06-02 16:27:04 -070071 float m00 = transform.data[Matrix4::kScaleX];
72 float m01 = transform.data[Matrix4::kSkewY];
73 float m10 = transform.data[Matrix4::kSkewX];
74 float m11 = transform.data[Matrix4::kScaleY];
Chris Craik74cf7e62014-08-07 14:34:46 -070075 *scaleX = MathUtils::clampTessellationScale(sqrt(m00 * m00 + m01 * m01));
76 *scaleY = MathUtils::clampTessellationScale(sqrt(m10 * m10 + m11 * m11));
Chris Craik65cd6122012-12-10 17:56:27 -080077 }
78}
79
Chris Craik65cd6122012-12-10 17:56:27 -080080/**
81 * Produces a pseudo-normal for a vertex, given the normals of the two incoming lines. If the offset
82 * from each vertex in a perimeter is calculated, the resultant lines connecting the offset vertices
83 * will be offset by 1.0
84 *
85 * Note that we can't add and normalize the two vectors, that would result in a rectangle having an
86 * offset of (sqrt(2)/2, sqrt(2)/2) at each corner, instead of (1, 1)
87 *
88 * NOTE: assumes angles between normals 90 degrees or less
89 */
John Reck1aa5d2d2014-07-24 13:38:28 -070090inline static Vector2 totalOffsetFromNormals(const Vector2& normalA, const Vector2& normalB) {
Chris Craik65cd6122012-12-10 17:56:27 -080091 return (normalA + normalB) / (1 + fabs(normalA.dot(normalB)));
92}
93
94/**
95 * Structure used for storing useful information about the SkPaint and scale used for tessellating
96 */
97struct PaintInfo {
98public:
Chris Craikd6b65f62014-01-01 14:45:21 -080099 PaintInfo(const SkPaint* paint, const mat4& transform) :
Chris Craik65cd6122012-12-10 17:56:27 -0800100 style(paint->getStyle()), cap(paint->getStrokeCap()), isAA(paint->isAntiAlias()),
Chris Craik65cd6122012-12-10 17:56:27 -0800101 halfStrokeWidth(paint->getStrokeWidth() * 0.5f), maxAlpha(1.0f) {
102 // compute inverse scales
Chris Craikfe02b4b2014-06-16 16:34:29 -0700103 if (CC_LIKELY(transform.isPureTranslate())) {
104 inverseScaleX = 1.0f;
105 inverseScaleY = 1.0f;
106 } else {
Chris Craik05f3d6e2014-06-02 16:27:04 -0700107 float scaleX, scaleY;
108 PathTessellator::extractTessellationScales(transform, &scaleX, &scaleY);
Chris Craik74cf7e62014-08-07 14:34:46 -0700109 inverseScaleX = 1.0f / scaleX;
110 inverseScaleY = 1.0f / scaleY;
Chris Craik65cd6122012-12-10 17:56:27 -0800111 }
112
113 if (isAA && halfStrokeWidth != 0 && inverseScaleX == inverseScaleY &&
Chris Craik19a390b2013-02-27 15:43:26 -0800114 2 * halfStrokeWidth < inverseScaleX) {
Chris Craik05f3d6e2014-06-02 16:27:04 -0700115 // AA, with non-hairline stroke, width < 1 pixel. Scale alpha and treat as hairline.
Chris Craik65cd6122012-12-10 17:56:27 -0800116 maxAlpha *= (2 * halfStrokeWidth) / inverseScaleX;
117 halfStrokeWidth = 0.0f;
118 }
119 }
120
121 SkPaint::Style style;
122 SkPaint::Cap cap;
123 bool isAA;
124 float inverseScaleX;
125 float inverseScaleY;
126 float halfStrokeWidth;
127 float maxAlpha;
128
John Reck1aa5d2d2014-07-24 13:38:28 -0700129 inline void scaleOffsetForStrokeWidth(Vector2& offset) const {
Chris Craik65cd6122012-12-10 17:56:27 -0800130 if (halfStrokeWidth == 0.0f) {
131 // hairline - compensate for scale
132 offset.x *= 0.5f * inverseScaleX;
133 offset.y *= 0.5f * inverseScaleY;
134 } else {
135 offset *= halfStrokeWidth;
136 }
137 }
138
139 /**
140 * NOTE: the input will not always be a normal, especially for sharp edges - it should be the
141 * result of totalOffsetFromNormals (see documentation there)
142 */
John Reck1aa5d2d2014-07-24 13:38:28 -0700143 inline Vector2 deriveAAOffset(const Vector2& offset) const {
144 return (Vector2){offset.x * 0.5f * inverseScaleX, offset.y * 0.5f * inverseScaleY};
Chris Craik65cd6122012-12-10 17:56:27 -0800145 }
146
147 /**
148 * Returns the number of cap divisions beyond the minimum 2 (kButt_Cap/kSquareCap will return 0)
149 * Should only be used when stroking and drawing caps
150 */
151 inline int capExtraDivisions() const {
152 if (cap == SkPaint::kRound_Cap) {
153 if (halfStrokeWidth == 0.0f) return 2;
154
155 // ROUND_CAP_THRESH is the maximum error for polygonal approximation of the round cap
156 const float errConst = (-ROUND_CAP_THRESH / halfStrokeWidth + 1);
157 const float targetCosVal = 2 * errConst * errConst - 1;
158 int neededDivisions = (int)(ceilf(PI / acos(targetCosVal)/2)) * 2;
159 return neededDivisions;
160 }
161 return 0;
162 }
Chris Craikf0a59072013-11-19 18:00:46 -0800163
164 /**
165 * Outset the bounds of point data (for line endpoints or points) to account for AA stroke
166 * geometry.
167 */
Chris Craik05f3d6e2014-06-02 16:27:04 -0700168 void expandBoundsForStroke(Rect* bounds) const {
Chris Craikf0a59072013-11-19 18:00:46 -0800169 float outset = halfStrokeWidth;
170 if (outset == 0) outset = 0.5f;
Chris Craik05f3d6e2014-06-02 16:27:04 -0700171 bounds->outset(outset * inverseScaleX + Vertex::GeometryFudgeFactor(),
Chris Craik564acf72014-01-02 16:46:18 -0800172 outset * inverseScaleY + Vertex::GeometryFudgeFactor());
Chris Craikf0a59072013-11-19 18:00:46 -0800173 }
Chris Craik65cd6122012-12-10 17:56:27 -0800174};
175
176void getFillVerticesFromPerimeter(const Vector<Vertex>& perimeter, VertexBuffer& vertexBuffer) {
177 Vertex* buffer = vertexBuffer.alloc<Vertex>(perimeter.size());
178
179 int currentIndex = 0;
180 // zig zag between all previous points on the inside of the hull to create a
181 // triangle strip that fills the hull
182 int srcAindex = 0;
183 int srcBindex = perimeter.size() - 1;
184 while (srcAindex <= srcBindex) {
Chris Craik11a75672013-12-16 17:08:15 -0800185 buffer[currentIndex++] = perimeter[srcAindex];
Chris Craik65cd6122012-12-10 17:56:27 -0800186 if (srcAindex == srcBindex) break;
Chris Craik11a75672013-12-16 17:08:15 -0800187 buffer[currentIndex++] = perimeter[srcBindex];
Chris Craik65cd6122012-12-10 17:56:27 -0800188 srcAindex++;
189 srcBindex--;
190 }
191}
192
193/*
194 * Fills a vertexBuffer with non-alpha vertices, zig-zagging at each perimeter point to create a
195 * tri-strip as wide as the stroke.
196 *
197 * Uses an additional 2 vertices at the end to wrap around, closing the tri-strip
198 * (for a total of perimeter.size() * 2 + 2 vertices)
199 */
200void getStrokeVerticesFromPerimeter(const PaintInfo& paintInfo, const Vector<Vertex>& perimeter,
201 VertexBuffer& vertexBuffer) {
202 Vertex* buffer = vertexBuffer.alloc<Vertex>(perimeter.size() * 2 + 2);
203
204 int currentIndex = 0;
205 const Vertex* last = &(perimeter[perimeter.size() - 1]);
206 const Vertex* current = &(perimeter[0]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700207 Vector2 lastNormal = {current->y - last->y, last->x - current->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800208 lastNormal.normalize();
209 for (unsigned int i = 0; i < perimeter.size(); i++) {
210 const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700211 Vector2 nextNormal = {next->y - current->y, current->x - next->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800212 nextNormal.normalize();
213
John Reck1aa5d2d2014-07-24 13:38:28 -0700214 Vector2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
Chris Craik65cd6122012-12-10 17:56:27 -0800215 paintInfo.scaleOffsetForStrokeWidth(totalOffset);
216
217 Vertex::set(&buffer[currentIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700218 current->x + totalOffset.x,
219 current->y + totalOffset.y);
Chris Craik65cd6122012-12-10 17:56:27 -0800220
221 Vertex::set(&buffer[currentIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700222 current->x - totalOffset.x,
223 current->y - totalOffset.y);
Chris Craik65cd6122012-12-10 17:56:27 -0800224
225 last = current;
226 current = next;
227 lastNormal = nextNormal;
228 }
229
230 // wrap around to beginning
Chris Craik11a75672013-12-16 17:08:15 -0800231 buffer[currentIndex++] = buffer[0];
232 buffer[currentIndex++] = buffer[1];
Chris Craik65cd6122012-12-10 17:56:27 -0800233
234 DEBUG_DUMP_BUFFER();
235}
236
Chris Craik6d29c8d2013-05-08 18:35:44 -0700237static inline void storeBeginEnd(const PaintInfo& paintInfo, const Vertex& center,
John Reck1aa5d2d2014-07-24 13:38:28 -0700238 const Vector2& normal, Vertex* buffer, int& currentIndex, bool begin) {
239 Vector2 strokeOffset = normal;
Chris Craik6d29c8d2013-05-08 18:35:44 -0700240 paintInfo.scaleOffsetForStrokeWidth(strokeOffset);
241
John Reck1aa5d2d2014-07-24 13:38:28 -0700242 Vector2 referencePoint = {center.x, center.y};
Chris Craik6d29c8d2013-05-08 18:35:44 -0700243 if (paintInfo.cap == SkPaint::kSquare_Cap) {
John Reck1aa5d2d2014-07-24 13:38:28 -0700244 Vector2 rotated = {-strokeOffset.y, strokeOffset.x};
245 referencePoint += rotated * (begin ? -1 : 1);
Chris Craik6d29c8d2013-05-08 18:35:44 -0700246 }
247
248 Vertex::set(&buffer[currentIndex++], referencePoint + strokeOffset);
249 Vertex::set(&buffer[currentIndex++], referencePoint - strokeOffset);
250}
251
Chris Craik65cd6122012-12-10 17:56:27 -0800252/**
253 * Fills a vertexBuffer with non-alpha vertices similar to getStrokeVerticesFromPerimeter, except:
254 *
255 * 1 - Doesn't need to wrap around, since the input vertices are unclosed
256 *
257 * 2 - can zig-zag across 'extra' vertices at either end, to create round caps
258 */
259void getStrokeVerticesFromUnclosedVertices(const PaintInfo& paintInfo,
260 const Vector<Vertex>& vertices, VertexBuffer& vertexBuffer) {
261 const int extra = paintInfo.capExtraDivisions();
262 const int allocSize = (vertices.size() + extra) * 2;
Chris Craik65cd6122012-12-10 17:56:27 -0800263 Vertex* buffer = vertexBuffer.alloc<Vertex>(allocSize);
264
Chris Craik6d29c8d2013-05-08 18:35:44 -0700265 const int lastIndex = vertices.size() - 1;
Chris Craik65cd6122012-12-10 17:56:27 -0800266 if (extra > 0) {
267 // tessellate both round caps
Chris Craik65cd6122012-12-10 17:56:27 -0800268 float beginTheta = atan2(
Romain Guy3380cfd2013-08-15 16:57:57 -0700269 - (vertices[0].x - vertices[1].x),
270 vertices[0].y - vertices[1].y);
Chris Craik65cd6122012-12-10 17:56:27 -0800271 float endTheta = atan2(
Romain Guy3380cfd2013-08-15 16:57:57 -0700272 - (vertices[lastIndex].x - vertices[lastIndex - 1].x),
273 vertices[lastIndex].y - vertices[lastIndex - 1].y);
Chris Craik65cd6122012-12-10 17:56:27 -0800274 const float dTheta = PI / (extra + 1);
275 const float radialScale = 2.0f / (1 + cos(dTheta));
276
277 int capOffset;
278 for (int i = 0; i < extra; i++) {
279 if (i < extra / 2) {
280 capOffset = extra - 2 * i - 1;
281 } else {
282 capOffset = 2 * i - extra;
283 }
284
285 beginTheta += dTheta;
John Reck1aa5d2d2014-07-24 13:38:28 -0700286 Vector2 beginRadialOffset = {cos(beginTheta), sin(beginTheta)};
Chris Craik65cd6122012-12-10 17:56:27 -0800287 paintInfo.scaleOffsetForStrokeWidth(beginRadialOffset);
288 Vertex::set(&buffer[capOffset],
Romain Guy3380cfd2013-08-15 16:57:57 -0700289 vertices[0].x + beginRadialOffset.x,
290 vertices[0].y + beginRadialOffset.y);
Chris Craik65cd6122012-12-10 17:56:27 -0800291
292 endTheta += dTheta;
John Reck1aa5d2d2014-07-24 13:38:28 -0700293 Vector2 endRadialOffset = {cos(endTheta), sin(endTheta)};
Chris Craik65cd6122012-12-10 17:56:27 -0800294 paintInfo.scaleOffsetForStrokeWidth(endRadialOffset);
295 Vertex::set(&buffer[allocSize - 1 - capOffset],
Romain Guy3380cfd2013-08-15 16:57:57 -0700296 vertices[lastIndex].x + endRadialOffset.x,
297 vertices[lastIndex].y + endRadialOffset.y);
Chris Craik65cd6122012-12-10 17:56:27 -0800298 }
299 }
300
301 int currentIndex = extra;
Chris Craik6d29c8d2013-05-08 18:35:44 -0700302 const Vertex* last = &(vertices[0]);
303 const Vertex* current = &(vertices[1]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700304 Vector2 lastNormal = {current->y - last->y, last->x - current->x};
Chris Craik6d29c8d2013-05-08 18:35:44 -0700305 lastNormal.normalize();
306
307 storeBeginEnd(paintInfo, vertices[0], lastNormal, buffer, currentIndex, true);
308
309 for (unsigned int i = 1; i < vertices.size() - 1; i++) {
Chris Craik65cd6122012-12-10 17:56:27 -0800310 const Vertex* next = &(vertices[i + 1]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700311 Vector2 nextNormal = {next->y - current->y, current->x - next->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800312 nextNormal.normalize();
313
John Reck1aa5d2d2014-07-24 13:38:28 -0700314 Vector2 strokeOffset = totalOffsetFromNormals(lastNormal, nextNormal);
Chris Craik6d29c8d2013-05-08 18:35:44 -0700315 paintInfo.scaleOffsetForStrokeWidth(strokeOffset);
Chris Craik65cd6122012-12-10 17:56:27 -0800316
John Reck1aa5d2d2014-07-24 13:38:28 -0700317 Vector2 center = {current->x, current->y};
Chris Craik6d29c8d2013-05-08 18:35:44 -0700318 Vertex::set(&buffer[currentIndex++], center + strokeOffset);
319 Vertex::set(&buffer[currentIndex++], center - strokeOffset);
Chris Craik65cd6122012-12-10 17:56:27 -0800320
321 current = next;
322 lastNormal = nextNormal;
323 }
324
Chris Craik6d29c8d2013-05-08 18:35:44 -0700325 storeBeginEnd(paintInfo, vertices[lastIndex], lastNormal, buffer, currentIndex, false);
Chris Craik65cd6122012-12-10 17:56:27 -0800326
327 DEBUG_DUMP_BUFFER();
328}
329
330/**
331 * Populates a vertexBuffer with AlphaVertices to create an anti-aliased fill shape tessellation
Chris Craik6d29c8d2013-05-08 18:35:44 -0700332 *
Chris Craik65cd6122012-12-10 17:56:27 -0800333 * 1 - create the AA perimeter of unit width, by zig-zagging at each point around the perimeter of
334 * the shape (using 2 * perimeter.size() vertices)
335 *
336 * 2 - wrap around to the beginning to complete the perimeter (2 vertices)
337 *
338 * 3 - zig zag back and forth inside the shape to fill it (using perimeter.size() vertices)
339 */
340void getFillVerticesFromPerimeterAA(const PaintInfo& paintInfo, const Vector<Vertex>& perimeter,
Chris Craikf0a59072013-11-19 18:00:46 -0800341 VertexBuffer& vertexBuffer, float maxAlpha = 1.0f) {
Chris Craik65cd6122012-12-10 17:56:27 -0800342 AlphaVertex* buffer = vertexBuffer.alloc<AlphaVertex>(perimeter.size() * 3 + 2);
343
344 // generate alpha points - fill Alpha vertex gaps in between each point with
345 // alpha 0 vertex, offset by a scaled normal.
346 int currentIndex = 0;
347 const Vertex* last = &(perimeter[perimeter.size() - 1]);
348 const Vertex* current = &(perimeter[0]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700349 Vector2 lastNormal = {current->y - last->y, last->x - current->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800350 lastNormal.normalize();
351 for (unsigned int i = 0; i < perimeter.size(); i++) {
352 const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700353 Vector2 nextNormal = {next->y - current->y, current->x - next->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800354 nextNormal.normalize();
355
356 // AA point offset from original point is that point's normal, such that each side is offset
357 // by .5 pixels
John Reck1aa5d2d2014-07-24 13:38:28 -0700358 Vector2 totalOffset = paintInfo.deriveAAOffset(totalOffsetFromNormals(lastNormal, nextNormal));
Chris Craik65cd6122012-12-10 17:56:27 -0800359
360 AlphaVertex::set(&buffer[currentIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700361 current->x + totalOffset.x,
362 current->y + totalOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800363 0.0f);
364 AlphaVertex::set(&buffer[currentIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700365 current->x - totalOffset.x,
366 current->y - totalOffset.y,
Chris Craikf0a59072013-11-19 18:00:46 -0800367 maxAlpha);
Chris Craik65cd6122012-12-10 17:56:27 -0800368
369 last = current;
370 current = next;
371 lastNormal = nextNormal;
372 }
373
374 // wrap around to beginning
Chris Craik11a75672013-12-16 17:08:15 -0800375 buffer[currentIndex++] = buffer[0];
376 buffer[currentIndex++] = buffer[1];
Chris Craik65cd6122012-12-10 17:56:27 -0800377
378 // zig zag between all previous points on the inside of the hull to create a
379 // triangle strip that fills the hull, repeating the first inner point to
380 // create degenerate tris to start inside path
381 int srcAindex = 0;
382 int srcBindex = perimeter.size() - 1;
383 while (srcAindex <= srcBindex) {
Chris Craik11a75672013-12-16 17:08:15 -0800384 buffer[currentIndex++] = buffer[srcAindex * 2 + 1];
Chris Craik65cd6122012-12-10 17:56:27 -0800385 if (srcAindex == srcBindex) break;
Chris Craik11a75672013-12-16 17:08:15 -0800386 buffer[currentIndex++] = buffer[srcBindex * 2 + 1];
Chris Craik65cd6122012-12-10 17:56:27 -0800387 srcAindex++;
388 srcBindex--;
389 }
390
391 DEBUG_DUMP_BUFFER();
392}
393
394/**
395 * Stores geometry for a single, AA-perimeter (potentially rounded) cap
396 *
397 * For explanation of constants and general methodoloyg, see comments for
398 * getStrokeVerticesFromUnclosedVerticesAA() below.
399 */
Chris Craik6d29c8d2013-05-08 18:35:44 -0700400inline static void storeCapAA(const PaintInfo& paintInfo, const Vector<Vertex>& vertices,
John Reck1aa5d2d2014-07-24 13:38:28 -0700401 AlphaVertex* buffer, bool isFirst, Vector2 normal, int offset) {
Chris Craik65cd6122012-12-10 17:56:27 -0800402 const int extra = paintInfo.capExtraDivisions();
403 const int extraOffset = (extra + 1) / 2;
404 const int capIndex = isFirst
405 ? 2 * offset + 6 + 2 * (extra + extraOffset)
406 : offset + 2 + 2 * extraOffset;
407 if (isFirst) normal *= -1;
408
409 // TODO: this normal should be scaled by radialScale if extra != 0, see totalOffsetFromNormals()
John Reck1aa5d2d2014-07-24 13:38:28 -0700410 Vector2 AAOffset = paintInfo.deriveAAOffset(normal);
Chris Craik65cd6122012-12-10 17:56:27 -0800411
John Reck1aa5d2d2014-07-24 13:38:28 -0700412 Vector2 strokeOffset = normal;
Chris Craik65cd6122012-12-10 17:56:27 -0800413 paintInfo.scaleOffsetForStrokeWidth(strokeOffset);
John Reck1aa5d2d2014-07-24 13:38:28 -0700414 Vector2 outerOffset = strokeOffset + AAOffset;
415 Vector2 innerOffset = strokeOffset - AAOffset;
Chris Craik65cd6122012-12-10 17:56:27 -0800416
John Reck1aa5d2d2014-07-24 13:38:28 -0700417 Vector2 capAAOffset = {0, 0};
Chris Craik65cd6122012-12-10 17:56:27 -0800418 if (paintInfo.cap != SkPaint::kRound_Cap) {
419 // if the cap is square or butt, the inside primary cap vertices will be inset in two
420 // directions - both normal to the stroke, and parallel to it.
John Reck1aa5d2d2014-07-24 13:38:28 -0700421 capAAOffset = (Vector2){-AAOffset.y, AAOffset.x};
Chris Craik65cd6122012-12-10 17:56:27 -0800422 }
423
424 // determine referencePoint, the center point for the 4 primary cap vertices
425 const Vertex* point = isFirst ? vertices.begin() : (vertices.end() - 1);
John Reck1aa5d2d2014-07-24 13:38:28 -0700426 Vector2 referencePoint = {point->x, point->y};
Chris Craik65cd6122012-12-10 17:56:27 -0800427 if (paintInfo.cap == SkPaint::kSquare_Cap) {
428 // To account for square cap, move the primary cap vertices (that create the AA edge) by the
429 // stroke offset vector (rotated to be parallel to the stroke)
John Reck1aa5d2d2014-07-24 13:38:28 -0700430 Vector2 rotated = {-strokeOffset.y, strokeOffset.x};
431 referencePoint += rotated;
Chris Craik65cd6122012-12-10 17:56:27 -0800432 }
433
434 AlphaVertex::set(&buffer[capIndex + 0],
435 referencePoint.x + outerOffset.x + capAAOffset.x,
436 referencePoint.y + outerOffset.y + capAAOffset.y,
437 0.0f);
438 AlphaVertex::set(&buffer[capIndex + 1],
439 referencePoint.x + innerOffset.x - capAAOffset.x,
440 referencePoint.y + innerOffset.y - capAAOffset.y,
441 paintInfo.maxAlpha);
442
443 bool isRound = paintInfo.cap == SkPaint::kRound_Cap;
444
445 const int postCapIndex = (isRound && isFirst) ? (2 * extraOffset - 2) : capIndex + (2 * extra);
446 AlphaVertex::set(&buffer[postCapIndex + 2],
447 referencePoint.x - outerOffset.x + capAAOffset.x,
448 referencePoint.y - outerOffset.y + capAAOffset.y,
449 0.0f);
450 AlphaVertex::set(&buffer[postCapIndex + 3],
451 referencePoint.x - innerOffset.x - capAAOffset.x,
452 referencePoint.y - innerOffset.y - capAAOffset.y,
453 paintInfo.maxAlpha);
454
455 if (isRound) {
456 const float dTheta = PI / (extra + 1);
457 const float radialScale = 2.0f / (1 + cos(dTheta));
458 float theta = atan2(normal.y, normal.x);
459 int capPerimIndex = capIndex + 2;
460
461 for (int i = 0; i < extra; i++) {
462 theta += dTheta;
463
John Reck1aa5d2d2014-07-24 13:38:28 -0700464 Vector2 radialOffset = {cos(theta), sin(theta)};
Chris Craik65cd6122012-12-10 17:56:27 -0800465
466 // scale to compensate for pinching at sharp angles, see totalOffsetFromNormals()
467 radialOffset *= radialScale;
468
469 AAOffset = paintInfo.deriveAAOffset(radialOffset);
470 paintInfo.scaleOffsetForStrokeWidth(radialOffset);
471 AlphaVertex::set(&buffer[capPerimIndex++],
472 referencePoint.x + radialOffset.x + AAOffset.x,
473 referencePoint.y + radialOffset.y + AAOffset.y,
474 0.0f);
475 AlphaVertex::set(&buffer[capPerimIndex++],
476 referencePoint.x + radialOffset.x - AAOffset.x,
477 referencePoint.y + radialOffset.y - AAOffset.y,
478 paintInfo.maxAlpha);
479
480 if (isFirst && i == extra - extraOffset) {
481 //copy most recent two points to first two points
Chris Craik11a75672013-12-16 17:08:15 -0800482 buffer[0] = buffer[capPerimIndex - 2];
483 buffer[1] = buffer[capPerimIndex - 1];
Chris Craik65cd6122012-12-10 17:56:27 -0800484
485 capPerimIndex = 2; // start writing the rest of the round cap at index 2
486 }
487 }
488
489 if (isFirst) {
490 const int startCapFillIndex = capIndex + 2 * (extra - extraOffset) + 4;
491 int capFillIndex = startCapFillIndex;
492 for (int i = 0; i < extra + 2; i += 2) {
Chris Craik11a75672013-12-16 17:08:15 -0800493 buffer[capFillIndex++] = buffer[1 + i];
Chris Craik65cd6122012-12-10 17:56:27 -0800494 // TODO: to support odd numbers of divisions, break here on the last iteration
Chris Craik11a75672013-12-16 17:08:15 -0800495 buffer[capFillIndex++] = buffer[startCapFillIndex - 3 - i];
Chris Craik65cd6122012-12-10 17:56:27 -0800496 }
497 } else {
498 int capFillIndex = 6 * vertices.size() + 2 + 6 * extra - (extra + 2);
499 for (int i = 0; i < extra + 2; i += 2) {
Chris Craik11a75672013-12-16 17:08:15 -0800500 buffer[capFillIndex++] = buffer[capIndex + 1 + i];
Chris Craik65cd6122012-12-10 17:56:27 -0800501 // TODO: to support odd numbers of divisions, break here on the last iteration
Chris Craik11a75672013-12-16 17:08:15 -0800502 buffer[capFillIndex++] = buffer[capIndex + 3 + 2 * extra - i];
Chris Craik65cd6122012-12-10 17:56:27 -0800503 }
504 }
505 return;
506 }
507 if (isFirst) {
Chris Craik11a75672013-12-16 17:08:15 -0800508 buffer[0] = buffer[postCapIndex + 2];
509 buffer[1] = buffer[postCapIndex + 3];
510 buffer[postCapIndex + 4] = buffer[1]; // degenerate tris (the only two!)
511 buffer[postCapIndex + 5] = buffer[postCapIndex + 1];
Chris Craik65cd6122012-12-10 17:56:27 -0800512 } else {
Chris Craik11a75672013-12-16 17:08:15 -0800513 buffer[6 * vertices.size()] = buffer[postCapIndex + 1];
514 buffer[6 * vertices.size() + 1] = buffer[postCapIndex + 3];
Chris Craik65cd6122012-12-10 17:56:27 -0800515 }
516}
517
518/*
519the geometry for an aa, capped stroke consists of the following:
520
521 # vertices | function
522----------------------------------------------------------------------
523a) 2 | Start AA perimeter
524b) 2, 2 * roundDivOff | First half of begin cap's perimeter
525 |
526 2 * middlePts | 'Outer' or 'Top' AA perimeter half (between caps)
527 |
528a) 4 | End cap's
529b) 2, 2 * roundDivs, 2 | AA perimeter
530 |
531 2 * middlePts | 'Inner' or 'bottom' AA perimeter half
532 |
533a) 6 | Begin cap's perimeter
534b) 2, 2*(rD - rDO + 1), | Last half of begin cap's perimeter
535 roundDivs, 2 |
536 |
537 2 * middlePts | Stroke's full opacity center strip
538 |
539a) 2 | end stroke
540b) 2, roundDivs | (and end cap fill, for round)
541
542Notes:
543* rows starting with 'a)' denote the Butt or Square cap vertex use, 'b)' denote Round
544
545* 'middlePts' is (number of points in the unclosed input vertex list, minus 2) times two
546
547* 'roundDivs' or 'rD' is the number of extra vertices (beyond the minimum of 2) that define the
548 round cap's shape, and is at least two. This will increase with cap size to sufficiently
549 define the cap's level of tessellation.
550
551* 'roundDivOffset' or 'rDO' is the point about halfway along the start cap's round perimeter, where
552 the stream of vertices for the AA perimeter starts. By starting and ending the perimeter at
553 this offset, the fill of the stroke is drawn from this point with minimal extra vertices.
554
555This means the outer perimeter starts at:
556 outerIndex = (2) OR (2 + 2 * roundDivOff)
557the inner perimeter (since it is filled in reverse) starts at:
558 innerIndex = outerIndex + (4 * middlePts) + ((4) OR (4 + 2 * roundDivs)) - 1
559the stroke starts at:
560 strokeIndex = innerIndex + 1 + ((6) OR (6 + 3 * roundDivs - 2 * roundDivOffset))
561
562The total needed allocated space is either:
563 2 + 4 + 6 + 2 + 3 * (2 * middlePts) = 14 + 6 * middlePts = 2 + 6 * pts
564or, for rounded caps:
565 (2 + 2 * rDO) + (4 + 2 * rD) + (2 * (rD - rDO + 1)
566 + roundDivs + 4) + (2 + roundDivs) + 3 * (2 * middlePts)
567 = 14 + 6 * middlePts + 6 * roundDivs
568 = 2 + 6 * pts + 6 * roundDivs
569 */
570void getStrokeVerticesFromUnclosedVerticesAA(const PaintInfo& paintInfo,
571 const Vector<Vertex>& vertices, VertexBuffer& vertexBuffer) {
572
573 const int extra = paintInfo.capExtraDivisions();
574 const int allocSize = 6 * vertices.size() + 2 + 6 * extra;
575
576 AlphaVertex* buffer = vertexBuffer.alloc<AlphaVertex>(allocSize);
577
578 const int extraOffset = (extra + 1) / 2;
579 int offset = 2 * (vertices.size() - 2);
580 // there is no outer/inner here, using them for consistency with below approach
581 int currentAAOuterIndex = 2 + 2 * extraOffset;
582 int currentAAInnerIndex = currentAAOuterIndex + (2 * offset) + 3 + (2 * extra);
583 int currentStrokeIndex = currentAAInnerIndex + 7 + (3 * extra - 2 * extraOffset);
584
585 const Vertex* last = &(vertices[0]);
586 const Vertex* current = &(vertices[1]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700587 Vector2 lastNormal = {current->y - last->y, last->x - current->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800588 lastNormal.normalize();
589
590 // TODO: use normal from bezier traversal for cap, instead of from vertices
591 storeCapAA(paintInfo, vertices, buffer, true, lastNormal, offset);
592
593 for (unsigned int i = 1; i < vertices.size() - 1; i++) {
594 const Vertex* next = &(vertices[i + 1]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700595 Vector2 nextNormal = {next->y - current->y, current->x - next->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800596 nextNormal.normalize();
597
John Reck1aa5d2d2014-07-24 13:38:28 -0700598 Vector2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
599 Vector2 AAOffset = paintInfo.deriveAAOffset(totalOffset);
Chris Craik65cd6122012-12-10 17:56:27 -0800600
John Reck1aa5d2d2014-07-24 13:38:28 -0700601 Vector2 innerOffset = totalOffset;
Chris Craik65cd6122012-12-10 17:56:27 -0800602 paintInfo.scaleOffsetForStrokeWidth(innerOffset);
John Reck1aa5d2d2014-07-24 13:38:28 -0700603 Vector2 outerOffset = innerOffset + AAOffset;
Chris Craik65cd6122012-12-10 17:56:27 -0800604 innerOffset -= AAOffset;
605
606 AlphaVertex::set(&buffer[currentAAOuterIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700607 current->x + outerOffset.x,
608 current->y + outerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800609 0.0f);
610 AlphaVertex::set(&buffer[currentAAOuterIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700611 current->x + innerOffset.x,
612 current->y + innerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800613 paintInfo.maxAlpha);
614
615 AlphaVertex::set(&buffer[currentStrokeIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700616 current->x + innerOffset.x,
617 current->y + innerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800618 paintInfo.maxAlpha);
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
624 AlphaVertex::set(&buffer[currentAAInnerIndex--],
Romain Guy3380cfd2013-08-15 16:57:57 -0700625 current->x - innerOffset.x,
626 current->y - innerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800627 paintInfo.maxAlpha);
628 AlphaVertex::set(&buffer[currentAAInnerIndex--],
Romain Guy3380cfd2013-08-15 16:57:57 -0700629 current->x - outerOffset.x,
630 current->y - outerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800631 0.0f);
632
633 current = next;
634 lastNormal = nextNormal;
635 }
636
637 // TODO: use normal from bezier traversal for cap, instead of from vertices
638 storeCapAA(paintInfo, vertices, buffer, false, lastNormal, offset);
639
640 DEBUG_DUMP_ALPHA_BUFFER();
641}
642
643
644void getStrokeVerticesFromPerimeterAA(const PaintInfo& paintInfo, const Vector<Vertex>& perimeter,
645 VertexBuffer& vertexBuffer) {
646 AlphaVertex* buffer = vertexBuffer.alloc<AlphaVertex>(6 * perimeter.size() + 8);
647
648 int offset = 2 * perimeter.size() + 3;
649 int currentAAOuterIndex = 0;
650 int currentStrokeIndex = offset;
651 int currentAAInnerIndex = offset * 2;
652
653 const Vertex* last = &(perimeter[perimeter.size() - 1]);
654 const Vertex* current = &(perimeter[0]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700655 Vector2 lastNormal = {current->y - last->y, last->x - current->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800656 lastNormal.normalize();
657 for (unsigned int i = 0; i < perimeter.size(); i++) {
658 const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700659 Vector2 nextNormal = {next->y - current->y, current->x - next->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800660 nextNormal.normalize();
661
John Reck1aa5d2d2014-07-24 13:38:28 -0700662 Vector2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
663 Vector2 AAOffset = paintInfo.deriveAAOffset(totalOffset);
Chris Craik65cd6122012-12-10 17:56:27 -0800664
John Reck1aa5d2d2014-07-24 13:38:28 -0700665 Vector2 innerOffset = totalOffset;
Chris Craik65cd6122012-12-10 17:56:27 -0800666 paintInfo.scaleOffsetForStrokeWidth(innerOffset);
John Reck1aa5d2d2014-07-24 13:38:28 -0700667 Vector2 outerOffset = innerOffset + AAOffset;
Chris Craik65cd6122012-12-10 17:56:27 -0800668 innerOffset -= AAOffset;
669
670 AlphaVertex::set(&buffer[currentAAOuterIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700671 current->x + outerOffset.x,
672 current->y + outerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800673 0.0f);
674 AlphaVertex::set(&buffer[currentAAOuterIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700675 current->x + innerOffset.x,
676 current->y + innerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800677 paintInfo.maxAlpha);
678
679 AlphaVertex::set(&buffer[currentStrokeIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700680 current->x + innerOffset.x,
681 current->y + innerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800682 paintInfo.maxAlpha);
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
688 AlphaVertex::set(&buffer[currentAAInnerIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700689 current->x - innerOffset.x,
690 current->y - innerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800691 paintInfo.maxAlpha);
692 AlphaVertex::set(&buffer[currentAAInnerIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700693 current->x - outerOffset.x,
694 current->y - outerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800695 0.0f);
696
697 last = current;
698 current = next;
699 lastNormal = nextNormal;
700 }
701
702 // wrap each strip around to beginning, creating degenerate tris to bridge strips
Chris Craik11a75672013-12-16 17:08:15 -0800703 buffer[currentAAOuterIndex++] = buffer[0];
704 buffer[currentAAOuterIndex++] = buffer[1];
705 buffer[currentAAOuterIndex++] = buffer[1];
Chris Craik65cd6122012-12-10 17:56:27 -0800706
Chris Craik11a75672013-12-16 17:08:15 -0800707 buffer[currentStrokeIndex++] = buffer[offset];
708 buffer[currentStrokeIndex++] = buffer[offset + 1];
709 buffer[currentStrokeIndex++] = buffer[offset + 1];
Chris Craik65cd6122012-12-10 17:56:27 -0800710
Chris Craik11a75672013-12-16 17:08:15 -0800711 buffer[currentAAInnerIndex++] = buffer[2 * offset];
712 buffer[currentAAInnerIndex++] = buffer[2 * offset + 1];
Chris Craik65cd6122012-12-10 17:56:27 -0800713 // don't need to create last degenerate tri
714
715 DEBUG_DUMP_ALPHA_BUFFER();
716}
717
718void PathTessellator::tessellatePath(const SkPath &path, const SkPaint* paint,
Chris Craikd6b65f62014-01-01 14:45:21 -0800719 const mat4& transform, VertexBuffer& vertexBuffer) {
Chris Craik65cd6122012-12-10 17:56:27 -0800720 ATRACE_CALL();
721
722 const PaintInfo paintInfo(paint, transform);
723
724 Vector<Vertex> tempVertices;
725 float threshInvScaleX = paintInfo.inverseScaleX;
726 float threshInvScaleY = paintInfo.inverseScaleY;
727 if (paintInfo.style == SkPaint::kStroke_Style) {
728 // alter the bezier recursion threshold values we calculate in order to compensate for
729 // expansion done after the path vertices are found
730 SkRect bounds = path.getBounds();
731 if (!bounds.isEmpty()) {
732 threshInvScaleX *= bounds.width() / (bounds.width() + paint->getStrokeWidth());
733 threshInvScaleY *= bounds.height() / (bounds.height() + paint->getStrokeWidth());
734 }
735 }
736
737 // force close if we're filling the path, since fill path expects closed perimeter.
738 bool forceClose = paintInfo.style != SkPaint::kStroke_Style;
739 bool wasClosed = approximatePathOutlineVertices(path, forceClose,
Chris Craik15a07a22014-01-26 13:43:53 -0800740 threshInvScaleX * threshInvScaleX, threshInvScaleY * threshInvScaleY,
741 OUTLINE_REFINE_THRESHOLD_SQUARED, tempVertices);
Chris Craik65cd6122012-12-10 17:56:27 -0800742
743 if (!tempVertices.size()) {
744 // path was empty, return without allocating vertex buffer
745 return;
746 }
747
748#if VERTEX_DEBUG
749 for (unsigned int i = 0; i < tempVertices.size(); i++) {
750 ALOGD("orig path: point at %f %f",
Romain Guy3380cfd2013-08-15 16:57:57 -0700751 tempVertices[i].x, tempVertices[i].y);
Chris Craik65cd6122012-12-10 17:56:27 -0800752 }
753#endif
754
755 if (paintInfo.style == SkPaint::kStroke_Style) {
756 if (!paintInfo.isAA) {
757 if (wasClosed) {
758 getStrokeVerticesFromPerimeter(paintInfo, tempVertices, vertexBuffer);
759 } else {
760 getStrokeVerticesFromUnclosedVertices(paintInfo, tempVertices, vertexBuffer);
761 }
762
763 } else {
764 if (wasClosed) {
765 getStrokeVerticesFromPerimeterAA(paintInfo, tempVertices, vertexBuffer);
766 } else {
767 getStrokeVerticesFromUnclosedVerticesAA(paintInfo, tempVertices, vertexBuffer);
768 }
769 }
770 } else {
771 // For kStrokeAndFill style, the path should be adjusted externally.
772 // It will be treated as a fill here.
773 if (!paintInfo.isAA) {
774 getFillVerticesFromPerimeter(tempVertices, vertexBuffer);
775 } else {
776 getFillVerticesFromPerimeterAA(paintInfo, tempVertices, vertexBuffer);
777 }
778 }
Chris Craik05f3d6e2014-06-02 16:27:04 -0700779
780 Rect bounds(path.getBounds());
781 paintInfo.expandBoundsForStroke(&bounds);
782 vertexBuffer.setBounds(bounds);
Chris Craik65cd6122012-12-10 17:56:27 -0800783}
784
Chris Craik6d29c8d2013-05-08 18:35:44 -0700785template <class TYPE>
786static void instanceVertices(VertexBuffer& srcBuffer, VertexBuffer& dstBuffer,
Chris Craik05f3d6e2014-06-02 16:27:04 -0700787 const float* points, int count, Rect& bounds) {
Chris Craik6d29c8d2013-05-08 18:35:44 -0700788 bounds.set(points[0], points[1], points[0], points[1]);
789
790 int numPoints = count / 2;
791 int verticesPerPoint = srcBuffer.getVertexCount();
792 dstBuffer.alloc<TYPE>(numPoints * verticesPerPoint + (numPoints - 1) * 2);
793
794 for (int i = 0; i < count; i += 2) {
Chris Craikc93e45c2014-07-16 10:15:56 -0700795 bounds.expandToCoverVertex(points[i + 0], points[i + 1]);
Chris Craik6d29c8d2013-05-08 18:35:44 -0700796 dstBuffer.copyInto<TYPE>(srcBuffer, points[i + 0], points[i + 1]);
797 }
798 dstBuffer.createDegenerateSeparators<TYPE>(verticesPerPoint);
799}
800
Chris Craikd218a922014-01-02 17:13:34 -0800801void PathTessellator::tessellatePoints(const float* points, int count, const SkPaint* paint,
Chris Craik05f3d6e2014-06-02 16:27:04 -0700802 const mat4& transform, VertexBuffer& vertexBuffer) {
Chris Craik6d29c8d2013-05-08 18:35:44 -0700803 const PaintInfo paintInfo(paint, transform);
804
805 // determine point shape
806 SkPath path;
807 float radius = paintInfo.halfStrokeWidth;
808 if (radius == 0.0f) radius = 0.25f;
809
810 if (paintInfo.cap == SkPaint::kRound_Cap) {
811 path.addCircle(0, 0, radius);
812 } else {
813 path.addRect(-radius, -radius, radius, radius);
814 }
815
816 // calculate outline
817 Vector<Vertex> outlineVertices;
818 approximatePathOutlineVertices(path, true,
819 paintInfo.inverseScaleX * paintInfo.inverseScaleX,
Chris Craik15a07a22014-01-26 13:43:53 -0800820 paintInfo.inverseScaleY * paintInfo.inverseScaleY,
821 OUTLINE_REFINE_THRESHOLD_SQUARED, outlineVertices);
Chris Craik6d29c8d2013-05-08 18:35:44 -0700822
823 if (!outlineVertices.size()) return;
824
Chris Craik05f3d6e2014-06-02 16:27:04 -0700825 Rect bounds;
Chris Craik6d29c8d2013-05-08 18:35:44 -0700826 // tessellate, then duplicate outline across points
827 int numPoints = count / 2;
828 VertexBuffer tempBuffer;
829 if (!paintInfo.isAA) {
830 getFillVerticesFromPerimeter(outlineVertices, tempBuffer);
831 instanceVertices<Vertex>(tempBuffer, vertexBuffer, points, count, bounds);
832 } else {
Chris Craikf0a59072013-11-19 18:00:46 -0800833 // note: pass maxAlpha directly, since we want fill to be alpha modulated
834 getFillVerticesFromPerimeterAA(paintInfo, outlineVertices, tempBuffer, paintInfo.maxAlpha);
Chris Craik6d29c8d2013-05-08 18:35:44 -0700835 instanceVertices<AlphaVertex>(tempBuffer, vertexBuffer, points, count, bounds);
836 }
837
Chris Craikf0a59072013-11-19 18:00:46 -0800838 // expand bounds from vertex coords to pixel data
Chris Craik05f3d6e2014-06-02 16:27:04 -0700839 paintInfo.expandBoundsForStroke(&bounds);
840 vertexBuffer.setBounds(bounds);
Chris Craik65cd6122012-12-10 17:56:27 -0800841}
842
Chris Craikd218a922014-01-02 17:13:34 -0800843void PathTessellator::tessellateLines(const float* points, int count, const SkPaint* paint,
Chris Craik05f3d6e2014-06-02 16:27:04 -0700844 const mat4& transform, VertexBuffer& vertexBuffer) {
Chris Craik65cd6122012-12-10 17:56:27 -0800845 ATRACE_CALL();
846 const PaintInfo paintInfo(paint, transform);
847
848 const int extra = paintInfo.capExtraDivisions();
849 int numLines = count / 4;
850 int lineAllocSize;
851 // pre-allocate space for lines in the buffer, and degenerate tris in between
852 if (paintInfo.isAA) {
853 lineAllocSize = 6 * (2) + 2 + 6 * extra;
854 vertexBuffer.alloc<AlphaVertex>(numLines * lineAllocSize + (numLines - 1) * 2);
855 } else {
856 lineAllocSize = 2 * ((2) + extra);
857 vertexBuffer.alloc<Vertex>(numLines * lineAllocSize + (numLines - 1) * 2);
858 }
859
860 Vector<Vertex> tempVertices;
861 tempVertices.push();
862 tempVertices.push();
863 Vertex* tempVerticesData = tempVertices.editArray();
Chris Craik05f3d6e2014-06-02 16:27:04 -0700864 Rect bounds;
Chris Craik65cd6122012-12-10 17:56:27 -0800865 bounds.set(points[0], points[1], points[0], points[1]);
866 for (int i = 0; i < count; i += 4) {
867 Vertex::set(&(tempVerticesData[0]), points[i + 0], points[i + 1]);
868 Vertex::set(&(tempVerticesData[1]), points[i + 2], points[i + 3]);
869
870 if (paintInfo.isAA) {
871 getStrokeVerticesFromUnclosedVerticesAA(paintInfo, tempVertices, vertexBuffer);
872 } else {
873 getStrokeVerticesFromUnclosedVertices(paintInfo, tempVertices, vertexBuffer);
874 }
875
876 // calculate bounds
Chris Craikc93e45c2014-07-16 10:15:56 -0700877 bounds.expandToCoverVertex(tempVerticesData[0].x, tempVerticesData[0].y);
878 bounds.expandToCoverVertex(tempVerticesData[1].x, tempVerticesData[1].y);
Chris Craik65cd6122012-12-10 17:56:27 -0800879 }
880
Chris Craik65cd6122012-12-10 17:56:27 -0800881 // since multiple objects tessellated into buffer, separate them with degen tris
882 if (paintInfo.isAA) {
883 vertexBuffer.createDegenerateSeparators<AlphaVertex>(lineAllocSize);
884 } else {
885 vertexBuffer.createDegenerateSeparators<Vertex>(lineAllocSize);
886 }
Chris Craikf0a59072013-11-19 18:00:46 -0800887
888 // expand bounds from vertex coords to pixel data
Chris Craik05f3d6e2014-06-02 16:27:04 -0700889 paintInfo.expandBoundsForStroke(&bounds);
890 vertexBuffer.setBounds(bounds);
Chris Craik65cd6122012-12-10 17:56:27 -0800891}
892
893///////////////////////////////////////////////////////////////////////////////
894// Simple path line approximation
895///////////////////////////////////////////////////////////////////////////////
896
Chris Craik15a07a22014-01-26 13:43:53 -0800897bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, float thresholdSquared,
898 Vector<Vertex>& outputVertices) {
899 return approximatePathOutlineVertices(path, true, 1.0f, 1.0f, thresholdSquared, outputVertices);
900}
901
Chris Craik65cd6122012-12-10 17:56:27 -0800902void pushToVector(Vector<Vertex>& vertices, float x, float y) {
903 // TODO: make this not yuck
904 vertices.push();
905 Vertex* newVertex = &(vertices.editArray()[vertices.size() - 1]);
906 Vertex::set(newVertex, x, y);
907}
908
909bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool forceClose,
Chris Craik15a07a22014-01-26 13:43:53 -0800910 float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared,
911 Vector<Vertex>& outputVertices) {
Chris Craik65cd6122012-12-10 17:56:27 -0800912 ATRACE_CALL();
913
914 // TODO: to support joins other than sharp miter, join vertices should be labelled in the
915 // perimeter, or resolved into more vertices. Reconsider forceClose-ing in that case.
916 SkPath::Iter iter(path, forceClose);
917 SkPoint pts[4];
918 SkPath::Verb v;
919 while (SkPath::kDone_Verb != (v = iter.next(pts))) {
920 switch (v) {
921 case SkPath::kMove_Verb:
922 pushToVector(outputVertices, pts[0].x(), pts[0].y());
923 ALOGV("Move to pos %f %f", pts[0].x(), pts[0].y());
924 break;
925 case SkPath::kClose_Verb:
926 ALOGV("Close at pos %f %f", pts[0].x(), pts[0].y());
927 break;
928 case SkPath::kLine_Verb:
929 ALOGV("kLine_Verb %f %f -> %f %f", pts[0].x(), pts[0].y(), pts[1].x(), pts[1].y());
930 pushToVector(outputVertices, pts[1].x(), pts[1].y());
931 break;
932 case SkPath::kQuad_Verb:
933 ALOGV("kQuad_Verb");
934 recursiveQuadraticBezierVertices(
935 pts[0].x(), pts[0].y(),
936 pts[2].x(), pts[2].y(),
937 pts[1].x(), pts[1].y(),
Chris Craik15a07a22014-01-26 13:43:53 -0800938 sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices);
Chris Craik65cd6122012-12-10 17:56:27 -0800939 break;
940 case SkPath::kCubic_Verb:
941 ALOGV("kCubic_Verb");
942 recursiveCubicBezierVertices(
943 pts[0].x(), pts[0].y(),
944 pts[1].x(), pts[1].y(),
945 pts[3].x(), pts[3].y(),
946 pts[2].x(), pts[2].y(),
Chris Craik15a07a22014-01-26 13:43:53 -0800947 sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices);
Chris Craik65cd6122012-12-10 17:56:27 -0800948 break;
949 default:
950 break;
951 }
952 }
953
954 int size = outputVertices.size();
Romain Guy3380cfd2013-08-15 16:57:57 -0700955 if (size >= 2 && outputVertices[0].x == outputVertices[size - 1].x &&
956 outputVertices[0].y == outputVertices[size - 1].y) {
Chris Craik65cd6122012-12-10 17:56:27 -0800957 outputVertices.pop();
958 return true;
959 }
960 return false;
961}
962
963///////////////////////////////////////////////////////////////////////////////
964// Bezier approximation
965///////////////////////////////////////////////////////////////////////////////
966
967void PathTessellator::recursiveCubicBezierVertices(
968 float p1x, float p1y, float c1x, float c1y,
969 float p2x, float p2y, float c2x, float c2y,
Chris Craik15a07a22014-01-26 13:43:53 -0800970 float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared,
Chris Craik9c3dd622014-06-11 17:24:51 -0700971 Vector<Vertex>& outputVertices, int depth) {
Chris Craik65cd6122012-12-10 17:56:27 -0800972 float dx = p2x - p1x;
973 float dy = p2y - p1y;
974 float d1 = fabs((c1x - p2x) * dy - (c1y - p2y) * dx);
975 float d2 = fabs((c2x - p2x) * dy - (c2y - p2y) * dx);
976 float d = d1 + d2;
977
978 // multiplying by sqrInvScaleY/X equivalent to multiplying in dimensional scale factors
Chris Craik74cf7e62014-08-07 14:34:46 -0700979 if (depth >= MAX_DEPTH
980 || d * d <= thresholdSquared * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) {
Chris Craik65cd6122012-12-10 17:56:27 -0800981 // below thresh, draw line by adding endpoint
982 pushToVector(outputVertices, p2x, p2y);
983 } else {
984 float p1c1x = (p1x + c1x) * 0.5f;
985 float p1c1y = (p1y + c1y) * 0.5f;
986 float p2c2x = (p2x + c2x) * 0.5f;
987 float p2c2y = (p2y + c2y) * 0.5f;
988
989 float c1c2x = (c1x + c2x) * 0.5f;
990 float c1c2y = (c1y + c2y) * 0.5f;
991
992 float p1c1c2x = (p1c1x + c1c2x) * 0.5f;
993 float p1c1c2y = (p1c1y + c1c2y) * 0.5f;
994
995 float p2c1c2x = (p2c2x + c1c2x) * 0.5f;
996 float p2c1c2y = (p2c2y + c1c2y) * 0.5f;
997
998 float mx = (p1c1c2x + p2c1c2x) * 0.5f;
999 float my = (p1c1c2y + p2c1c2y) * 0.5f;
1000
1001 recursiveCubicBezierVertices(
1002 p1x, p1y, p1c1x, p1c1y,
1003 mx, my, p1c1c2x, p1c1c2y,
Chris Craik9c3dd622014-06-11 17:24:51 -07001004 sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices, depth + 1);
Chris Craik65cd6122012-12-10 17:56:27 -08001005 recursiveCubicBezierVertices(
1006 mx, my, p2c1c2x, p2c1c2y,
1007 p2x, p2y, p2c2x, p2c2y,
Chris Craik9c3dd622014-06-11 17:24:51 -07001008 sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices, depth + 1);
Chris Craik65cd6122012-12-10 17:56:27 -08001009 }
1010}
1011
1012void PathTessellator::recursiveQuadraticBezierVertices(
1013 float ax, float ay,
1014 float bx, float by,
1015 float cx, float cy,
Chris Craik15a07a22014-01-26 13:43:53 -08001016 float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared,
Chris Craik9c3dd622014-06-11 17:24:51 -07001017 Vector<Vertex>& outputVertices, int depth) {
Chris Craik65cd6122012-12-10 17:56:27 -08001018 float dx = bx - ax;
1019 float dy = by - ay;
1020 float d = (cx - bx) * dy - (cy - by) * dx;
1021
Chris Craik74cf7e62014-08-07 14:34:46 -07001022 // multiplying by sqrInvScaleY/X equivalent to multiplying in dimensional scale factors
1023 if (depth >= MAX_DEPTH
1024 || d * d <= thresholdSquared * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) {
Chris Craik65cd6122012-12-10 17:56:27 -08001025 // below thresh, draw line by adding endpoint
1026 pushToVector(outputVertices, bx, by);
1027 } else {
1028 float acx = (ax + cx) * 0.5f;
1029 float bcx = (bx + cx) * 0.5f;
1030 float acy = (ay + cy) * 0.5f;
1031 float bcy = (by + cy) * 0.5f;
1032
1033 // midpoint
1034 float mx = (acx + bcx) * 0.5f;
1035 float my = (acy + bcy) * 0.5f;
1036
1037 recursiveQuadraticBezierVertices(ax, ay, mx, my, acx, acy,
Chris Craik9c3dd622014-06-11 17:24:51 -07001038 sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices, depth + 1);
Chris Craik65cd6122012-12-10 17:56:27 -08001039 recursiveQuadraticBezierVertices(mx, my, bx, by, bcx, bcy,
Chris Craik9c3dd622014-06-11 17:24:51 -07001040 sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices, depth + 1);
Chris Craik65cd6122012-12-10 17:56:27 -08001041 }
1042}
1043
1044}; // namespace uirenderer
1045}; // namespace android