blob: 7cafd7b48ca45c497e8af0dcad59c788576c66a8 [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>
Derek Sollenbergerb0772a62015-03-16 14:35:55 -040040#include <SkGeometry.h> // WARNING: Internal Skia Header
Chris Craik65cd6122012-12-10 17:56:27 -080041
42#include <stdlib.h>
43#include <stdint.h>
44#include <sys/types.h>
45
46#include <utils/Log.h>
47#include <utils/Trace.h>
48
49#include "PathTessellator.h"
50#include "Matrix.h"
51#include "Vector.h"
52#include "Vertex.h"
Chris Craik74cf7e62014-08-07 14:34:46 -070053#include "utils/MathUtils.h"
Chris Craik65cd6122012-12-10 17:56:27 -080054
55namespace android {
56namespace uirenderer {
57
Chris Craik15a07a22014-01-26 13:43:53 -080058#define OUTLINE_REFINE_THRESHOLD_SQUARED (0.5f * 0.5f)
Chris Craik65cd6122012-12-10 17:56:27 -080059#define ROUND_CAP_THRESH 0.25f
60#define PI 3.1415926535897932f
Chris Craik74cf7e62014-08-07 14:34:46 -070061#define MAX_DEPTH 15
Chris Craik65cd6122012-12-10 17:56:27 -080062
Chris Craik74cf7e62014-08-07 14:34:46 -070063/**
64 * Extracts the x and y scale from the transform as positive values, and clamps them
65 */
Chris Craik05f3d6e2014-06-02 16:27:04 -070066void PathTessellator::extractTessellationScales(const Matrix4& transform,
67 float* scaleX, float* scaleY) {
Chris Craikfe02b4b2014-06-16 16:34:29 -070068 if (CC_LIKELY(transform.isPureTranslate())) {
69 *scaleX = 1.0f;
70 *scaleY = 1.0f;
71 } else {
Chris Craik05f3d6e2014-06-02 16:27:04 -070072 float m00 = transform.data[Matrix4::kScaleX];
73 float m01 = transform.data[Matrix4::kSkewY];
74 float m10 = transform.data[Matrix4::kSkewX];
75 float m11 = transform.data[Matrix4::kScaleY];
Chris Craik74cf7e62014-08-07 14:34:46 -070076 *scaleX = MathUtils::clampTessellationScale(sqrt(m00 * m00 + m01 * m01));
77 *scaleY = MathUtils::clampTessellationScale(sqrt(m10 * m10 + m11 * m11));
Chris Craik65cd6122012-12-10 17:56:27 -080078 }
79}
80
Chris Craik65cd6122012-12-10 17:56:27 -080081/**
82 * Produces a pseudo-normal for a vertex, given the normals of the two incoming lines. If the offset
83 * from each vertex in a perimeter is calculated, the resultant lines connecting the offset vertices
84 * will be offset by 1.0
85 *
86 * Note that we can't add and normalize the two vectors, that would result in a rectangle having an
87 * offset of (sqrt(2)/2, sqrt(2)/2) at each corner, instead of (1, 1)
88 *
89 * NOTE: assumes angles between normals 90 degrees or less
90 */
John Reck1aa5d2d2014-07-24 13:38:28 -070091inline static Vector2 totalOffsetFromNormals(const Vector2& normalA, const Vector2& normalB) {
Chris Craik65cd6122012-12-10 17:56:27 -080092 return (normalA + normalB) / (1 + fabs(normalA.dot(normalB)));
93}
94
95/**
96 * Structure used for storing useful information about the SkPaint and scale used for tessellating
97 */
98struct PaintInfo {
99public:
Chris Craikd6b65f62014-01-01 14:45:21 -0800100 PaintInfo(const SkPaint* paint, const mat4& transform) :
Chris Craik65cd6122012-12-10 17:56:27 -0800101 style(paint->getStyle()), cap(paint->getStrokeCap()), isAA(paint->isAntiAlias()),
Chris Craik65cd6122012-12-10 17:56:27 -0800102 halfStrokeWidth(paint->getStrokeWidth() * 0.5f), maxAlpha(1.0f) {
103 // compute inverse scales
Chris Craikfe02b4b2014-06-16 16:34:29 -0700104 if (CC_LIKELY(transform.isPureTranslate())) {
105 inverseScaleX = 1.0f;
106 inverseScaleY = 1.0f;
107 } else {
Chris Craik05f3d6e2014-06-02 16:27:04 -0700108 float scaleX, scaleY;
109 PathTessellator::extractTessellationScales(transform, &scaleX, &scaleY);
Chris Craik74cf7e62014-08-07 14:34:46 -0700110 inverseScaleX = 1.0f / scaleX;
111 inverseScaleY = 1.0f / scaleY;
Chris Craik65cd6122012-12-10 17:56:27 -0800112 }
113
114 if (isAA && halfStrokeWidth != 0 && inverseScaleX == inverseScaleY &&
Chris Craik19a390b2013-02-27 15:43:26 -0800115 2 * halfStrokeWidth < inverseScaleX) {
Chris Craik05f3d6e2014-06-02 16:27:04 -0700116 // AA, with non-hairline stroke, width < 1 pixel. Scale alpha and treat as hairline.
Chris Craik65cd6122012-12-10 17:56:27 -0800117 maxAlpha *= (2 * halfStrokeWidth) / inverseScaleX;
118 halfStrokeWidth = 0.0f;
119 }
120 }
121
122 SkPaint::Style style;
123 SkPaint::Cap cap;
124 bool isAA;
125 float inverseScaleX;
126 float inverseScaleY;
127 float halfStrokeWidth;
128 float maxAlpha;
129
John Reck1aa5d2d2014-07-24 13:38:28 -0700130 inline void scaleOffsetForStrokeWidth(Vector2& offset) const {
Chris Craik65cd6122012-12-10 17:56:27 -0800131 if (halfStrokeWidth == 0.0f) {
132 // hairline - compensate for scale
133 offset.x *= 0.5f * inverseScaleX;
134 offset.y *= 0.5f * inverseScaleY;
135 } else {
136 offset *= halfStrokeWidth;
137 }
138 }
139
140 /**
141 * NOTE: the input will not always be a normal, especially for sharp edges - it should be the
142 * result of totalOffsetFromNormals (see documentation there)
143 */
John Reck1aa5d2d2014-07-24 13:38:28 -0700144 inline Vector2 deriveAAOffset(const Vector2& offset) const {
145 return (Vector2){offset.x * 0.5f * inverseScaleX, offset.y * 0.5f * inverseScaleY};
Chris Craik65cd6122012-12-10 17:56:27 -0800146 }
147
148 /**
149 * Returns the number of cap divisions beyond the minimum 2 (kButt_Cap/kSquareCap will return 0)
150 * Should only be used when stroking and drawing caps
151 */
152 inline int capExtraDivisions() const {
153 if (cap == SkPaint::kRound_Cap) {
Chris Craikff29b5a2015-05-27 18:17:03 -0700154 // always use 2 points for hairline
Chris Craik65cd6122012-12-10 17:56:27 -0800155 if (halfStrokeWidth == 0.0f) return 2;
156
Chris Craikff29b5a2015-05-27 18:17:03 -0700157 float threshold = MathUtils::min(inverseScaleX, inverseScaleY) * ROUND_CAP_THRESH;
158 return MathUtils::divisionsNeededToApproximateArc(halfStrokeWidth, PI, threshold);
Chris Craik65cd6122012-12-10 17:56:27 -0800159 }
160 return 0;
161 }
Chris Craikf0a59072013-11-19 18:00:46 -0800162
163 /**
Chris Craik21bcfc4f2014-09-08 18:43:30 -0700164 * Outset the bounds of point data (for line endpoints or points) to account for stroke
Chris Craikf0a59072013-11-19 18:00:46 -0800165 * geometry.
Chris Craik21bcfc4f2014-09-08 18:43:30 -0700166 *
167 * bounds are in pre-scaled space.
Chris Craikf0a59072013-11-19 18:00:46 -0800168 */
Chris Craik05f3d6e2014-06-02 16:27:04 -0700169 void expandBoundsForStroke(Rect* bounds) const {
Chris Craik21bcfc4f2014-09-08 18:43:30 -0700170 if (halfStrokeWidth == 0) {
171 // hairline, outset by (0.5f + fudge factor) in post-scaling space
172 bounds->outset(fabs(inverseScaleX) * (0.5f + Vertex::GeometryFudgeFactor()),
173 fabs(inverseScaleY) * (0.5f + Vertex::GeometryFudgeFactor()));
174 } else {
175 // non hairline, outset by half stroke width pre-scaled, and fudge factor post scaled
176 bounds->outset(halfStrokeWidth + fabs(inverseScaleX) * Vertex::GeometryFudgeFactor(),
177 halfStrokeWidth + fabs(inverseScaleY) * Vertex::GeometryFudgeFactor());
178 }
Chris Craikf0a59072013-11-19 18:00:46 -0800179 }
Chris Craik65cd6122012-12-10 17:56:27 -0800180};
181
182void getFillVerticesFromPerimeter(const Vector<Vertex>& perimeter, VertexBuffer& vertexBuffer) {
183 Vertex* buffer = vertexBuffer.alloc<Vertex>(perimeter.size());
184
185 int currentIndex = 0;
186 // zig zag between all previous points on the inside of the hull to create a
187 // triangle strip that fills the hull
188 int srcAindex = 0;
189 int srcBindex = perimeter.size() - 1;
190 while (srcAindex <= srcBindex) {
Chris Craik11a75672013-12-16 17:08:15 -0800191 buffer[currentIndex++] = perimeter[srcAindex];
Chris Craik65cd6122012-12-10 17:56:27 -0800192 if (srcAindex == srcBindex) break;
Chris Craik11a75672013-12-16 17:08:15 -0800193 buffer[currentIndex++] = perimeter[srcBindex];
Chris Craik65cd6122012-12-10 17:56:27 -0800194 srcAindex++;
195 srcBindex--;
196 }
197}
198
199/*
200 * Fills a vertexBuffer with non-alpha vertices, zig-zagging at each perimeter point to create a
201 * tri-strip as wide as the stroke.
202 *
203 * Uses an additional 2 vertices at the end to wrap around, closing the tri-strip
204 * (for a total of perimeter.size() * 2 + 2 vertices)
205 */
206void getStrokeVerticesFromPerimeter(const PaintInfo& paintInfo, const Vector<Vertex>& perimeter,
207 VertexBuffer& vertexBuffer) {
208 Vertex* buffer = vertexBuffer.alloc<Vertex>(perimeter.size() * 2 + 2);
209
210 int currentIndex = 0;
211 const Vertex* last = &(perimeter[perimeter.size() - 1]);
212 const Vertex* current = &(perimeter[0]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700213 Vector2 lastNormal = {current->y - last->y, last->x - current->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800214 lastNormal.normalize();
215 for (unsigned int i = 0; i < perimeter.size(); i++) {
216 const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700217 Vector2 nextNormal = {next->y - current->y, current->x - next->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800218 nextNormal.normalize();
219
John Reck1aa5d2d2014-07-24 13:38:28 -0700220 Vector2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
Chris Craik65cd6122012-12-10 17:56:27 -0800221 paintInfo.scaleOffsetForStrokeWidth(totalOffset);
222
223 Vertex::set(&buffer[currentIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700224 current->x + totalOffset.x,
225 current->y + totalOffset.y);
Chris Craik65cd6122012-12-10 17:56:27 -0800226
227 Vertex::set(&buffer[currentIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700228 current->x - totalOffset.x,
229 current->y - totalOffset.y);
Chris Craik65cd6122012-12-10 17:56:27 -0800230
Chris Craik65cd6122012-12-10 17:56:27 -0800231 current = next;
232 lastNormal = nextNormal;
233 }
234
235 // wrap around to beginning
Chris Craik11a75672013-12-16 17:08:15 -0800236 buffer[currentIndex++] = buffer[0];
237 buffer[currentIndex++] = buffer[1];
Chris Craik65cd6122012-12-10 17:56:27 -0800238
239 DEBUG_DUMP_BUFFER();
240}
241
Chris Craik6d29c8d2013-05-08 18:35:44 -0700242static inline void storeBeginEnd(const PaintInfo& paintInfo, const Vertex& center,
John Reck1aa5d2d2014-07-24 13:38:28 -0700243 const Vector2& normal, Vertex* buffer, int& currentIndex, bool begin) {
244 Vector2 strokeOffset = normal;
Chris Craik6d29c8d2013-05-08 18:35:44 -0700245 paintInfo.scaleOffsetForStrokeWidth(strokeOffset);
246
John Reck1aa5d2d2014-07-24 13:38:28 -0700247 Vector2 referencePoint = {center.x, center.y};
Chris Craik6d29c8d2013-05-08 18:35:44 -0700248 if (paintInfo.cap == SkPaint::kSquare_Cap) {
John Reck1aa5d2d2014-07-24 13:38:28 -0700249 Vector2 rotated = {-strokeOffset.y, strokeOffset.x};
250 referencePoint += rotated * (begin ? -1 : 1);
Chris Craik6d29c8d2013-05-08 18:35:44 -0700251 }
252
253 Vertex::set(&buffer[currentIndex++], referencePoint + strokeOffset);
254 Vertex::set(&buffer[currentIndex++], referencePoint - strokeOffset);
255}
256
Chris Craik65cd6122012-12-10 17:56:27 -0800257/**
258 * Fills a vertexBuffer with non-alpha vertices similar to getStrokeVerticesFromPerimeter, except:
259 *
260 * 1 - Doesn't need to wrap around, since the input vertices are unclosed
261 *
262 * 2 - can zig-zag across 'extra' vertices at either end, to create round caps
263 */
264void getStrokeVerticesFromUnclosedVertices(const PaintInfo& paintInfo,
265 const Vector<Vertex>& vertices, VertexBuffer& vertexBuffer) {
266 const int extra = paintInfo.capExtraDivisions();
267 const int allocSize = (vertices.size() + extra) * 2;
Chris Craik65cd6122012-12-10 17:56:27 -0800268 Vertex* buffer = vertexBuffer.alloc<Vertex>(allocSize);
269
Chris Craik6d29c8d2013-05-08 18:35:44 -0700270 const int lastIndex = vertices.size() - 1;
Chris Craik65cd6122012-12-10 17:56:27 -0800271 if (extra > 0) {
272 // tessellate both round caps
Chris Craik65cd6122012-12-10 17:56:27 -0800273 float beginTheta = atan2(
Romain Guy3380cfd2013-08-15 16:57:57 -0700274 - (vertices[0].x - vertices[1].x),
275 vertices[0].y - vertices[1].y);
Chris Craik65cd6122012-12-10 17:56:27 -0800276 float endTheta = atan2(
Romain Guy3380cfd2013-08-15 16:57:57 -0700277 - (vertices[lastIndex].x - vertices[lastIndex - 1].x),
278 vertices[lastIndex].y - vertices[lastIndex - 1].y);
Chris Craik65cd6122012-12-10 17:56:27 -0800279 const float dTheta = PI / (extra + 1);
Chris Craik65cd6122012-12-10 17:56:27 -0800280
281 int capOffset;
282 for (int i = 0; i < extra; i++) {
283 if (i < extra / 2) {
284 capOffset = extra - 2 * i - 1;
285 } else {
286 capOffset = 2 * i - extra;
287 }
288
289 beginTheta += dTheta;
Dan Albertff1d8a62014-11-11 19:31:26 -0800290 Vector2 beginRadialOffset = {cosf(beginTheta), sinf(beginTheta)};
Chris Craik65cd6122012-12-10 17:56:27 -0800291 paintInfo.scaleOffsetForStrokeWidth(beginRadialOffset);
292 Vertex::set(&buffer[capOffset],
Romain Guy3380cfd2013-08-15 16:57:57 -0700293 vertices[0].x + beginRadialOffset.x,
294 vertices[0].y + beginRadialOffset.y);
Chris Craik65cd6122012-12-10 17:56:27 -0800295
296 endTheta += dTheta;
Dan Albertff1d8a62014-11-11 19:31:26 -0800297 Vector2 endRadialOffset = {cosf(endTheta), sinf(endTheta)};
Chris Craik65cd6122012-12-10 17:56:27 -0800298 paintInfo.scaleOffsetForStrokeWidth(endRadialOffset);
299 Vertex::set(&buffer[allocSize - 1 - capOffset],
Romain Guy3380cfd2013-08-15 16:57:57 -0700300 vertices[lastIndex].x + endRadialOffset.x,
301 vertices[lastIndex].y + endRadialOffset.y);
Chris Craik65cd6122012-12-10 17:56:27 -0800302 }
303 }
304
305 int currentIndex = extra;
Chris Craik6d29c8d2013-05-08 18:35:44 -0700306 const Vertex* last = &(vertices[0]);
307 const Vertex* current = &(vertices[1]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700308 Vector2 lastNormal = {current->y - last->y, last->x - current->x};
Chris Craik6d29c8d2013-05-08 18:35:44 -0700309 lastNormal.normalize();
310
311 storeBeginEnd(paintInfo, vertices[0], lastNormal, buffer, currentIndex, true);
312
313 for (unsigned int i = 1; i < vertices.size() - 1; i++) {
Chris Craik65cd6122012-12-10 17:56:27 -0800314 const Vertex* next = &(vertices[i + 1]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700315 Vector2 nextNormal = {next->y - current->y, current->x - next->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800316 nextNormal.normalize();
317
John Reck1aa5d2d2014-07-24 13:38:28 -0700318 Vector2 strokeOffset = totalOffsetFromNormals(lastNormal, nextNormal);
Chris Craik6d29c8d2013-05-08 18:35:44 -0700319 paintInfo.scaleOffsetForStrokeWidth(strokeOffset);
Chris Craik65cd6122012-12-10 17:56:27 -0800320
John Reck1aa5d2d2014-07-24 13:38:28 -0700321 Vector2 center = {current->x, current->y};
Chris Craik6d29c8d2013-05-08 18:35:44 -0700322 Vertex::set(&buffer[currentIndex++], center + strokeOffset);
323 Vertex::set(&buffer[currentIndex++], center - strokeOffset);
Chris Craik65cd6122012-12-10 17:56:27 -0800324
325 current = next;
326 lastNormal = nextNormal;
327 }
328
Chris Craik6d29c8d2013-05-08 18:35:44 -0700329 storeBeginEnd(paintInfo, vertices[lastIndex], lastNormal, buffer, currentIndex, false);
Chris Craik65cd6122012-12-10 17:56:27 -0800330
331 DEBUG_DUMP_BUFFER();
332}
333
334/**
335 * Populates a vertexBuffer with AlphaVertices to create an anti-aliased fill shape tessellation
Chris Craik6d29c8d2013-05-08 18:35:44 -0700336 *
Chris Craik65cd6122012-12-10 17:56:27 -0800337 * 1 - create the AA perimeter of unit width, by zig-zagging at each point around the perimeter of
338 * the shape (using 2 * perimeter.size() vertices)
339 *
340 * 2 - wrap around to the beginning to complete the perimeter (2 vertices)
341 *
342 * 3 - zig zag back and forth inside the shape to fill it (using perimeter.size() vertices)
343 */
344void getFillVerticesFromPerimeterAA(const PaintInfo& paintInfo, const Vector<Vertex>& perimeter,
Chris Craikf0a59072013-11-19 18:00:46 -0800345 VertexBuffer& vertexBuffer, float maxAlpha = 1.0f) {
Chris Craik65cd6122012-12-10 17:56:27 -0800346 AlphaVertex* buffer = vertexBuffer.alloc<AlphaVertex>(perimeter.size() * 3 + 2);
347
348 // generate alpha points - fill Alpha vertex gaps in between each point with
349 // alpha 0 vertex, offset by a scaled normal.
350 int currentIndex = 0;
351 const Vertex* last = &(perimeter[perimeter.size() - 1]);
352 const Vertex* current = &(perimeter[0]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700353 Vector2 lastNormal = {current->y - last->y, last->x - current->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800354 lastNormal.normalize();
355 for (unsigned int i = 0; i < perimeter.size(); i++) {
356 const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700357 Vector2 nextNormal = {next->y - current->y, current->x - next->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800358 nextNormal.normalize();
359
360 // AA point offset from original point is that point's normal, such that each side is offset
361 // by .5 pixels
John Reck1aa5d2d2014-07-24 13:38:28 -0700362 Vector2 totalOffset = paintInfo.deriveAAOffset(totalOffsetFromNormals(lastNormal, nextNormal));
Chris Craik65cd6122012-12-10 17:56:27 -0800363
364 AlphaVertex::set(&buffer[currentIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700365 current->x + totalOffset.x,
366 current->y + totalOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800367 0.0f);
368 AlphaVertex::set(&buffer[currentIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700369 current->x - totalOffset.x,
370 current->y - totalOffset.y,
Chris Craikf0a59072013-11-19 18:00:46 -0800371 maxAlpha);
Chris Craik65cd6122012-12-10 17:56:27 -0800372
Chris Craik65cd6122012-12-10 17:56:27 -0800373 current = next;
374 lastNormal = nextNormal;
375 }
376
377 // wrap around to beginning
Chris Craik11a75672013-12-16 17:08:15 -0800378 buffer[currentIndex++] = buffer[0];
379 buffer[currentIndex++] = buffer[1];
Chris Craik65cd6122012-12-10 17:56:27 -0800380
381 // zig zag between all previous points on the inside of the hull to create a
382 // triangle strip that fills the hull, repeating the first inner point to
383 // create degenerate tris to start inside path
384 int srcAindex = 0;
385 int srcBindex = perimeter.size() - 1;
386 while (srcAindex <= srcBindex) {
Chris Craik11a75672013-12-16 17:08:15 -0800387 buffer[currentIndex++] = buffer[srcAindex * 2 + 1];
Chris Craik65cd6122012-12-10 17:56:27 -0800388 if (srcAindex == srcBindex) break;
Chris Craik11a75672013-12-16 17:08:15 -0800389 buffer[currentIndex++] = buffer[srcBindex * 2 + 1];
Chris Craik65cd6122012-12-10 17:56:27 -0800390 srcAindex++;
391 srcBindex--;
392 }
393
394 DEBUG_DUMP_BUFFER();
395}
396
397/**
398 * Stores geometry for a single, AA-perimeter (potentially rounded) cap
399 *
400 * For explanation of constants and general methodoloyg, see comments for
401 * getStrokeVerticesFromUnclosedVerticesAA() below.
402 */
Chris Craik6d29c8d2013-05-08 18:35:44 -0700403inline static void storeCapAA(const PaintInfo& paintInfo, const Vector<Vertex>& vertices,
John Reck1aa5d2d2014-07-24 13:38:28 -0700404 AlphaVertex* buffer, bool isFirst, Vector2 normal, int offset) {
Chris Craik65cd6122012-12-10 17:56:27 -0800405 const int extra = paintInfo.capExtraDivisions();
406 const int extraOffset = (extra + 1) / 2;
407 const int capIndex = isFirst
408 ? 2 * offset + 6 + 2 * (extra + extraOffset)
409 : offset + 2 + 2 * extraOffset;
410 if (isFirst) normal *= -1;
411
412 // TODO: this normal should be scaled by radialScale if extra != 0, see totalOffsetFromNormals()
John Reck1aa5d2d2014-07-24 13:38:28 -0700413 Vector2 AAOffset = paintInfo.deriveAAOffset(normal);
Chris Craik65cd6122012-12-10 17:56:27 -0800414
John Reck1aa5d2d2014-07-24 13:38:28 -0700415 Vector2 strokeOffset = normal;
Chris Craik65cd6122012-12-10 17:56:27 -0800416 paintInfo.scaleOffsetForStrokeWidth(strokeOffset);
John Reck1aa5d2d2014-07-24 13:38:28 -0700417 Vector2 outerOffset = strokeOffset + AAOffset;
418 Vector2 innerOffset = strokeOffset - AAOffset;
Chris Craik65cd6122012-12-10 17:56:27 -0800419
John Reck1aa5d2d2014-07-24 13:38:28 -0700420 Vector2 capAAOffset = {0, 0};
Chris Craik65cd6122012-12-10 17:56:27 -0800421 if (paintInfo.cap != SkPaint::kRound_Cap) {
422 // if the cap is square or butt, the inside primary cap vertices will be inset in two
423 // directions - both normal to the stroke, and parallel to it.
John Reck1aa5d2d2014-07-24 13:38:28 -0700424 capAAOffset = (Vector2){-AAOffset.y, AAOffset.x};
Chris Craik65cd6122012-12-10 17:56:27 -0800425 }
426
427 // determine referencePoint, the center point for the 4 primary cap vertices
428 const Vertex* point = isFirst ? vertices.begin() : (vertices.end() - 1);
John Reck1aa5d2d2014-07-24 13:38:28 -0700429 Vector2 referencePoint = {point->x, point->y};
Chris Craik65cd6122012-12-10 17:56:27 -0800430 if (paintInfo.cap == SkPaint::kSquare_Cap) {
431 // To account for square cap, move the primary cap vertices (that create the AA edge) by the
432 // stroke offset vector (rotated to be parallel to the stroke)
John Reck1aa5d2d2014-07-24 13:38:28 -0700433 Vector2 rotated = {-strokeOffset.y, strokeOffset.x};
434 referencePoint += rotated;
Chris Craik65cd6122012-12-10 17:56:27 -0800435 }
436
437 AlphaVertex::set(&buffer[capIndex + 0],
438 referencePoint.x + outerOffset.x + capAAOffset.x,
439 referencePoint.y + outerOffset.y + capAAOffset.y,
440 0.0f);
441 AlphaVertex::set(&buffer[capIndex + 1],
442 referencePoint.x + innerOffset.x - capAAOffset.x,
443 referencePoint.y + innerOffset.y - capAAOffset.y,
444 paintInfo.maxAlpha);
445
446 bool isRound = paintInfo.cap == SkPaint::kRound_Cap;
447
448 const int postCapIndex = (isRound && isFirst) ? (2 * extraOffset - 2) : capIndex + (2 * extra);
449 AlphaVertex::set(&buffer[postCapIndex + 2],
450 referencePoint.x - outerOffset.x + capAAOffset.x,
451 referencePoint.y - outerOffset.y + capAAOffset.y,
452 0.0f);
453 AlphaVertex::set(&buffer[postCapIndex + 3],
454 referencePoint.x - innerOffset.x - capAAOffset.x,
455 referencePoint.y - innerOffset.y - capAAOffset.y,
456 paintInfo.maxAlpha);
457
458 if (isRound) {
459 const float dTheta = PI / (extra + 1);
460 const float radialScale = 2.0f / (1 + cos(dTheta));
461 float theta = atan2(normal.y, normal.x);
462 int capPerimIndex = capIndex + 2;
463
464 for (int i = 0; i < extra; i++) {
465 theta += dTheta;
466
Dan Albertff1d8a62014-11-11 19:31:26 -0800467 Vector2 radialOffset = {cosf(theta), sinf(theta)};
Chris Craik65cd6122012-12-10 17:56:27 -0800468
469 // scale to compensate for pinching at sharp angles, see totalOffsetFromNormals()
470 radialOffset *= radialScale;
471
472 AAOffset = paintInfo.deriveAAOffset(radialOffset);
473 paintInfo.scaleOffsetForStrokeWidth(radialOffset);
474 AlphaVertex::set(&buffer[capPerimIndex++],
475 referencePoint.x + radialOffset.x + AAOffset.x,
476 referencePoint.y + radialOffset.y + AAOffset.y,
477 0.0f);
478 AlphaVertex::set(&buffer[capPerimIndex++],
479 referencePoint.x + radialOffset.x - AAOffset.x,
480 referencePoint.y + radialOffset.y - AAOffset.y,
481 paintInfo.maxAlpha);
482
483 if (isFirst && i == extra - extraOffset) {
484 //copy most recent two points to first two points
Chris Craik11a75672013-12-16 17:08:15 -0800485 buffer[0] = buffer[capPerimIndex - 2];
486 buffer[1] = buffer[capPerimIndex - 1];
Chris Craik65cd6122012-12-10 17:56:27 -0800487
488 capPerimIndex = 2; // start writing the rest of the round cap at index 2
489 }
490 }
491
492 if (isFirst) {
493 const int startCapFillIndex = capIndex + 2 * (extra - extraOffset) + 4;
494 int capFillIndex = startCapFillIndex;
495 for (int i = 0; i < extra + 2; i += 2) {
Chris Craik11a75672013-12-16 17:08:15 -0800496 buffer[capFillIndex++] = buffer[1 + i];
Chris Craik65cd6122012-12-10 17:56:27 -0800497 // TODO: to support odd numbers of divisions, break here on the last iteration
Chris Craik11a75672013-12-16 17:08:15 -0800498 buffer[capFillIndex++] = buffer[startCapFillIndex - 3 - i];
Chris Craik65cd6122012-12-10 17:56:27 -0800499 }
500 } else {
501 int capFillIndex = 6 * vertices.size() + 2 + 6 * extra - (extra + 2);
502 for (int i = 0; i < extra + 2; i += 2) {
Chris Craik11a75672013-12-16 17:08:15 -0800503 buffer[capFillIndex++] = buffer[capIndex + 1 + i];
Chris Craik65cd6122012-12-10 17:56:27 -0800504 // TODO: to support odd numbers of divisions, break here on the last iteration
Chris Craik11a75672013-12-16 17:08:15 -0800505 buffer[capFillIndex++] = buffer[capIndex + 3 + 2 * extra - i];
Chris Craik65cd6122012-12-10 17:56:27 -0800506 }
507 }
508 return;
509 }
510 if (isFirst) {
Chris Craik11a75672013-12-16 17:08:15 -0800511 buffer[0] = buffer[postCapIndex + 2];
512 buffer[1] = buffer[postCapIndex + 3];
513 buffer[postCapIndex + 4] = buffer[1]; // degenerate tris (the only two!)
514 buffer[postCapIndex + 5] = buffer[postCapIndex + 1];
Chris Craik65cd6122012-12-10 17:56:27 -0800515 } else {
Chris Craik11a75672013-12-16 17:08:15 -0800516 buffer[6 * vertices.size()] = buffer[postCapIndex + 1];
517 buffer[6 * vertices.size() + 1] = buffer[postCapIndex + 3];
Chris Craik65cd6122012-12-10 17:56:27 -0800518 }
519}
520
521/*
522the geometry for an aa, capped stroke consists of the following:
523
524 # vertices | function
525----------------------------------------------------------------------
526a) 2 | Start AA perimeter
527b) 2, 2 * roundDivOff | First half of begin cap's perimeter
528 |
529 2 * middlePts | 'Outer' or 'Top' AA perimeter half (between caps)
530 |
531a) 4 | End cap's
532b) 2, 2 * roundDivs, 2 | AA perimeter
533 |
534 2 * middlePts | 'Inner' or 'bottom' AA perimeter half
535 |
536a) 6 | Begin cap's perimeter
537b) 2, 2*(rD - rDO + 1), | Last half of begin cap's perimeter
538 roundDivs, 2 |
539 |
540 2 * middlePts | Stroke's full opacity center strip
541 |
542a) 2 | end stroke
543b) 2, roundDivs | (and end cap fill, for round)
544
545Notes:
546* rows starting with 'a)' denote the Butt or Square cap vertex use, 'b)' denote Round
547
548* 'middlePts' is (number of points in the unclosed input vertex list, minus 2) times two
549
550* 'roundDivs' or 'rD' is the number of extra vertices (beyond the minimum of 2) that define the
551 round cap's shape, and is at least two. This will increase with cap size to sufficiently
552 define the cap's level of tessellation.
553
554* 'roundDivOffset' or 'rDO' is the point about halfway along the start cap's round perimeter, where
555 the stream of vertices for the AA perimeter starts. By starting and ending the perimeter at
556 this offset, the fill of the stroke is drawn from this point with minimal extra vertices.
557
558This means the outer perimeter starts at:
559 outerIndex = (2) OR (2 + 2 * roundDivOff)
560the inner perimeter (since it is filled in reverse) starts at:
561 innerIndex = outerIndex + (4 * middlePts) + ((4) OR (4 + 2 * roundDivs)) - 1
562the stroke starts at:
563 strokeIndex = innerIndex + 1 + ((6) OR (6 + 3 * roundDivs - 2 * roundDivOffset))
564
565The total needed allocated space is either:
566 2 + 4 + 6 + 2 + 3 * (2 * middlePts) = 14 + 6 * middlePts = 2 + 6 * pts
567or, for rounded caps:
568 (2 + 2 * rDO) + (4 + 2 * rD) + (2 * (rD - rDO + 1)
569 + roundDivs + 4) + (2 + roundDivs) + 3 * (2 * middlePts)
570 = 14 + 6 * middlePts + 6 * roundDivs
571 = 2 + 6 * pts + 6 * roundDivs
572 */
573void getStrokeVerticesFromUnclosedVerticesAA(const PaintInfo& paintInfo,
574 const Vector<Vertex>& vertices, VertexBuffer& vertexBuffer) {
575
576 const int extra = paintInfo.capExtraDivisions();
577 const int allocSize = 6 * vertices.size() + 2 + 6 * extra;
578
579 AlphaVertex* buffer = vertexBuffer.alloc<AlphaVertex>(allocSize);
580
581 const int extraOffset = (extra + 1) / 2;
582 int offset = 2 * (vertices.size() - 2);
583 // there is no outer/inner here, using them for consistency with below approach
584 int currentAAOuterIndex = 2 + 2 * extraOffset;
585 int currentAAInnerIndex = currentAAOuterIndex + (2 * offset) + 3 + (2 * extra);
586 int currentStrokeIndex = currentAAInnerIndex + 7 + (3 * extra - 2 * extraOffset);
587
588 const Vertex* last = &(vertices[0]);
589 const Vertex* current = &(vertices[1]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700590 Vector2 lastNormal = {current->y - last->y, last->x - current->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800591 lastNormal.normalize();
592
593 // TODO: use normal from bezier traversal for cap, instead of from vertices
594 storeCapAA(paintInfo, vertices, buffer, true, lastNormal, offset);
595
596 for (unsigned int i = 1; i < vertices.size() - 1; i++) {
597 const Vertex* next = &(vertices[i + 1]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700598 Vector2 nextNormal = {next->y - current->y, current->x - next->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800599 nextNormal.normalize();
600
John Reck1aa5d2d2014-07-24 13:38:28 -0700601 Vector2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
602 Vector2 AAOffset = paintInfo.deriveAAOffset(totalOffset);
Chris Craik65cd6122012-12-10 17:56:27 -0800603
John Reck1aa5d2d2014-07-24 13:38:28 -0700604 Vector2 innerOffset = totalOffset;
Chris Craik65cd6122012-12-10 17:56:27 -0800605 paintInfo.scaleOffsetForStrokeWidth(innerOffset);
John Reck1aa5d2d2014-07-24 13:38:28 -0700606 Vector2 outerOffset = innerOffset + AAOffset;
Chris Craik65cd6122012-12-10 17:56:27 -0800607 innerOffset -= AAOffset;
608
609 AlphaVertex::set(&buffer[currentAAOuterIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700610 current->x + outerOffset.x,
611 current->y + outerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800612 0.0f);
613 AlphaVertex::set(&buffer[currentAAOuterIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700614 current->x + innerOffset.x,
615 current->y + innerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800616 paintInfo.maxAlpha);
617
618 AlphaVertex::set(&buffer[currentStrokeIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700619 current->x + innerOffset.x,
620 current->y + innerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800621 paintInfo.maxAlpha);
622 AlphaVertex::set(&buffer[currentStrokeIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700623 current->x - innerOffset.x,
624 current->y - innerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800625 paintInfo.maxAlpha);
626
627 AlphaVertex::set(&buffer[currentAAInnerIndex--],
Romain Guy3380cfd2013-08-15 16:57:57 -0700628 current->x - innerOffset.x,
629 current->y - innerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800630 paintInfo.maxAlpha);
631 AlphaVertex::set(&buffer[currentAAInnerIndex--],
Romain Guy3380cfd2013-08-15 16:57:57 -0700632 current->x - outerOffset.x,
633 current->y - outerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800634 0.0f);
635
636 current = next;
637 lastNormal = nextNormal;
638 }
639
640 // TODO: use normal from bezier traversal for cap, instead of from vertices
641 storeCapAA(paintInfo, vertices, buffer, false, lastNormal, offset);
642
643 DEBUG_DUMP_ALPHA_BUFFER();
644}
645
646
647void getStrokeVerticesFromPerimeterAA(const PaintInfo& paintInfo, const Vector<Vertex>& perimeter,
648 VertexBuffer& vertexBuffer) {
649 AlphaVertex* buffer = vertexBuffer.alloc<AlphaVertex>(6 * perimeter.size() + 8);
650
651 int offset = 2 * perimeter.size() + 3;
652 int currentAAOuterIndex = 0;
653 int currentStrokeIndex = offset;
654 int currentAAInnerIndex = offset * 2;
655
656 const Vertex* last = &(perimeter[perimeter.size() - 1]);
657 const Vertex* current = &(perimeter[0]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700658 Vector2 lastNormal = {current->y - last->y, last->x - current->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800659 lastNormal.normalize();
660 for (unsigned int i = 0; i < perimeter.size(); i++) {
661 const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700662 Vector2 nextNormal = {next->y - current->y, current->x - next->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800663 nextNormal.normalize();
664
John Reck1aa5d2d2014-07-24 13:38:28 -0700665 Vector2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
666 Vector2 AAOffset = paintInfo.deriveAAOffset(totalOffset);
Chris Craik65cd6122012-12-10 17:56:27 -0800667
John Reck1aa5d2d2014-07-24 13:38:28 -0700668 Vector2 innerOffset = totalOffset;
Chris Craik65cd6122012-12-10 17:56:27 -0800669 paintInfo.scaleOffsetForStrokeWidth(innerOffset);
John Reck1aa5d2d2014-07-24 13:38:28 -0700670 Vector2 outerOffset = innerOffset + AAOffset;
Chris Craik65cd6122012-12-10 17:56:27 -0800671 innerOffset -= AAOffset;
672
673 AlphaVertex::set(&buffer[currentAAOuterIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700674 current->x + outerOffset.x,
675 current->y + outerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800676 0.0f);
677 AlphaVertex::set(&buffer[currentAAOuterIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700678 current->x + innerOffset.x,
679 current->y + innerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800680 paintInfo.maxAlpha);
681
682 AlphaVertex::set(&buffer[currentStrokeIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700683 current->x + innerOffset.x,
684 current->y + innerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800685 paintInfo.maxAlpha);
686 AlphaVertex::set(&buffer[currentStrokeIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700687 current->x - innerOffset.x,
688 current->y - innerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800689 paintInfo.maxAlpha);
690
691 AlphaVertex::set(&buffer[currentAAInnerIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700692 current->x - innerOffset.x,
693 current->y - innerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800694 paintInfo.maxAlpha);
695 AlphaVertex::set(&buffer[currentAAInnerIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700696 current->x - outerOffset.x,
697 current->y - outerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800698 0.0f);
699
Chris Craik65cd6122012-12-10 17:56:27 -0800700 current = next;
701 lastNormal = nextNormal;
702 }
703
704 // wrap each strip around to beginning, creating degenerate tris to bridge strips
Chris Craik11a75672013-12-16 17:08:15 -0800705 buffer[currentAAOuterIndex++] = buffer[0];
706 buffer[currentAAOuterIndex++] = buffer[1];
707 buffer[currentAAOuterIndex++] = buffer[1];
Chris Craik65cd6122012-12-10 17:56:27 -0800708
Chris Craik11a75672013-12-16 17:08:15 -0800709 buffer[currentStrokeIndex++] = buffer[offset];
710 buffer[currentStrokeIndex++] = buffer[offset + 1];
711 buffer[currentStrokeIndex++] = buffer[offset + 1];
Chris Craik65cd6122012-12-10 17:56:27 -0800712
Chris Craik11a75672013-12-16 17:08:15 -0800713 buffer[currentAAInnerIndex++] = buffer[2 * offset];
714 buffer[currentAAInnerIndex++] = buffer[2 * offset + 1];
Chris Craik65cd6122012-12-10 17:56:27 -0800715 // don't need to create last degenerate tri
716
717 DEBUG_DUMP_ALPHA_BUFFER();
718}
719
720void PathTessellator::tessellatePath(const SkPath &path, const SkPaint* paint,
Chris Craikd6b65f62014-01-01 14:45:21 -0800721 const mat4& transform, VertexBuffer& vertexBuffer) {
Chris Craik65cd6122012-12-10 17:56:27 -0800722 ATRACE_CALL();
723
724 const PaintInfo paintInfo(paint, transform);
725
726 Vector<Vertex> tempVertices;
727 float threshInvScaleX = paintInfo.inverseScaleX;
728 float threshInvScaleY = paintInfo.inverseScaleY;
729 if (paintInfo.style == SkPaint::kStroke_Style) {
730 // alter the bezier recursion threshold values we calculate in order to compensate for
731 // expansion done after the path vertices are found
732 SkRect bounds = path.getBounds();
733 if (!bounds.isEmpty()) {
734 threshInvScaleX *= bounds.width() / (bounds.width() + paint->getStrokeWidth());
735 threshInvScaleY *= bounds.height() / (bounds.height() + paint->getStrokeWidth());
736 }
737 }
738
739 // force close if we're filling the path, since fill path expects closed perimeter.
740 bool forceClose = paintInfo.style != SkPaint::kStroke_Style;
741 bool wasClosed = approximatePathOutlineVertices(path, forceClose,
Chris Craik15a07a22014-01-26 13:43:53 -0800742 threshInvScaleX * threshInvScaleX, threshInvScaleY * threshInvScaleY,
743 OUTLINE_REFINE_THRESHOLD_SQUARED, tempVertices);
Chris Craik65cd6122012-12-10 17:56:27 -0800744
745 if (!tempVertices.size()) {
746 // path was empty, return without allocating vertex buffer
747 return;
748 }
749
750#if VERTEX_DEBUG
751 for (unsigned int i = 0; i < tempVertices.size(); i++) {
752 ALOGD("orig path: point at %f %f",
Romain Guy3380cfd2013-08-15 16:57:57 -0700753 tempVertices[i].x, tempVertices[i].y);
Chris Craik65cd6122012-12-10 17:56:27 -0800754 }
755#endif
756
757 if (paintInfo.style == SkPaint::kStroke_Style) {
758 if (!paintInfo.isAA) {
759 if (wasClosed) {
760 getStrokeVerticesFromPerimeter(paintInfo, tempVertices, vertexBuffer);
761 } else {
762 getStrokeVerticesFromUnclosedVertices(paintInfo, tempVertices, vertexBuffer);
763 }
764
765 } else {
766 if (wasClosed) {
767 getStrokeVerticesFromPerimeterAA(paintInfo, tempVertices, vertexBuffer);
768 } else {
769 getStrokeVerticesFromUnclosedVerticesAA(paintInfo, tempVertices, vertexBuffer);
770 }
771 }
772 } else {
773 // For kStrokeAndFill style, the path should be adjusted externally.
774 // It will be treated as a fill here.
775 if (!paintInfo.isAA) {
776 getFillVerticesFromPerimeter(tempVertices, vertexBuffer);
777 } else {
778 getFillVerticesFromPerimeterAA(paintInfo, tempVertices, vertexBuffer);
779 }
780 }
Chris Craik05f3d6e2014-06-02 16:27:04 -0700781
782 Rect bounds(path.getBounds());
783 paintInfo.expandBoundsForStroke(&bounds);
784 vertexBuffer.setBounds(bounds);
Chris Craik117bdbc2015-02-05 10:12:38 -0800785 vertexBuffer.setMeshFeatureFlags(paintInfo.isAA ? VertexBuffer::kAlpha : VertexBuffer::kNone);
Chris Craik65cd6122012-12-10 17:56:27 -0800786}
787
Chris Craik6d29c8d2013-05-08 18:35:44 -0700788template <class TYPE>
789static void instanceVertices(VertexBuffer& srcBuffer, VertexBuffer& dstBuffer,
Chris Craik05f3d6e2014-06-02 16:27:04 -0700790 const float* points, int count, Rect& bounds) {
Chris Craik6d29c8d2013-05-08 18:35:44 -0700791 bounds.set(points[0], points[1], points[0], points[1]);
792
793 int numPoints = count / 2;
794 int verticesPerPoint = srcBuffer.getVertexCount();
795 dstBuffer.alloc<TYPE>(numPoints * verticesPerPoint + (numPoints - 1) * 2);
796
797 for (int i = 0; i < count; i += 2) {
Chris Craikc93e45c2014-07-16 10:15:56 -0700798 bounds.expandToCoverVertex(points[i + 0], points[i + 1]);
Chris Craik6d29c8d2013-05-08 18:35:44 -0700799 dstBuffer.copyInto<TYPE>(srcBuffer, points[i + 0], points[i + 1]);
800 }
801 dstBuffer.createDegenerateSeparators<TYPE>(verticesPerPoint);
802}
803
Chris Craikd218a922014-01-02 17:13:34 -0800804void PathTessellator::tessellatePoints(const float* points, int count, const SkPaint* paint,
Chris Craik05f3d6e2014-06-02 16:27:04 -0700805 const mat4& transform, VertexBuffer& vertexBuffer) {
Chris Craik6d29c8d2013-05-08 18:35:44 -0700806 const PaintInfo paintInfo(paint, transform);
807
808 // determine point shape
809 SkPath path;
810 float radius = paintInfo.halfStrokeWidth;
Rob Tsukb7c26562014-11-03 16:29:03 -0800811 if (radius == 0.0f) radius = 0.5f;
Chris Craik6d29c8d2013-05-08 18:35:44 -0700812
813 if (paintInfo.cap == SkPaint::kRound_Cap) {
814 path.addCircle(0, 0, radius);
815 } else {
816 path.addRect(-radius, -radius, radius, radius);
817 }
818
819 // calculate outline
820 Vector<Vertex> outlineVertices;
821 approximatePathOutlineVertices(path, true,
822 paintInfo.inverseScaleX * paintInfo.inverseScaleX,
Chris Craik15a07a22014-01-26 13:43:53 -0800823 paintInfo.inverseScaleY * paintInfo.inverseScaleY,
824 OUTLINE_REFINE_THRESHOLD_SQUARED, outlineVertices);
Chris Craik6d29c8d2013-05-08 18:35:44 -0700825
826 if (!outlineVertices.size()) return;
827
Chris Craik05f3d6e2014-06-02 16:27:04 -0700828 Rect bounds;
Chris Craik6d29c8d2013-05-08 18:35:44 -0700829 // tessellate, then duplicate outline across points
Chris Craik6d29c8d2013-05-08 18:35:44 -0700830 VertexBuffer tempBuffer;
831 if (!paintInfo.isAA) {
832 getFillVerticesFromPerimeter(outlineVertices, tempBuffer);
833 instanceVertices<Vertex>(tempBuffer, vertexBuffer, points, count, bounds);
834 } else {
Chris Craikf0a59072013-11-19 18:00:46 -0800835 // note: pass maxAlpha directly, since we want fill to be alpha modulated
836 getFillVerticesFromPerimeterAA(paintInfo, outlineVertices, tempBuffer, paintInfo.maxAlpha);
Chris Craik6d29c8d2013-05-08 18:35:44 -0700837 instanceVertices<AlphaVertex>(tempBuffer, vertexBuffer, points, count, bounds);
838 }
839
Chris Craikf0a59072013-11-19 18:00:46 -0800840 // expand bounds from vertex coords to pixel data
Chris Craik05f3d6e2014-06-02 16:27:04 -0700841 paintInfo.expandBoundsForStroke(&bounds);
842 vertexBuffer.setBounds(bounds);
Chris Craik117bdbc2015-02-05 10:12:38 -0800843 vertexBuffer.setMeshFeatureFlags(paintInfo.isAA ? VertexBuffer::kAlpha : VertexBuffer::kNone);
Chris Craik65cd6122012-12-10 17:56:27 -0800844}
845
Chris Craikd218a922014-01-02 17:13:34 -0800846void PathTessellator::tessellateLines(const float* points, int count, const SkPaint* paint,
Chris Craik05f3d6e2014-06-02 16:27:04 -0700847 const mat4& transform, VertexBuffer& vertexBuffer) {
Chris Craik65cd6122012-12-10 17:56:27 -0800848 ATRACE_CALL();
849 const PaintInfo paintInfo(paint, transform);
850
851 const int extra = paintInfo.capExtraDivisions();
852 int numLines = count / 4;
853 int lineAllocSize;
854 // pre-allocate space for lines in the buffer, and degenerate tris in between
855 if (paintInfo.isAA) {
856 lineAllocSize = 6 * (2) + 2 + 6 * extra;
857 vertexBuffer.alloc<AlphaVertex>(numLines * lineAllocSize + (numLines - 1) * 2);
858 } else {
859 lineAllocSize = 2 * ((2) + extra);
860 vertexBuffer.alloc<Vertex>(numLines * lineAllocSize + (numLines - 1) * 2);
861 }
862
863 Vector<Vertex> tempVertices;
864 tempVertices.push();
865 tempVertices.push();
866 Vertex* tempVerticesData = tempVertices.editArray();
Chris Craik05f3d6e2014-06-02 16:27:04 -0700867 Rect bounds;
Chris Craik65cd6122012-12-10 17:56:27 -0800868 bounds.set(points[0], points[1], points[0], points[1]);
869 for (int i = 0; i < count; i += 4) {
870 Vertex::set(&(tempVerticesData[0]), points[i + 0], points[i + 1]);
871 Vertex::set(&(tempVerticesData[1]), points[i + 2], points[i + 3]);
872
873 if (paintInfo.isAA) {
874 getStrokeVerticesFromUnclosedVerticesAA(paintInfo, tempVertices, vertexBuffer);
875 } else {
876 getStrokeVerticesFromUnclosedVertices(paintInfo, tempVertices, vertexBuffer);
877 }
878
879 // calculate bounds
Chris Craikc93e45c2014-07-16 10:15:56 -0700880 bounds.expandToCoverVertex(tempVerticesData[0].x, tempVerticesData[0].y);
881 bounds.expandToCoverVertex(tempVerticesData[1].x, tempVerticesData[1].y);
Chris Craik65cd6122012-12-10 17:56:27 -0800882 }
883
Chris Craik65cd6122012-12-10 17:56:27 -0800884 // since multiple objects tessellated into buffer, separate them with degen tris
885 if (paintInfo.isAA) {
886 vertexBuffer.createDegenerateSeparators<AlphaVertex>(lineAllocSize);
887 } else {
888 vertexBuffer.createDegenerateSeparators<Vertex>(lineAllocSize);
889 }
Chris Craikf0a59072013-11-19 18:00:46 -0800890
891 // expand bounds from vertex coords to pixel data
Chris Craik05f3d6e2014-06-02 16:27:04 -0700892 paintInfo.expandBoundsForStroke(&bounds);
893 vertexBuffer.setBounds(bounds);
Chris Craik117bdbc2015-02-05 10:12:38 -0800894 vertexBuffer.setMeshFeatureFlags(paintInfo.isAA ? VertexBuffer::kAlpha : VertexBuffer::kNone);
Chris Craik65cd6122012-12-10 17:56:27 -0800895}
896
897///////////////////////////////////////////////////////////////////////////////
898// Simple path line approximation
899///////////////////////////////////////////////////////////////////////////////
900
Chris Craik15a07a22014-01-26 13:43:53 -0800901bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, float thresholdSquared,
902 Vector<Vertex>& outputVertices) {
903 return approximatePathOutlineVertices(path, true, 1.0f, 1.0f, thresholdSquared, outputVertices);
904}
905
Chris Craik65cd6122012-12-10 17:56:27 -0800906void pushToVector(Vector<Vertex>& vertices, float x, float y) {
907 // TODO: make this not yuck
908 vertices.push();
909 Vertex* newVertex = &(vertices.editArray()[vertices.size() - 1]);
910 Vertex::set(newVertex, x, y);
911}
912
913bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool forceClose,
Chris Craik15a07a22014-01-26 13:43:53 -0800914 float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared,
915 Vector<Vertex>& outputVertices) {
Chris Craik65cd6122012-12-10 17:56:27 -0800916 ATRACE_CALL();
917
918 // TODO: to support joins other than sharp miter, join vertices should be labelled in the
919 // perimeter, or resolved into more vertices. Reconsider forceClose-ing in that case.
920 SkPath::Iter iter(path, forceClose);
921 SkPoint pts[4];
922 SkPath::Verb v;
923 while (SkPath::kDone_Verb != (v = iter.next(pts))) {
924 switch (v) {
925 case SkPath::kMove_Verb:
926 pushToVector(outputVertices, pts[0].x(), pts[0].y());
927 ALOGV("Move to pos %f %f", pts[0].x(), pts[0].y());
928 break;
929 case SkPath::kClose_Verb:
930 ALOGV("Close at pos %f %f", pts[0].x(), pts[0].y());
931 break;
932 case SkPath::kLine_Verb:
933 ALOGV("kLine_Verb %f %f -> %f %f", pts[0].x(), pts[0].y(), pts[1].x(), pts[1].y());
934 pushToVector(outputVertices, pts[1].x(), pts[1].y());
935 break;
936 case SkPath::kQuad_Verb:
937 ALOGV("kQuad_Verb");
938 recursiveQuadraticBezierVertices(
939 pts[0].x(), pts[0].y(),
940 pts[2].x(), pts[2].y(),
941 pts[1].x(), pts[1].y(),
Chris Craik15a07a22014-01-26 13:43:53 -0800942 sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices);
Chris Craik65cd6122012-12-10 17:56:27 -0800943 break;
944 case SkPath::kCubic_Verb:
945 ALOGV("kCubic_Verb");
946 recursiveCubicBezierVertices(
947 pts[0].x(), pts[0].y(),
948 pts[1].x(), pts[1].y(),
949 pts[3].x(), pts[3].y(),
950 pts[2].x(), pts[2].y(),
Chris Craik15a07a22014-01-26 13:43:53 -0800951 sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices);
Chris Craik65cd6122012-12-10 17:56:27 -0800952 break;
Derek Sollenbergerb0772a62015-03-16 14:35:55 -0400953 case SkPath::kConic_Verb: {
954 ALOGV("kConic_Verb");
955 SkAutoConicToQuads converter;
956 const SkPoint* quads = converter.computeQuads(pts, iter.conicWeight(),
957 thresholdSquared);
958 for (int i = 0; i < converter.countQuads(); ++i) {
959 const int offset = 2 * i;
960 recursiveQuadraticBezierVertices(
961 quads[offset].x(), quads[offset].y(),
962 quads[offset+2].x(), quads[offset+2].y(),
963 quads[offset+1].x(), quads[offset+1].y(),
964 sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices);
965 }
966 break;
967 }
Chris Craik65cd6122012-12-10 17:56:27 -0800968 default:
969 break;
970 }
971 }
972
973 int size = outputVertices.size();
Romain Guy3380cfd2013-08-15 16:57:57 -0700974 if (size >= 2 && outputVertices[0].x == outputVertices[size - 1].x &&
975 outputVertices[0].y == outputVertices[size - 1].y) {
Chris Craik65cd6122012-12-10 17:56:27 -0800976 outputVertices.pop();
977 return true;
978 }
979 return false;
980}
981
982///////////////////////////////////////////////////////////////////////////////
983// Bezier approximation
984///////////////////////////////////////////////////////////////////////////////
985
986void PathTessellator::recursiveCubicBezierVertices(
987 float p1x, float p1y, float c1x, float c1y,
988 float p2x, float p2y, float c2x, float c2y,
Chris Craik15a07a22014-01-26 13:43:53 -0800989 float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared,
Chris Craik9c3dd622014-06-11 17:24:51 -0700990 Vector<Vertex>& outputVertices, int depth) {
Chris Craik65cd6122012-12-10 17:56:27 -0800991 float dx = p2x - p1x;
992 float dy = p2y - p1y;
993 float d1 = fabs((c1x - p2x) * dy - (c1y - p2y) * dx);
994 float d2 = fabs((c2x - p2x) * dy - (c2y - p2y) * dx);
995 float d = d1 + d2;
996
997 // multiplying by sqrInvScaleY/X equivalent to multiplying in dimensional scale factors
Chris Craik74cf7e62014-08-07 14:34:46 -0700998 if (depth >= MAX_DEPTH
999 || d * d <= thresholdSquared * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) {
Chris Craik65cd6122012-12-10 17:56:27 -08001000 // below thresh, draw line by adding endpoint
1001 pushToVector(outputVertices, p2x, p2y);
1002 } else {
1003 float p1c1x = (p1x + c1x) * 0.5f;
1004 float p1c1y = (p1y + c1y) * 0.5f;
1005 float p2c2x = (p2x + c2x) * 0.5f;
1006 float p2c2y = (p2y + c2y) * 0.5f;
1007
1008 float c1c2x = (c1x + c2x) * 0.5f;
1009 float c1c2y = (c1y + c2y) * 0.5f;
1010
1011 float p1c1c2x = (p1c1x + c1c2x) * 0.5f;
1012 float p1c1c2y = (p1c1y + c1c2y) * 0.5f;
1013
1014 float p2c1c2x = (p2c2x + c1c2x) * 0.5f;
1015 float p2c1c2y = (p2c2y + c1c2y) * 0.5f;
1016
1017 float mx = (p1c1c2x + p2c1c2x) * 0.5f;
1018 float my = (p1c1c2y + p2c1c2y) * 0.5f;
1019
1020 recursiveCubicBezierVertices(
1021 p1x, p1y, p1c1x, p1c1y,
1022 mx, my, p1c1c2x, p1c1c2y,
Chris Craik9c3dd622014-06-11 17:24:51 -07001023 sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices, depth + 1);
Chris Craik65cd6122012-12-10 17:56:27 -08001024 recursiveCubicBezierVertices(
1025 mx, my, p2c1c2x, p2c1c2y,
1026 p2x, p2y, p2c2x, p2c2y,
Chris Craik9c3dd622014-06-11 17:24:51 -07001027 sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices, depth + 1);
Chris Craik65cd6122012-12-10 17:56:27 -08001028 }
1029}
1030
1031void PathTessellator::recursiveQuadraticBezierVertices(
1032 float ax, float ay,
1033 float bx, float by,
1034 float cx, float cy,
Chris Craik15a07a22014-01-26 13:43:53 -08001035 float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared,
Chris Craik9c3dd622014-06-11 17:24:51 -07001036 Vector<Vertex>& outputVertices, int depth) {
Chris Craik65cd6122012-12-10 17:56:27 -08001037 float dx = bx - ax;
1038 float dy = by - ay;
1039 float d = (cx - bx) * dy - (cy - by) * dx;
1040
Chris Craik74cf7e62014-08-07 14:34:46 -07001041 // multiplying by sqrInvScaleY/X equivalent to multiplying in dimensional scale factors
1042 if (depth >= MAX_DEPTH
1043 || d * d <= thresholdSquared * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) {
Chris Craik65cd6122012-12-10 17:56:27 -08001044 // below thresh, draw line by adding endpoint
1045 pushToVector(outputVertices, bx, by);
1046 } else {
1047 float acx = (ax + cx) * 0.5f;
1048 float bcx = (bx + cx) * 0.5f;
1049 float acy = (ay + cy) * 0.5f;
1050 float bcy = (by + cy) * 0.5f;
1051
1052 // midpoint
1053 float mx = (acx + bcx) * 0.5f;
1054 float my = (acy + bcy) * 0.5f;
1055
1056 recursiveQuadraticBezierVertices(ax, ay, mx, my, acx, acy,
Chris Craik9c3dd622014-06-11 17:24:51 -07001057 sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices, depth + 1);
Chris Craik65cd6122012-12-10 17:56:27 -08001058 recursiveQuadraticBezierVertices(mx, my, bx, by, bcx, bcy,
Chris Craik9c3dd622014-06-11 17:24:51 -07001059 sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices, depth + 1);
Chris Craik65cd6122012-12-10 17:56:27 -08001060 }
1061}
1062
1063}; // namespace uirenderer
1064}; // namespace android