blob: c1f61d696b006f7991c5f5500f811354b4f9cfec [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) {
154 if (halfStrokeWidth == 0.0f) return 2;
155
156 // ROUND_CAP_THRESH is the maximum error for polygonal approximation of the round cap
157 const float errConst = (-ROUND_CAP_THRESH / halfStrokeWidth + 1);
158 const float targetCosVal = 2 * errConst * errConst - 1;
159 int neededDivisions = (int)(ceilf(PI / acos(targetCosVal)/2)) * 2;
160 return neededDivisions;
161 }
162 return 0;
163 }
Chris Craikf0a59072013-11-19 18:00:46 -0800164
165 /**
Chris Craik21bcfc4f2014-09-08 18:43:30 -0700166 * Outset the bounds of point data (for line endpoints or points) to account for stroke
Chris Craikf0a59072013-11-19 18:00:46 -0800167 * geometry.
Chris Craik21bcfc4f2014-09-08 18:43:30 -0700168 *
169 * bounds are in pre-scaled space.
Chris Craikf0a59072013-11-19 18:00:46 -0800170 */
Chris Craik05f3d6e2014-06-02 16:27:04 -0700171 void expandBoundsForStroke(Rect* bounds) const {
Chris Craik21bcfc4f2014-09-08 18:43:30 -0700172 if (halfStrokeWidth == 0) {
173 // hairline, outset by (0.5f + fudge factor) in post-scaling space
174 bounds->outset(fabs(inverseScaleX) * (0.5f + Vertex::GeometryFudgeFactor()),
175 fabs(inverseScaleY) * (0.5f + Vertex::GeometryFudgeFactor()));
176 } else {
177 // non hairline, outset by half stroke width pre-scaled, and fudge factor post scaled
178 bounds->outset(halfStrokeWidth + fabs(inverseScaleX) * Vertex::GeometryFudgeFactor(),
179 halfStrokeWidth + fabs(inverseScaleY) * Vertex::GeometryFudgeFactor());
180 }
Chris Craikf0a59072013-11-19 18:00:46 -0800181 }
Chris Craik65cd6122012-12-10 17:56:27 -0800182};
183
184void getFillVerticesFromPerimeter(const Vector<Vertex>& perimeter, VertexBuffer& vertexBuffer) {
185 Vertex* buffer = vertexBuffer.alloc<Vertex>(perimeter.size());
186
187 int currentIndex = 0;
188 // zig zag between all previous points on the inside of the hull to create a
189 // triangle strip that fills the hull
190 int srcAindex = 0;
191 int srcBindex = perimeter.size() - 1;
192 while (srcAindex <= srcBindex) {
Chris Craik11a75672013-12-16 17:08:15 -0800193 buffer[currentIndex++] = perimeter[srcAindex];
Chris Craik65cd6122012-12-10 17:56:27 -0800194 if (srcAindex == srcBindex) break;
Chris Craik11a75672013-12-16 17:08:15 -0800195 buffer[currentIndex++] = perimeter[srcBindex];
Chris Craik65cd6122012-12-10 17:56:27 -0800196 srcAindex++;
197 srcBindex--;
198 }
199}
200
201/*
202 * Fills a vertexBuffer with non-alpha vertices, zig-zagging at each perimeter point to create a
203 * tri-strip as wide as the stroke.
204 *
205 * Uses an additional 2 vertices at the end to wrap around, closing the tri-strip
206 * (for a total of perimeter.size() * 2 + 2 vertices)
207 */
208void getStrokeVerticesFromPerimeter(const PaintInfo& paintInfo, const Vector<Vertex>& perimeter,
209 VertexBuffer& vertexBuffer) {
210 Vertex* buffer = vertexBuffer.alloc<Vertex>(perimeter.size() * 2 + 2);
211
212 int currentIndex = 0;
213 const Vertex* last = &(perimeter[perimeter.size() - 1]);
214 const Vertex* current = &(perimeter[0]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700215 Vector2 lastNormal = {current->y - last->y, last->x - current->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800216 lastNormal.normalize();
217 for (unsigned int i = 0; i < perimeter.size(); i++) {
218 const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700219 Vector2 nextNormal = {next->y - current->y, current->x - next->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800220 nextNormal.normalize();
221
John Reck1aa5d2d2014-07-24 13:38:28 -0700222 Vector2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
Chris Craik65cd6122012-12-10 17:56:27 -0800223 paintInfo.scaleOffsetForStrokeWidth(totalOffset);
224
225 Vertex::set(&buffer[currentIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700226 current->x + totalOffset.x,
227 current->y + totalOffset.y);
Chris Craik65cd6122012-12-10 17:56:27 -0800228
229 Vertex::set(&buffer[currentIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700230 current->x - totalOffset.x,
231 current->y - totalOffset.y);
Chris Craik65cd6122012-12-10 17:56:27 -0800232
Chris Craik65cd6122012-12-10 17:56:27 -0800233 current = next;
234 lastNormal = nextNormal;
235 }
236
237 // wrap around to beginning
Chris Craik11a75672013-12-16 17:08:15 -0800238 buffer[currentIndex++] = buffer[0];
239 buffer[currentIndex++] = buffer[1];
Chris Craik65cd6122012-12-10 17:56:27 -0800240
241 DEBUG_DUMP_BUFFER();
242}
243
Chris Craik6d29c8d2013-05-08 18:35:44 -0700244static inline void storeBeginEnd(const PaintInfo& paintInfo, const Vertex& center,
John Reck1aa5d2d2014-07-24 13:38:28 -0700245 const Vector2& normal, Vertex* buffer, int& currentIndex, bool begin) {
246 Vector2 strokeOffset = normal;
Chris Craik6d29c8d2013-05-08 18:35:44 -0700247 paintInfo.scaleOffsetForStrokeWidth(strokeOffset);
248
John Reck1aa5d2d2014-07-24 13:38:28 -0700249 Vector2 referencePoint = {center.x, center.y};
Chris Craik6d29c8d2013-05-08 18:35:44 -0700250 if (paintInfo.cap == SkPaint::kSquare_Cap) {
John Reck1aa5d2d2014-07-24 13:38:28 -0700251 Vector2 rotated = {-strokeOffset.y, strokeOffset.x};
252 referencePoint += rotated * (begin ? -1 : 1);
Chris Craik6d29c8d2013-05-08 18:35:44 -0700253 }
254
255 Vertex::set(&buffer[currentIndex++], referencePoint + strokeOffset);
256 Vertex::set(&buffer[currentIndex++], referencePoint - strokeOffset);
257}
258
Chris Craik65cd6122012-12-10 17:56:27 -0800259/**
260 * Fills a vertexBuffer with non-alpha vertices similar to getStrokeVerticesFromPerimeter, except:
261 *
262 * 1 - Doesn't need to wrap around, since the input vertices are unclosed
263 *
264 * 2 - can zig-zag across 'extra' vertices at either end, to create round caps
265 */
266void getStrokeVerticesFromUnclosedVertices(const PaintInfo& paintInfo,
267 const Vector<Vertex>& vertices, VertexBuffer& vertexBuffer) {
268 const int extra = paintInfo.capExtraDivisions();
269 const int allocSize = (vertices.size() + extra) * 2;
Chris Craik65cd6122012-12-10 17:56:27 -0800270 Vertex* buffer = vertexBuffer.alloc<Vertex>(allocSize);
271
Chris Craik6d29c8d2013-05-08 18:35:44 -0700272 const int lastIndex = vertices.size() - 1;
Chris Craik65cd6122012-12-10 17:56:27 -0800273 if (extra > 0) {
274 // tessellate both round caps
Chris Craik65cd6122012-12-10 17:56:27 -0800275 float beginTheta = atan2(
Romain Guy3380cfd2013-08-15 16:57:57 -0700276 - (vertices[0].x - vertices[1].x),
277 vertices[0].y - vertices[1].y);
Chris Craik65cd6122012-12-10 17:56:27 -0800278 float endTheta = atan2(
Romain Guy3380cfd2013-08-15 16:57:57 -0700279 - (vertices[lastIndex].x - vertices[lastIndex - 1].x),
280 vertices[lastIndex].y - vertices[lastIndex - 1].y);
Chris Craik65cd6122012-12-10 17:56:27 -0800281 const float dTheta = PI / (extra + 1);
Chris Craik65cd6122012-12-10 17:56:27 -0800282
283 int capOffset;
284 for (int i = 0; i < extra; i++) {
285 if (i < extra / 2) {
286 capOffset = extra - 2 * i - 1;
287 } else {
288 capOffset = 2 * i - extra;
289 }
290
291 beginTheta += dTheta;
Dan Albertff1d8a62014-11-11 19:31:26 -0800292 Vector2 beginRadialOffset = {cosf(beginTheta), sinf(beginTheta)};
Chris Craik65cd6122012-12-10 17:56:27 -0800293 paintInfo.scaleOffsetForStrokeWidth(beginRadialOffset);
294 Vertex::set(&buffer[capOffset],
Romain Guy3380cfd2013-08-15 16:57:57 -0700295 vertices[0].x + beginRadialOffset.x,
296 vertices[0].y + beginRadialOffset.y);
Chris Craik65cd6122012-12-10 17:56:27 -0800297
298 endTheta += dTheta;
Dan Albertff1d8a62014-11-11 19:31:26 -0800299 Vector2 endRadialOffset = {cosf(endTheta), sinf(endTheta)};
Chris Craik65cd6122012-12-10 17:56:27 -0800300 paintInfo.scaleOffsetForStrokeWidth(endRadialOffset);
301 Vertex::set(&buffer[allocSize - 1 - capOffset],
Romain Guy3380cfd2013-08-15 16:57:57 -0700302 vertices[lastIndex].x + endRadialOffset.x,
303 vertices[lastIndex].y + endRadialOffset.y);
Chris Craik65cd6122012-12-10 17:56:27 -0800304 }
305 }
306
307 int currentIndex = extra;
Chris Craik6d29c8d2013-05-08 18:35:44 -0700308 const Vertex* last = &(vertices[0]);
309 const Vertex* current = &(vertices[1]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700310 Vector2 lastNormal = {current->y - last->y, last->x - current->x};
Chris Craik6d29c8d2013-05-08 18:35:44 -0700311 lastNormal.normalize();
312
313 storeBeginEnd(paintInfo, vertices[0], lastNormal, buffer, currentIndex, true);
314
315 for (unsigned int i = 1; i < vertices.size() - 1; i++) {
Chris Craik65cd6122012-12-10 17:56:27 -0800316 const Vertex* next = &(vertices[i + 1]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700317 Vector2 nextNormal = {next->y - current->y, current->x - next->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800318 nextNormal.normalize();
319
John Reck1aa5d2d2014-07-24 13:38:28 -0700320 Vector2 strokeOffset = totalOffsetFromNormals(lastNormal, nextNormal);
Chris Craik6d29c8d2013-05-08 18:35:44 -0700321 paintInfo.scaleOffsetForStrokeWidth(strokeOffset);
Chris Craik65cd6122012-12-10 17:56:27 -0800322
John Reck1aa5d2d2014-07-24 13:38:28 -0700323 Vector2 center = {current->x, current->y};
Chris Craik6d29c8d2013-05-08 18:35:44 -0700324 Vertex::set(&buffer[currentIndex++], center + strokeOffset);
325 Vertex::set(&buffer[currentIndex++], center - strokeOffset);
Chris Craik65cd6122012-12-10 17:56:27 -0800326
327 current = next;
328 lastNormal = nextNormal;
329 }
330
Chris Craik6d29c8d2013-05-08 18:35:44 -0700331 storeBeginEnd(paintInfo, vertices[lastIndex], lastNormal, buffer, currentIndex, false);
Chris Craik65cd6122012-12-10 17:56:27 -0800332
333 DEBUG_DUMP_BUFFER();
334}
335
336/**
337 * Populates a vertexBuffer with AlphaVertices to create an anti-aliased fill shape tessellation
Chris Craik6d29c8d2013-05-08 18:35:44 -0700338 *
Chris Craik65cd6122012-12-10 17:56:27 -0800339 * 1 - create the AA perimeter of unit width, by zig-zagging at each point around the perimeter of
340 * the shape (using 2 * perimeter.size() vertices)
341 *
342 * 2 - wrap around to the beginning to complete the perimeter (2 vertices)
343 *
344 * 3 - zig zag back and forth inside the shape to fill it (using perimeter.size() vertices)
345 */
346void getFillVerticesFromPerimeterAA(const PaintInfo& paintInfo, const Vector<Vertex>& perimeter,
Chris Craikf0a59072013-11-19 18:00:46 -0800347 VertexBuffer& vertexBuffer, float maxAlpha = 1.0f) {
Chris Craik65cd6122012-12-10 17:56:27 -0800348 AlphaVertex* buffer = vertexBuffer.alloc<AlphaVertex>(perimeter.size() * 3 + 2);
349
350 // generate alpha points - fill Alpha vertex gaps in between each point with
351 // alpha 0 vertex, offset by a scaled normal.
352 int currentIndex = 0;
353 const Vertex* last = &(perimeter[perimeter.size() - 1]);
354 const Vertex* current = &(perimeter[0]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700355 Vector2 lastNormal = {current->y - last->y, last->x - current->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800356 lastNormal.normalize();
357 for (unsigned int i = 0; i < perimeter.size(); i++) {
358 const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700359 Vector2 nextNormal = {next->y - current->y, current->x - next->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800360 nextNormal.normalize();
361
362 // AA point offset from original point is that point's normal, such that each side is offset
363 // by .5 pixels
John Reck1aa5d2d2014-07-24 13:38:28 -0700364 Vector2 totalOffset = paintInfo.deriveAAOffset(totalOffsetFromNormals(lastNormal, nextNormal));
Chris Craik65cd6122012-12-10 17:56:27 -0800365
366 AlphaVertex::set(&buffer[currentIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700367 current->x + totalOffset.x,
368 current->y + totalOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800369 0.0f);
370 AlphaVertex::set(&buffer[currentIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700371 current->x - totalOffset.x,
372 current->y - totalOffset.y,
Chris Craikf0a59072013-11-19 18:00:46 -0800373 maxAlpha);
Chris Craik65cd6122012-12-10 17:56:27 -0800374
Chris Craik65cd6122012-12-10 17:56:27 -0800375 current = next;
376 lastNormal = nextNormal;
377 }
378
379 // wrap around to beginning
Chris Craik11a75672013-12-16 17:08:15 -0800380 buffer[currentIndex++] = buffer[0];
381 buffer[currentIndex++] = buffer[1];
Chris Craik65cd6122012-12-10 17:56:27 -0800382
383 // zig zag between all previous points on the inside of the hull to create a
384 // triangle strip that fills the hull, repeating the first inner point to
385 // create degenerate tris to start inside path
386 int srcAindex = 0;
387 int srcBindex = perimeter.size() - 1;
388 while (srcAindex <= srcBindex) {
Chris Craik11a75672013-12-16 17:08:15 -0800389 buffer[currentIndex++] = buffer[srcAindex * 2 + 1];
Chris Craik65cd6122012-12-10 17:56:27 -0800390 if (srcAindex == srcBindex) break;
Chris Craik11a75672013-12-16 17:08:15 -0800391 buffer[currentIndex++] = buffer[srcBindex * 2 + 1];
Chris Craik65cd6122012-12-10 17:56:27 -0800392 srcAindex++;
393 srcBindex--;
394 }
395
396 DEBUG_DUMP_BUFFER();
397}
398
399/**
400 * Stores geometry for a single, AA-perimeter (potentially rounded) cap
401 *
402 * For explanation of constants and general methodoloyg, see comments for
403 * getStrokeVerticesFromUnclosedVerticesAA() below.
404 */
Chris Craik6d29c8d2013-05-08 18:35:44 -0700405inline static void storeCapAA(const PaintInfo& paintInfo, const Vector<Vertex>& vertices,
John Reck1aa5d2d2014-07-24 13:38:28 -0700406 AlphaVertex* buffer, bool isFirst, Vector2 normal, int offset) {
Chris Craik65cd6122012-12-10 17:56:27 -0800407 const int extra = paintInfo.capExtraDivisions();
408 const int extraOffset = (extra + 1) / 2;
409 const int capIndex = isFirst
410 ? 2 * offset + 6 + 2 * (extra + extraOffset)
411 : offset + 2 + 2 * extraOffset;
412 if (isFirst) normal *= -1;
413
414 // TODO: this normal should be scaled by radialScale if extra != 0, see totalOffsetFromNormals()
John Reck1aa5d2d2014-07-24 13:38:28 -0700415 Vector2 AAOffset = paintInfo.deriveAAOffset(normal);
Chris Craik65cd6122012-12-10 17:56:27 -0800416
John Reck1aa5d2d2014-07-24 13:38:28 -0700417 Vector2 strokeOffset = normal;
Chris Craik65cd6122012-12-10 17:56:27 -0800418 paintInfo.scaleOffsetForStrokeWidth(strokeOffset);
John Reck1aa5d2d2014-07-24 13:38:28 -0700419 Vector2 outerOffset = strokeOffset + AAOffset;
420 Vector2 innerOffset = strokeOffset - AAOffset;
Chris Craik65cd6122012-12-10 17:56:27 -0800421
John Reck1aa5d2d2014-07-24 13:38:28 -0700422 Vector2 capAAOffset = {0, 0};
Chris Craik65cd6122012-12-10 17:56:27 -0800423 if (paintInfo.cap != SkPaint::kRound_Cap) {
424 // if the cap is square or butt, the inside primary cap vertices will be inset in two
425 // directions - both normal to the stroke, and parallel to it.
John Reck1aa5d2d2014-07-24 13:38:28 -0700426 capAAOffset = (Vector2){-AAOffset.y, AAOffset.x};
Chris Craik65cd6122012-12-10 17:56:27 -0800427 }
428
429 // determine referencePoint, the center point for the 4 primary cap vertices
430 const Vertex* point = isFirst ? vertices.begin() : (vertices.end() - 1);
John Reck1aa5d2d2014-07-24 13:38:28 -0700431 Vector2 referencePoint = {point->x, point->y};
Chris Craik65cd6122012-12-10 17:56:27 -0800432 if (paintInfo.cap == SkPaint::kSquare_Cap) {
433 // To account for square cap, move the primary cap vertices (that create the AA edge) by the
434 // stroke offset vector (rotated to be parallel to the stroke)
John Reck1aa5d2d2014-07-24 13:38:28 -0700435 Vector2 rotated = {-strokeOffset.y, strokeOffset.x};
436 referencePoint += rotated;
Chris Craik65cd6122012-12-10 17:56:27 -0800437 }
438
439 AlphaVertex::set(&buffer[capIndex + 0],
440 referencePoint.x + outerOffset.x + capAAOffset.x,
441 referencePoint.y + outerOffset.y + capAAOffset.y,
442 0.0f);
443 AlphaVertex::set(&buffer[capIndex + 1],
444 referencePoint.x + innerOffset.x - capAAOffset.x,
445 referencePoint.y + innerOffset.y - capAAOffset.y,
446 paintInfo.maxAlpha);
447
448 bool isRound = paintInfo.cap == SkPaint::kRound_Cap;
449
450 const int postCapIndex = (isRound && isFirst) ? (2 * extraOffset - 2) : capIndex + (2 * extra);
451 AlphaVertex::set(&buffer[postCapIndex + 2],
452 referencePoint.x - outerOffset.x + capAAOffset.x,
453 referencePoint.y - outerOffset.y + capAAOffset.y,
454 0.0f);
455 AlphaVertex::set(&buffer[postCapIndex + 3],
456 referencePoint.x - innerOffset.x - capAAOffset.x,
457 referencePoint.y - innerOffset.y - capAAOffset.y,
458 paintInfo.maxAlpha);
459
460 if (isRound) {
461 const float dTheta = PI / (extra + 1);
462 const float radialScale = 2.0f / (1 + cos(dTheta));
463 float theta = atan2(normal.y, normal.x);
464 int capPerimIndex = capIndex + 2;
465
466 for (int i = 0; i < extra; i++) {
467 theta += dTheta;
468
Dan Albertff1d8a62014-11-11 19:31:26 -0800469 Vector2 radialOffset = {cosf(theta), sinf(theta)};
Chris Craik65cd6122012-12-10 17:56:27 -0800470
471 // scale to compensate for pinching at sharp angles, see totalOffsetFromNormals()
472 radialOffset *= radialScale;
473
474 AAOffset = paintInfo.deriveAAOffset(radialOffset);
475 paintInfo.scaleOffsetForStrokeWidth(radialOffset);
476 AlphaVertex::set(&buffer[capPerimIndex++],
477 referencePoint.x + radialOffset.x + AAOffset.x,
478 referencePoint.y + radialOffset.y + AAOffset.y,
479 0.0f);
480 AlphaVertex::set(&buffer[capPerimIndex++],
481 referencePoint.x + radialOffset.x - AAOffset.x,
482 referencePoint.y + radialOffset.y - AAOffset.y,
483 paintInfo.maxAlpha);
484
485 if (isFirst && i == extra - extraOffset) {
486 //copy most recent two points to first two points
Chris Craik11a75672013-12-16 17:08:15 -0800487 buffer[0] = buffer[capPerimIndex - 2];
488 buffer[1] = buffer[capPerimIndex - 1];
Chris Craik65cd6122012-12-10 17:56:27 -0800489
490 capPerimIndex = 2; // start writing the rest of the round cap at index 2
491 }
492 }
493
494 if (isFirst) {
495 const int startCapFillIndex = capIndex + 2 * (extra - extraOffset) + 4;
496 int capFillIndex = startCapFillIndex;
497 for (int i = 0; i < extra + 2; i += 2) {
Chris Craik11a75672013-12-16 17:08:15 -0800498 buffer[capFillIndex++] = buffer[1 + i];
Chris Craik65cd6122012-12-10 17:56:27 -0800499 // TODO: to support odd numbers of divisions, break here on the last iteration
Chris Craik11a75672013-12-16 17:08:15 -0800500 buffer[capFillIndex++] = buffer[startCapFillIndex - 3 - i];
Chris Craik65cd6122012-12-10 17:56:27 -0800501 }
502 } else {
503 int capFillIndex = 6 * vertices.size() + 2 + 6 * extra - (extra + 2);
504 for (int i = 0; i < extra + 2; i += 2) {
Chris Craik11a75672013-12-16 17:08:15 -0800505 buffer[capFillIndex++] = buffer[capIndex + 1 + i];
Chris Craik65cd6122012-12-10 17:56:27 -0800506 // TODO: to support odd numbers of divisions, break here on the last iteration
Chris Craik11a75672013-12-16 17:08:15 -0800507 buffer[capFillIndex++] = buffer[capIndex + 3 + 2 * extra - i];
Chris Craik65cd6122012-12-10 17:56:27 -0800508 }
509 }
510 return;
511 }
512 if (isFirst) {
Chris Craik11a75672013-12-16 17:08:15 -0800513 buffer[0] = buffer[postCapIndex + 2];
514 buffer[1] = buffer[postCapIndex + 3];
515 buffer[postCapIndex + 4] = buffer[1]; // degenerate tris (the only two!)
516 buffer[postCapIndex + 5] = buffer[postCapIndex + 1];
Chris Craik65cd6122012-12-10 17:56:27 -0800517 } else {
Chris Craik11a75672013-12-16 17:08:15 -0800518 buffer[6 * vertices.size()] = buffer[postCapIndex + 1];
519 buffer[6 * vertices.size() + 1] = buffer[postCapIndex + 3];
Chris Craik65cd6122012-12-10 17:56:27 -0800520 }
521}
522
523/*
524the geometry for an aa, capped stroke consists of the following:
525
526 # vertices | function
527----------------------------------------------------------------------
528a) 2 | Start AA perimeter
529b) 2, 2 * roundDivOff | First half of begin cap's perimeter
530 |
531 2 * middlePts | 'Outer' or 'Top' AA perimeter half (between caps)
532 |
533a) 4 | End cap's
534b) 2, 2 * roundDivs, 2 | AA perimeter
535 |
536 2 * middlePts | 'Inner' or 'bottom' AA perimeter half
537 |
538a) 6 | Begin cap's perimeter
539b) 2, 2*(rD - rDO + 1), | Last half of begin cap's perimeter
540 roundDivs, 2 |
541 |
542 2 * middlePts | Stroke's full opacity center strip
543 |
544a) 2 | end stroke
545b) 2, roundDivs | (and end cap fill, for round)
546
547Notes:
548* rows starting with 'a)' denote the Butt or Square cap vertex use, 'b)' denote Round
549
550* 'middlePts' is (number of points in the unclosed input vertex list, minus 2) times two
551
552* 'roundDivs' or 'rD' is the number of extra vertices (beyond the minimum of 2) that define the
553 round cap's shape, and is at least two. This will increase with cap size to sufficiently
554 define the cap's level of tessellation.
555
556* 'roundDivOffset' or 'rDO' is the point about halfway along the start cap's round perimeter, where
557 the stream of vertices for the AA perimeter starts. By starting and ending the perimeter at
558 this offset, the fill of the stroke is drawn from this point with minimal extra vertices.
559
560This means the outer perimeter starts at:
561 outerIndex = (2) OR (2 + 2 * roundDivOff)
562the inner perimeter (since it is filled in reverse) starts at:
563 innerIndex = outerIndex + (4 * middlePts) + ((4) OR (4 + 2 * roundDivs)) - 1
564the stroke starts at:
565 strokeIndex = innerIndex + 1 + ((6) OR (6 + 3 * roundDivs - 2 * roundDivOffset))
566
567The total needed allocated space is either:
568 2 + 4 + 6 + 2 + 3 * (2 * middlePts) = 14 + 6 * middlePts = 2 + 6 * pts
569or, for rounded caps:
570 (2 + 2 * rDO) + (4 + 2 * rD) + (2 * (rD - rDO + 1)
571 + roundDivs + 4) + (2 + roundDivs) + 3 * (2 * middlePts)
572 = 14 + 6 * middlePts + 6 * roundDivs
573 = 2 + 6 * pts + 6 * roundDivs
574 */
575void getStrokeVerticesFromUnclosedVerticesAA(const PaintInfo& paintInfo,
576 const Vector<Vertex>& vertices, VertexBuffer& vertexBuffer) {
577
578 const int extra = paintInfo.capExtraDivisions();
579 const int allocSize = 6 * vertices.size() + 2 + 6 * extra;
580
581 AlphaVertex* buffer = vertexBuffer.alloc<AlphaVertex>(allocSize);
582
583 const int extraOffset = (extra + 1) / 2;
584 int offset = 2 * (vertices.size() - 2);
585 // there is no outer/inner here, using them for consistency with below approach
586 int currentAAOuterIndex = 2 + 2 * extraOffset;
587 int currentAAInnerIndex = currentAAOuterIndex + (2 * offset) + 3 + (2 * extra);
588 int currentStrokeIndex = currentAAInnerIndex + 7 + (3 * extra - 2 * extraOffset);
589
590 const Vertex* last = &(vertices[0]);
591 const Vertex* current = &(vertices[1]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700592 Vector2 lastNormal = {current->y - last->y, last->x - current->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800593 lastNormal.normalize();
594
595 // TODO: use normal from bezier traversal for cap, instead of from vertices
596 storeCapAA(paintInfo, vertices, buffer, true, lastNormal, offset);
597
598 for (unsigned int i = 1; i < vertices.size() - 1; i++) {
599 const Vertex* next = &(vertices[i + 1]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700600 Vector2 nextNormal = {next->y - current->y, current->x - next->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800601 nextNormal.normalize();
602
John Reck1aa5d2d2014-07-24 13:38:28 -0700603 Vector2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
604 Vector2 AAOffset = paintInfo.deriveAAOffset(totalOffset);
Chris Craik65cd6122012-12-10 17:56:27 -0800605
John Reck1aa5d2d2014-07-24 13:38:28 -0700606 Vector2 innerOffset = totalOffset;
Chris Craik65cd6122012-12-10 17:56:27 -0800607 paintInfo.scaleOffsetForStrokeWidth(innerOffset);
John Reck1aa5d2d2014-07-24 13:38:28 -0700608 Vector2 outerOffset = innerOffset + AAOffset;
Chris Craik65cd6122012-12-10 17:56:27 -0800609 innerOffset -= AAOffset;
610
611 AlphaVertex::set(&buffer[currentAAOuterIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700612 current->x + outerOffset.x,
613 current->y + outerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800614 0.0f);
615 AlphaVertex::set(&buffer[currentAAOuterIndex++],
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
620 AlphaVertex::set(&buffer[currentStrokeIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700621 current->x + innerOffset.x,
622 current->y + innerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800623 paintInfo.maxAlpha);
624 AlphaVertex::set(&buffer[currentStrokeIndex++],
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
629 AlphaVertex::set(&buffer[currentAAInnerIndex--],
Romain Guy3380cfd2013-08-15 16:57:57 -0700630 current->x - innerOffset.x,
631 current->y - innerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800632 paintInfo.maxAlpha);
633 AlphaVertex::set(&buffer[currentAAInnerIndex--],
Romain Guy3380cfd2013-08-15 16:57:57 -0700634 current->x - outerOffset.x,
635 current->y - outerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800636 0.0f);
637
638 current = next;
639 lastNormal = nextNormal;
640 }
641
642 // TODO: use normal from bezier traversal for cap, instead of from vertices
643 storeCapAA(paintInfo, vertices, buffer, false, lastNormal, offset);
644
645 DEBUG_DUMP_ALPHA_BUFFER();
646}
647
648
649void getStrokeVerticesFromPerimeterAA(const PaintInfo& paintInfo, const Vector<Vertex>& perimeter,
650 VertexBuffer& vertexBuffer) {
651 AlphaVertex* buffer = vertexBuffer.alloc<AlphaVertex>(6 * perimeter.size() + 8);
652
653 int offset = 2 * perimeter.size() + 3;
654 int currentAAOuterIndex = 0;
655 int currentStrokeIndex = offset;
656 int currentAAInnerIndex = offset * 2;
657
658 const Vertex* last = &(perimeter[perimeter.size() - 1]);
659 const Vertex* current = &(perimeter[0]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700660 Vector2 lastNormal = {current->y - last->y, last->x - current->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800661 lastNormal.normalize();
662 for (unsigned int i = 0; i < perimeter.size(); i++) {
663 const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700664 Vector2 nextNormal = {next->y - current->y, current->x - next->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800665 nextNormal.normalize();
666
John Reck1aa5d2d2014-07-24 13:38:28 -0700667 Vector2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
668 Vector2 AAOffset = paintInfo.deriveAAOffset(totalOffset);
Chris Craik65cd6122012-12-10 17:56:27 -0800669
John Reck1aa5d2d2014-07-24 13:38:28 -0700670 Vector2 innerOffset = totalOffset;
Chris Craik65cd6122012-12-10 17:56:27 -0800671 paintInfo.scaleOffsetForStrokeWidth(innerOffset);
John Reck1aa5d2d2014-07-24 13:38:28 -0700672 Vector2 outerOffset = innerOffset + AAOffset;
Chris Craik65cd6122012-12-10 17:56:27 -0800673 innerOffset -= AAOffset;
674
675 AlphaVertex::set(&buffer[currentAAOuterIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700676 current->x + outerOffset.x,
677 current->y + outerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800678 0.0f);
679 AlphaVertex::set(&buffer[currentAAOuterIndex++],
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
684 AlphaVertex::set(&buffer[currentStrokeIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700685 current->x + innerOffset.x,
686 current->y + innerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800687 paintInfo.maxAlpha);
688 AlphaVertex::set(&buffer[currentStrokeIndex++],
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
693 AlphaVertex::set(&buffer[currentAAInnerIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700694 current->x - innerOffset.x,
695 current->y - innerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800696 paintInfo.maxAlpha);
697 AlphaVertex::set(&buffer[currentAAInnerIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700698 current->x - outerOffset.x,
699 current->y - outerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800700 0.0f);
701
Chris Craik65cd6122012-12-10 17:56:27 -0800702 current = next;
703 lastNormal = nextNormal;
704 }
705
706 // wrap each strip around to beginning, creating degenerate tris to bridge strips
Chris Craik11a75672013-12-16 17:08:15 -0800707 buffer[currentAAOuterIndex++] = buffer[0];
708 buffer[currentAAOuterIndex++] = buffer[1];
709 buffer[currentAAOuterIndex++] = buffer[1];
Chris Craik65cd6122012-12-10 17:56:27 -0800710
Chris Craik11a75672013-12-16 17:08:15 -0800711 buffer[currentStrokeIndex++] = buffer[offset];
712 buffer[currentStrokeIndex++] = buffer[offset + 1];
713 buffer[currentStrokeIndex++] = buffer[offset + 1];
Chris Craik65cd6122012-12-10 17:56:27 -0800714
Chris Craik11a75672013-12-16 17:08:15 -0800715 buffer[currentAAInnerIndex++] = buffer[2 * offset];
716 buffer[currentAAInnerIndex++] = buffer[2 * offset + 1];
Chris Craik65cd6122012-12-10 17:56:27 -0800717 // don't need to create last degenerate tri
718
719 DEBUG_DUMP_ALPHA_BUFFER();
720}
721
722void PathTessellator::tessellatePath(const SkPath &path, const SkPaint* paint,
Chris Craikd6b65f62014-01-01 14:45:21 -0800723 const mat4& transform, VertexBuffer& vertexBuffer) {
Chris Craik65cd6122012-12-10 17:56:27 -0800724 ATRACE_CALL();
725
726 const PaintInfo paintInfo(paint, transform);
727
728 Vector<Vertex> tempVertices;
729 float threshInvScaleX = paintInfo.inverseScaleX;
730 float threshInvScaleY = paintInfo.inverseScaleY;
731 if (paintInfo.style == SkPaint::kStroke_Style) {
732 // alter the bezier recursion threshold values we calculate in order to compensate for
733 // expansion done after the path vertices are found
734 SkRect bounds = path.getBounds();
735 if (!bounds.isEmpty()) {
736 threshInvScaleX *= bounds.width() / (bounds.width() + paint->getStrokeWidth());
737 threshInvScaleY *= bounds.height() / (bounds.height() + paint->getStrokeWidth());
738 }
739 }
740
741 // force close if we're filling the path, since fill path expects closed perimeter.
742 bool forceClose = paintInfo.style != SkPaint::kStroke_Style;
743 bool wasClosed = approximatePathOutlineVertices(path, forceClose,
Chris Craik15a07a22014-01-26 13:43:53 -0800744 threshInvScaleX * threshInvScaleX, threshInvScaleY * threshInvScaleY,
745 OUTLINE_REFINE_THRESHOLD_SQUARED, tempVertices);
Chris Craik65cd6122012-12-10 17:56:27 -0800746
747 if (!tempVertices.size()) {
748 // path was empty, return without allocating vertex buffer
749 return;
750 }
751
752#if VERTEX_DEBUG
753 for (unsigned int i = 0; i < tempVertices.size(); i++) {
754 ALOGD("orig path: point at %f %f",
Romain Guy3380cfd2013-08-15 16:57:57 -0700755 tempVertices[i].x, tempVertices[i].y);
Chris Craik65cd6122012-12-10 17:56:27 -0800756 }
757#endif
758
759 if (paintInfo.style == SkPaint::kStroke_Style) {
760 if (!paintInfo.isAA) {
761 if (wasClosed) {
762 getStrokeVerticesFromPerimeter(paintInfo, tempVertices, vertexBuffer);
763 } else {
764 getStrokeVerticesFromUnclosedVertices(paintInfo, tempVertices, vertexBuffer);
765 }
766
767 } else {
768 if (wasClosed) {
769 getStrokeVerticesFromPerimeterAA(paintInfo, tempVertices, vertexBuffer);
770 } else {
771 getStrokeVerticesFromUnclosedVerticesAA(paintInfo, tempVertices, vertexBuffer);
772 }
773 }
774 } else {
775 // For kStrokeAndFill style, the path should be adjusted externally.
776 // It will be treated as a fill here.
777 if (!paintInfo.isAA) {
778 getFillVerticesFromPerimeter(tempVertices, vertexBuffer);
779 } else {
780 getFillVerticesFromPerimeterAA(paintInfo, tempVertices, vertexBuffer);
781 }
782 }
Chris Craik05f3d6e2014-06-02 16:27:04 -0700783
784 Rect bounds(path.getBounds());
785 paintInfo.expandBoundsForStroke(&bounds);
786 vertexBuffer.setBounds(bounds);
Chris Craik117bdbc2015-02-05 10:12:38 -0800787 vertexBuffer.setMeshFeatureFlags(paintInfo.isAA ? VertexBuffer::kAlpha : VertexBuffer::kNone);
Chris Craik65cd6122012-12-10 17:56:27 -0800788}
789
Chris Craik6d29c8d2013-05-08 18:35:44 -0700790template <class TYPE>
791static void instanceVertices(VertexBuffer& srcBuffer, VertexBuffer& dstBuffer,
Chris Craik05f3d6e2014-06-02 16:27:04 -0700792 const float* points, int count, Rect& bounds) {
Chris Craik6d29c8d2013-05-08 18:35:44 -0700793 bounds.set(points[0], points[1], points[0], points[1]);
794
795 int numPoints = count / 2;
796 int verticesPerPoint = srcBuffer.getVertexCount();
797 dstBuffer.alloc<TYPE>(numPoints * verticesPerPoint + (numPoints - 1) * 2);
798
799 for (int i = 0; i < count; i += 2) {
Chris Craikc93e45c2014-07-16 10:15:56 -0700800 bounds.expandToCoverVertex(points[i + 0], points[i + 1]);
Chris Craik6d29c8d2013-05-08 18:35:44 -0700801 dstBuffer.copyInto<TYPE>(srcBuffer, points[i + 0], points[i + 1]);
802 }
803 dstBuffer.createDegenerateSeparators<TYPE>(verticesPerPoint);
804}
805
Chris Craikd218a922014-01-02 17:13:34 -0800806void PathTessellator::tessellatePoints(const float* points, int count, const SkPaint* paint,
Chris Craik05f3d6e2014-06-02 16:27:04 -0700807 const mat4& transform, VertexBuffer& vertexBuffer) {
Chris Craik6d29c8d2013-05-08 18:35:44 -0700808 const PaintInfo paintInfo(paint, transform);
809
810 // determine point shape
811 SkPath path;
812 float radius = paintInfo.halfStrokeWidth;
Rob Tsukb7c26562014-11-03 16:29:03 -0800813 if (radius == 0.0f) radius = 0.5f;
Chris Craik6d29c8d2013-05-08 18:35:44 -0700814
815 if (paintInfo.cap == SkPaint::kRound_Cap) {
816 path.addCircle(0, 0, radius);
817 } else {
818 path.addRect(-radius, -radius, radius, radius);
819 }
820
821 // calculate outline
822 Vector<Vertex> outlineVertices;
823 approximatePathOutlineVertices(path, true,
824 paintInfo.inverseScaleX * paintInfo.inverseScaleX,
Chris Craik15a07a22014-01-26 13:43:53 -0800825 paintInfo.inverseScaleY * paintInfo.inverseScaleY,
826 OUTLINE_REFINE_THRESHOLD_SQUARED, outlineVertices);
Chris Craik6d29c8d2013-05-08 18:35:44 -0700827
828 if (!outlineVertices.size()) return;
829
Chris Craik05f3d6e2014-06-02 16:27:04 -0700830 Rect bounds;
Chris Craik6d29c8d2013-05-08 18:35:44 -0700831 // tessellate, then duplicate outline across points
Chris Craik6d29c8d2013-05-08 18:35:44 -0700832 VertexBuffer tempBuffer;
833 if (!paintInfo.isAA) {
834 getFillVerticesFromPerimeter(outlineVertices, tempBuffer);
835 instanceVertices<Vertex>(tempBuffer, vertexBuffer, points, count, bounds);
836 } else {
Chris Craikf0a59072013-11-19 18:00:46 -0800837 // note: pass maxAlpha directly, since we want fill to be alpha modulated
838 getFillVerticesFromPerimeterAA(paintInfo, outlineVertices, tempBuffer, paintInfo.maxAlpha);
Chris Craik6d29c8d2013-05-08 18:35:44 -0700839 instanceVertices<AlphaVertex>(tempBuffer, vertexBuffer, points, count, bounds);
840 }
841
Chris Craikf0a59072013-11-19 18:00:46 -0800842 // expand bounds from vertex coords to pixel data
Chris Craik05f3d6e2014-06-02 16:27:04 -0700843 paintInfo.expandBoundsForStroke(&bounds);
844 vertexBuffer.setBounds(bounds);
Chris Craik117bdbc2015-02-05 10:12:38 -0800845 vertexBuffer.setMeshFeatureFlags(paintInfo.isAA ? VertexBuffer::kAlpha : VertexBuffer::kNone);
Chris Craik65cd6122012-12-10 17:56:27 -0800846}
847
Chris Craikd218a922014-01-02 17:13:34 -0800848void PathTessellator::tessellateLines(const float* points, int count, const SkPaint* paint,
Chris Craik05f3d6e2014-06-02 16:27:04 -0700849 const mat4& transform, VertexBuffer& vertexBuffer) {
Chris Craik65cd6122012-12-10 17:56:27 -0800850 ATRACE_CALL();
851 const PaintInfo paintInfo(paint, transform);
852
853 const int extra = paintInfo.capExtraDivisions();
854 int numLines = count / 4;
855 int lineAllocSize;
856 // pre-allocate space for lines in the buffer, and degenerate tris in between
857 if (paintInfo.isAA) {
858 lineAllocSize = 6 * (2) + 2 + 6 * extra;
859 vertexBuffer.alloc<AlphaVertex>(numLines * lineAllocSize + (numLines - 1) * 2);
860 } else {
861 lineAllocSize = 2 * ((2) + extra);
862 vertexBuffer.alloc<Vertex>(numLines * lineAllocSize + (numLines - 1) * 2);
863 }
864
865 Vector<Vertex> tempVertices;
866 tempVertices.push();
867 tempVertices.push();
868 Vertex* tempVerticesData = tempVertices.editArray();
Chris Craik05f3d6e2014-06-02 16:27:04 -0700869 Rect bounds;
Chris Craik65cd6122012-12-10 17:56:27 -0800870 bounds.set(points[0], points[1], points[0], points[1]);
871 for (int i = 0; i < count; i += 4) {
872 Vertex::set(&(tempVerticesData[0]), points[i + 0], points[i + 1]);
873 Vertex::set(&(tempVerticesData[1]), points[i + 2], points[i + 3]);
874
875 if (paintInfo.isAA) {
876 getStrokeVerticesFromUnclosedVerticesAA(paintInfo, tempVertices, vertexBuffer);
877 } else {
878 getStrokeVerticesFromUnclosedVertices(paintInfo, tempVertices, vertexBuffer);
879 }
880
881 // calculate bounds
Chris Craikc93e45c2014-07-16 10:15:56 -0700882 bounds.expandToCoverVertex(tempVerticesData[0].x, tempVerticesData[0].y);
883 bounds.expandToCoverVertex(tempVerticesData[1].x, tempVerticesData[1].y);
Chris Craik65cd6122012-12-10 17:56:27 -0800884 }
885
Chris Craik65cd6122012-12-10 17:56:27 -0800886 // since multiple objects tessellated into buffer, separate them with degen tris
887 if (paintInfo.isAA) {
888 vertexBuffer.createDegenerateSeparators<AlphaVertex>(lineAllocSize);
889 } else {
890 vertexBuffer.createDegenerateSeparators<Vertex>(lineAllocSize);
891 }
Chris Craikf0a59072013-11-19 18:00:46 -0800892
893 // expand bounds from vertex coords to pixel data
Chris Craik05f3d6e2014-06-02 16:27:04 -0700894 paintInfo.expandBoundsForStroke(&bounds);
895 vertexBuffer.setBounds(bounds);
Chris Craik117bdbc2015-02-05 10:12:38 -0800896 vertexBuffer.setMeshFeatureFlags(paintInfo.isAA ? VertexBuffer::kAlpha : VertexBuffer::kNone);
Chris Craik65cd6122012-12-10 17:56:27 -0800897}
898
899///////////////////////////////////////////////////////////////////////////////
900// Simple path line approximation
901///////////////////////////////////////////////////////////////////////////////
902
Chris Craik15a07a22014-01-26 13:43:53 -0800903bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, float thresholdSquared,
904 Vector<Vertex>& outputVertices) {
905 return approximatePathOutlineVertices(path, true, 1.0f, 1.0f, thresholdSquared, outputVertices);
906}
907
Chris Craik65cd6122012-12-10 17:56:27 -0800908void pushToVector(Vector<Vertex>& vertices, float x, float y) {
909 // TODO: make this not yuck
910 vertices.push();
911 Vertex* newVertex = &(vertices.editArray()[vertices.size() - 1]);
912 Vertex::set(newVertex, x, y);
913}
914
915bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool forceClose,
Chris Craik15a07a22014-01-26 13:43:53 -0800916 float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared,
917 Vector<Vertex>& outputVertices) {
Chris Craik65cd6122012-12-10 17:56:27 -0800918 ATRACE_CALL();
919
920 // TODO: to support joins other than sharp miter, join vertices should be labelled in the
921 // perimeter, or resolved into more vertices. Reconsider forceClose-ing in that case.
922 SkPath::Iter iter(path, forceClose);
923 SkPoint pts[4];
924 SkPath::Verb v;
925 while (SkPath::kDone_Verb != (v = iter.next(pts))) {
926 switch (v) {
927 case SkPath::kMove_Verb:
928 pushToVector(outputVertices, pts[0].x(), pts[0].y());
929 ALOGV("Move to pos %f %f", pts[0].x(), pts[0].y());
930 break;
931 case SkPath::kClose_Verb:
932 ALOGV("Close at pos %f %f", pts[0].x(), pts[0].y());
933 break;
934 case SkPath::kLine_Verb:
935 ALOGV("kLine_Verb %f %f -> %f %f", pts[0].x(), pts[0].y(), pts[1].x(), pts[1].y());
936 pushToVector(outputVertices, pts[1].x(), pts[1].y());
937 break;
938 case SkPath::kQuad_Verb:
939 ALOGV("kQuad_Verb");
940 recursiveQuadraticBezierVertices(
941 pts[0].x(), pts[0].y(),
942 pts[2].x(), pts[2].y(),
943 pts[1].x(), pts[1].y(),
Chris Craik15a07a22014-01-26 13:43:53 -0800944 sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices);
Chris Craik65cd6122012-12-10 17:56:27 -0800945 break;
946 case SkPath::kCubic_Verb:
947 ALOGV("kCubic_Verb");
948 recursiveCubicBezierVertices(
949 pts[0].x(), pts[0].y(),
950 pts[1].x(), pts[1].y(),
951 pts[3].x(), pts[3].y(),
952 pts[2].x(), pts[2].y(),
Chris Craik15a07a22014-01-26 13:43:53 -0800953 sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices);
Chris Craik65cd6122012-12-10 17:56:27 -0800954 break;
Derek Sollenbergerb0772a62015-03-16 14:35:55 -0400955 case SkPath::kConic_Verb: {
956 ALOGV("kConic_Verb");
957 SkAutoConicToQuads converter;
958 const SkPoint* quads = converter.computeQuads(pts, iter.conicWeight(),
959 thresholdSquared);
960 for (int i = 0; i < converter.countQuads(); ++i) {
961 const int offset = 2 * i;
962 recursiveQuadraticBezierVertices(
963 quads[offset].x(), quads[offset].y(),
964 quads[offset+2].x(), quads[offset+2].y(),
965 quads[offset+1].x(), quads[offset+1].y(),
966 sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices);
967 }
968 break;
969 }
Chris Craik65cd6122012-12-10 17:56:27 -0800970 default:
971 break;
972 }
973 }
974
975 int size = outputVertices.size();
Romain Guy3380cfd2013-08-15 16:57:57 -0700976 if (size >= 2 && outputVertices[0].x == outputVertices[size - 1].x &&
977 outputVertices[0].y == outputVertices[size - 1].y) {
Chris Craik65cd6122012-12-10 17:56:27 -0800978 outputVertices.pop();
979 return true;
980 }
981 return false;
982}
983
984///////////////////////////////////////////////////////////////////////////////
985// Bezier approximation
986///////////////////////////////////////////////////////////////////////////////
987
988void PathTessellator::recursiveCubicBezierVertices(
989 float p1x, float p1y, float c1x, float c1y,
990 float p2x, float p2y, float c2x, float c2y,
Chris Craik15a07a22014-01-26 13:43:53 -0800991 float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared,
Chris Craik9c3dd622014-06-11 17:24:51 -0700992 Vector<Vertex>& outputVertices, int depth) {
Chris Craik65cd6122012-12-10 17:56:27 -0800993 float dx = p2x - p1x;
994 float dy = p2y - p1y;
995 float d1 = fabs((c1x - p2x) * dy - (c1y - p2y) * dx);
996 float d2 = fabs((c2x - p2x) * dy - (c2y - p2y) * dx);
997 float d = d1 + d2;
998
999 // multiplying by sqrInvScaleY/X equivalent to multiplying in dimensional scale factors
Chris Craik74cf7e62014-08-07 14:34:46 -07001000 if (depth >= MAX_DEPTH
1001 || d * d <= thresholdSquared * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) {
Chris Craik65cd6122012-12-10 17:56:27 -08001002 // below thresh, draw line by adding endpoint
1003 pushToVector(outputVertices, p2x, p2y);
1004 } else {
1005 float p1c1x = (p1x + c1x) * 0.5f;
1006 float p1c1y = (p1y + c1y) * 0.5f;
1007 float p2c2x = (p2x + c2x) * 0.5f;
1008 float p2c2y = (p2y + c2y) * 0.5f;
1009
1010 float c1c2x = (c1x + c2x) * 0.5f;
1011 float c1c2y = (c1y + c2y) * 0.5f;
1012
1013 float p1c1c2x = (p1c1x + c1c2x) * 0.5f;
1014 float p1c1c2y = (p1c1y + c1c2y) * 0.5f;
1015
1016 float p2c1c2x = (p2c2x + c1c2x) * 0.5f;
1017 float p2c1c2y = (p2c2y + c1c2y) * 0.5f;
1018
1019 float mx = (p1c1c2x + p2c1c2x) * 0.5f;
1020 float my = (p1c1c2y + p2c1c2y) * 0.5f;
1021
1022 recursiveCubicBezierVertices(
1023 p1x, p1y, p1c1x, p1c1y,
1024 mx, my, p1c1c2x, p1c1c2y,
Chris Craik9c3dd622014-06-11 17:24:51 -07001025 sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices, depth + 1);
Chris Craik65cd6122012-12-10 17:56:27 -08001026 recursiveCubicBezierVertices(
1027 mx, my, p2c1c2x, p2c1c2y,
1028 p2x, p2y, p2c2x, p2c2y,
Chris Craik9c3dd622014-06-11 17:24:51 -07001029 sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices, depth + 1);
Chris Craik65cd6122012-12-10 17:56:27 -08001030 }
1031}
1032
1033void PathTessellator::recursiveQuadraticBezierVertices(
1034 float ax, float ay,
1035 float bx, float by,
1036 float cx, float cy,
Chris Craik15a07a22014-01-26 13:43:53 -08001037 float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared,
Chris Craik9c3dd622014-06-11 17:24:51 -07001038 Vector<Vertex>& outputVertices, int depth) {
Chris Craik65cd6122012-12-10 17:56:27 -08001039 float dx = bx - ax;
1040 float dy = by - ay;
1041 float d = (cx - bx) * dy - (cy - by) * dx;
1042
Chris Craik74cf7e62014-08-07 14:34:46 -07001043 // multiplying by sqrInvScaleY/X equivalent to multiplying in dimensional scale factors
1044 if (depth >= MAX_DEPTH
1045 || d * d <= thresholdSquared * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) {
Chris Craik65cd6122012-12-10 17:56:27 -08001046 // below thresh, draw line by adding endpoint
1047 pushToVector(outputVertices, bx, by);
1048 } else {
1049 float acx = (ax + cx) * 0.5f;
1050 float bcx = (bx + cx) * 0.5f;
1051 float acy = (ay + cy) * 0.5f;
1052 float bcy = (by + cy) * 0.5f;
1053
1054 // midpoint
1055 float mx = (acx + bcx) * 0.5f;
1056 float my = (acy + bcy) * 0.5f;
1057
1058 recursiveQuadraticBezierVertices(ax, ay, mx, my, acx, acy,
Chris Craik9c3dd622014-06-11 17:24:51 -07001059 sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices, depth + 1);
Chris Craik65cd6122012-12-10 17:56:27 -08001060 recursiveQuadraticBezierVertices(mx, my, bx, by, bcx, bcy,
Chris Craik9c3dd622014-06-11 17:24:51 -07001061 sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices, depth + 1);
Chris Craik65cd6122012-12-10 17:56:27 -08001062 }
1063}
1064
1065}; // namespace uirenderer
1066}; // namespace android