blob: 27ef06fae866af88dfba3bccced72868bbaa299b [file] [log] [blame]
Chris Craik65cd6122012-12-10 17:56:27 -08001/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Chris Craikf0a59072013-11-19 18:00:46 -080017#define LOG_TAG "OpenGLRenderer"
Chris Craik65cd6122012-12-10 17:56:27 -080018#define LOG_NDEBUG 1
Chris Craik87f9df82014-03-07 14:34:42 -080019#define ATRACE_TAG ATRACE_TAG_VIEW
Chris Craik65cd6122012-12-10 17:56:27 -080020
21#define VERTEX_DEBUG 0
22
23#if VERTEX_DEBUG
24#define DEBUG_DUMP_ALPHA_BUFFER() \
25 for (unsigned int i = 0; i < vertexBuffer.getSize(); i++) { \
26 ALOGD("point %d at %f %f, alpha %f", \
Romain Guy3380cfd2013-08-15 16:57:57 -070027 i, buffer[i].x, buffer[i].y, buffer[i].alpha); \
Chris Craik65cd6122012-12-10 17:56:27 -080028 }
29#define DEBUG_DUMP_BUFFER() \
30 for (unsigned int i = 0; i < vertexBuffer.getSize(); i++) { \
Romain Guy3380cfd2013-08-15 16:57:57 -070031 ALOGD("point %d at %f %f", i, buffer[i].x, buffer[i].y); \
Chris Craik65cd6122012-12-10 17:56:27 -080032 }
33#else
34#define DEBUG_DUMP_ALPHA_BUFFER()
35#define DEBUG_DUMP_BUFFER()
36#endif
37
38#include <SkPath.h>
39#include <SkPaint.h>
40
41#include <stdlib.h>
42#include <stdint.h>
43#include <sys/types.h>
44
45#include <utils/Log.h>
46#include <utils/Trace.h>
47
48#include "PathTessellator.h"
49#include "Matrix.h"
50#include "Vector.h"
51#include "Vertex.h"
Chris Craik74cf7e62014-08-07 14:34:46 -070052#include "utils/MathUtils.h"
Chris Craik65cd6122012-12-10 17:56:27 -080053
54namespace android {
55namespace uirenderer {
56
Chris Craik15a07a22014-01-26 13:43:53 -080057#define OUTLINE_REFINE_THRESHOLD_SQUARED (0.5f * 0.5f)
Chris Craik65cd6122012-12-10 17:56:27 -080058#define ROUND_CAP_THRESH 0.25f
59#define PI 3.1415926535897932f
Chris Craik74cf7e62014-08-07 14:34:46 -070060#define MAX_DEPTH 15
Chris Craik65cd6122012-12-10 17:56:27 -080061
Chris Craik74cf7e62014-08-07 14:34:46 -070062/**
63 * Extracts the x and y scale from the transform as positive values, and clamps them
64 */
Chris Craik05f3d6e2014-06-02 16:27:04 -070065void PathTessellator::extractTessellationScales(const Matrix4& transform,
66 float* scaleX, float* scaleY) {
Chris Craikfe02b4b2014-06-16 16:34:29 -070067 if (CC_LIKELY(transform.isPureTranslate())) {
68 *scaleX = 1.0f;
69 *scaleY = 1.0f;
70 } else {
Chris Craik05f3d6e2014-06-02 16:27:04 -070071 float m00 = transform.data[Matrix4::kScaleX];
72 float m01 = transform.data[Matrix4::kSkewY];
73 float m10 = transform.data[Matrix4::kSkewX];
74 float m11 = transform.data[Matrix4::kScaleY];
Chris Craik74cf7e62014-08-07 14:34:46 -070075 *scaleX = MathUtils::clampTessellationScale(sqrt(m00 * m00 + m01 * m01));
76 *scaleY = MathUtils::clampTessellationScale(sqrt(m10 * m10 + m11 * m11));
Chris Craik65cd6122012-12-10 17:56:27 -080077 }
78}
79
Chris Craik65cd6122012-12-10 17:56:27 -080080/**
81 * Produces a pseudo-normal for a vertex, given the normals of the two incoming lines. If the offset
82 * from each vertex in a perimeter is calculated, the resultant lines connecting the offset vertices
83 * will be offset by 1.0
84 *
85 * Note that we can't add and normalize the two vectors, that would result in a rectangle having an
86 * offset of (sqrt(2)/2, sqrt(2)/2) at each corner, instead of (1, 1)
87 *
88 * NOTE: assumes angles between normals 90 degrees or less
89 */
John Reck1aa5d2d2014-07-24 13:38:28 -070090inline static Vector2 totalOffsetFromNormals(const Vector2& normalA, const Vector2& normalB) {
Chris Craik65cd6122012-12-10 17:56:27 -080091 return (normalA + normalB) / (1 + fabs(normalA.dot(normalB)));
92}
93
94/**
95 * Structure used for storing useful information about the SkPaint and scale used for tessellating
96 */
97struct PaintInfo {
98public:
Chris Craikd6b65f62014-01-01 14:45:21 -080099 PaintInfo(const SkPaint* paint, const mat4& transform) :
Chris Craik65cd6122012-12-10 17:56:27 -0800100 style(paint->getStyle()), cap(paint->getStrokeCap()), isAA(paint->isAntiAlias()),
Chris Craik65cd6122012-12-10 17:56:27 -0800101 halfStrokeWidth(paint->getStrokeWidth() * 0.5f), maxAlpha(1.0f) {
102 // compute inverse scales
Chris Craikfe02b4b2014-06-16 16:34:29 -0700103 if (CC_LIKELY(transform.isPureTranslate())) {
104 inverseScaleX = 1.0f;
105 inverseScaleY = 1.0f;
106 } else {
Chris Craik05f3d6e2014-06-02 16:27:04 -0700107 float scaleX, scaleY;
108 PathTessellator::extractTessellationScales(transform, &scaleX, &scaleY);
Chris Craik74cf7e62014-08-07 14:34:46 -0700109 inverseScaleX = 1.0f / scaleX;
110 inverseScaleY = 1.0f / scaleY;
Chris Craik65cd6122012-12-10 17:56:27 -0800111 }
112
113 if (isAA && halfStrokeWidth != 0 && inverseScaleX == inverseScaleY &&
Chris Craik19a390b2013-02-27 15:43:26 -0800114 2 * halfStrokeWidth < inverseScaleX) {
Chris Craik05f3d6e2014-06-02 16:27:04 -0700115 // AA, with non-hairline stroke, width < 1 pixel. Scale alpha and treat as hairline.
Chris Craik65cd6122012-12-10 17:56:27 -0800116 maxAlpha *= (2 * halfStrokeWidth) / inverseScaleX;
117 halfStrokeWidth = 0.0f;
118 }
119 }
120
121 SkPaint::Style style;
122 SkPaint::Cap cap;
123 bool isAA;
124 float inverseScaleX;
125 float inverseScaleY;
126 float halfStrokeWidth;
127 float maxAlpha;
128
John Reck1aa5d2d2014-07-24 13:38:28 -0700129 inline void scaleOffsetForStrokeWidth(Vector2& offset) const {
Chris Craik65cd6122012-12-10 17:56:27 -0800130 if (halfStrokeWidth == 0.0f) {
131 // hairline - compensate for scale
132 offset.x *= 0.5f * inverseScaleX;
133 offset.y *= 0.5f * inverseScaleY;
134 } else {
135 offset *= halfStrokeWidth;
136 }
137 }
138
139 /**
140 * NOTE: the input will not always be a normal, especially for sharp edges - it should be the
141 * result of totalOffsetFromNormals (see documentation there)
142 */
John Reck1aa5d2d2014-07-24 13:38:28 -0700143 inline Vector2 deriveAAOffset(const Vector2& offset) const {
144 return (Vector2){offset.x * 0.5f * inverseScaleX, offset.y * 0.5f * inverseScaleY};
Chris Craik65cd6122012-12-10 17:56:27 -0800145 }
146
147 /**
148 * Returns the number of cap divisions beyond the minimum 2 (kButt_Cap/kSquareCap will return 0)
149 * Should only be used when stroking and drawing caps
150 */
151 inline int capExtraDivisions() const {
152 if (cap == SkPaint::kRound_Cap) {
153 if (halfStrokeWidth == 0.0f) return 2;
154
155 // ROUND_CAP_THRESH is the maximum error for polygonal approximation of the round cap
156 const float errConst = (-ROUND_CAP_THRESH / halfStrokeWidth + 1);
157 const float targetCosVal = 2 * errConst * errConst - 1;
158 int neededDivisions = (int)(ceilf(PI / acos(targetCosVal)/2)) * 2;
159 return neededDivisions;
160 }
161 return 0;
162 }
Chris Craikf0a59072013-11-19 18:00:46 -0800163
164 /**
Chris Craik21bcfc4f2014-09-08 18:43:30 -0700165 * Outset the bounds of point data (for line endpoints or points) to account for stroke
Chris Craikf0a59072013-11-19 18:00:46 -0800166 * geometry.
Chris Craik21bcfc4f2014-09-08 18:43:30 -0700167 *
168 * bounds are in pre-scaled space.
Chris Craikf0a59072013-11-19 18:00:46 -0800169 */
Chris Craik05f3d6e2014-06-02 16:27:04 -0700170 void expandBoundsForStroke(Rect* bounds) const {
Chris Craik21bcfc4f2014-09-08 18:43:30 -0700171 if (halfStrokeWidth == 0) {
172 // hairline, outset by (0.5f + fudge factor) in post-scaling space
173 bounds->outset(fabs(inverseScaleX) * (0.5f + Vertex::GeometryFudgeFactor()),
174 fabs(inverseScaleY) * (0.5f + Vertex::GeometryFudgeFactor()));
175 } else {
176 // non hairline, outset by half stroke width pre-scaled, and fudge factor post scaled
177 bounds->outset(halfStrokeWidth + fabs(inverseScaleX) * Vertex::GeometryFudgeFactor(),
178 halfStrokeWidth + fabs(inverseScaleY) * Vertex::GeometryFudgeFactor());
179 }
Chris Craikf0a59072013-11-19 18:00:46 -0800180 }
Chris Craik65cd6122012-12-10 17:56:27 -0800181};
182
183void getFillVerticesFromPerimeter(const Vector<Vertex>& perimeter, VertexBuffer& vertexBuffer) {
184 Vertex* buffer = vertexBuffer.alloc<Vertex>(perimeter.size());
185
186 int currentIndex = 0;
187 // zig zag between all previous points on the inside of the hull to create a
188 // triangle strip that fills the hull
189 int srcAindex = 0;
190 int srcBindex = perimeter.size() - 1;
191 while (srcAindex <= srcBindex) {
Chris Craik11a75672013-12-16 17:08:15 -0800192 buffer[currentIndex++] = perimeter[srcAindex];
Chris Craik65cd6122012-12-10 17:56:27 -0800193 if (srcAindex == srcBindex) break;
Chris Craik11a75672013-12-16 17:08:15 -0800194 buffer[currentIndex++] = perimeter[srcBindex];
Chris Craik65cd6122012-12-10 17:56:27 -0800195 srcAindex++;
196 srcBindex--;
197 }
198}
199
200/*
201 * Fills a vertexBuffer with non-alpha vertices, zig-zagging at each perimeter point to create a
202 * tri-strip as wide as the stroke.
203 *
204 * Uses an additional 2 vertices at the end to wrap around, closing the tri-strip
205 * (for a total of perimeter.size() * 2 + 2 vertices)
206 */
207void getStrokeVerticesFromPerimeter(const PaintInfo& paintInfo, const Vector<Vertex>& perimeter,
208 VertexBuffer& vertexBuffer) {
209 Vertex* buffer = vertexBuffer.alloc<Vertex>(perimeter.size() * 2 + 2);
210
211 int currentIndex = 0;
212 const Vertex* last = &(perimeter[perimeter.size() - 1]);
213 const Vertex* current = &(perimeter[0]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700214 Vector2 lastNormal = {current->y - last->y, last->x - current->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800215 lastNormal.normalize();
216 for (unsigned int i = 0; i < perimeter.size(); i++) {
217 const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700218 Vector2 nextNormal = {next->y - current->y, current->x - next->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800219 nextNormal.normalize();
220
John Reck1aa5d2d2014-07-24 13:38:28 -0700221 Vector2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
Chris Craik65cd6122012-12-10 17:56:27 -0800222 paintInfo.scaleOffsetForStrokeWidth(totalOffset);
223
224 Vertex::set(&buffer[currentIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700225 current->x + totalOffset.x,
226 current->y + totalOffset.y);
Chris Craik65cd6122012-12-10 17:56:27 -0800227
228 Vertex::set(&buffer[currentIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700229 current->x - totalOffset.x,
230 current->y - totalOffset.y);
Chris Craik65cd6122012-12-10 17:56:27 -0800231
232 last = current;
233 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);
282 const float radialScale = 2.0f / (1 + cos(dTheta));
283
284 int capOffset;
285 for (int i = 0; i < extra; i++) {
286 if (i < extra / 2) {
287 capOffset = extra - 2 * i - 1;
288 } else {
289 capOffset = 2 * i - extra;
290 }
291
292 beginTheta += dTheta;
John Reck1aa5d2d2014-07-24 13:38:28 -0700293 Vector2 beginRadialOffset = {cos(beginTheta), sin(beginTheta)};
Chris Craik65cd6122012-12-10 17:56:27 -0800294 paintInfo.scaleOffsetForStrokeWidth(beginRadialOffset);
295 Vertex::set(&buffer[capOffset],
Romain Guy3380cfd2013-08-15 16:57:57 -0700296 vertices[0].x + beginRadialOffset.x,
297 vertices[0].y + beginRadialOffset.y);
Chris Craik65cd6122012-12-10 17:56:27 -0800298
299 endTheta += dTheta;
John Reck1aa5d2d2014-07-24 13:38:28 -0700300 Vector2 endRadialOffset = {cos(endTheta), sin(endTheta)};
Chris Craik65cd6122012-12-10 17:56:27 -0800301 paintInfo.scaleOffsetForStrokeWidth(endRadialOffset);
302 Vertex::set(&buffer[allocSize - 1 - capOffset],
Romain Guy3380cfd2013-08-15 16:57:57 -0700303 vertices[lastIndex].x + endRadialOffset.x,
304 vertices[lastIndex].y + endRadialOffset.y);
Chris Craik65cd6122012-12-10 17:56:27 -0800305 }
306 }
307
308 int currentIndex = extra;
Chris Craik6d29c8d2013-05-08 18:35:44 -0700309 const Vertex* last = &(vertices[0]);
310 const Vertex* current = &(vertices[1]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700311 Vector2 lastNormal = {current->y - last->y, last->x - current->x};
Chris Craik6d29c8d2013-05-08 18:35:44 -0700312 lastNormal.normalize();
313
314 storeBeginEnd(paintInfo, vertices[0], lastNormal, buffer, currentIndex, true);
315
316 for (unsigned int i = 1; i < vertices.size() - 1; i++) {
Chris Craik65cd6122012-12-10 17:56:27 -0800317 const Vertex* next = &(vertices[i + 1]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700318 Vector2 nextNormal = {next->y - current->y, current->x - next->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800319 nextNormal.normalize();
320
John Reck1aa5d2d2014-07-24 13:38:28 -0700321 Vector2 strokeOffset = totalOffsetFromNormals(lastNormal, nextNormal);
Chris Craik6d29c8d2013-05-08 18:35:44 -0700322 paintInfo.scaleOffsetForStrokeWidth(strokeOffset);
Chris Craik65cd6122012-12-10 17:56:27 -0800323
John Reck1aa5d2d2014-07-24 13:38:28 -0700324 Vector2 center = {current->x, current->y};
Chris Craik6d29c8d2013-05-08 18:35:44 -0700325 Vertex::set(&buffer[currentIndex++], center + strokeOffset);
326 Vertex::set(&buffer[currentIndex++], center - strokeOffset);
Chris Craik65cd6122012-12-10 17:56:27 -0800327
328 current = next;
329 lastNormal = nextNormal;
330 }
331
Chris Craik6d29c8d2013-05-08 18:35:44 -0700332 storeBeginEnd(paintInfo, vertices[lastIndex], lastNormal, buffer, currentIndex, false);
Chris Craik65cd6122012-12-10 17:56:27 -0800333
334 DEBUG_DUMP_BUFFER();
335}
336
337/**
338 * Populates a vertexBuffer with AlphaVertices to create an anti-aliased fill shape tessellation
Chris Craik6d29c8d2013-05-08 18:35:44 -0700339 *
Chris Craik65cd6122012-12-10 17:56:27 -0800340 * 1 - create the AA perimeter of unit width, by zig-zagging at each point around the perimeter of
341 * the shape (using 2 * perimeter.size() vertices)
342 *
343 * 2 - wrap around to the beginning to complete the perimeter (2 vertices)
344 *
345 * 3 - zig zag back and forth inside the shape to fill it (using perimeter.size() vertices)
346 */
347void getFillVerticesFromPerimeterAA(const PaintInfo& paintInfo, const Vector<Vertex>& perimeter,
Chris Craikf0a59072013-11-19 18:00:46 -0800348 VertexBuffer& vertexBuffer, float maxAlpha = 1.0f) {
Chris Craik65cd6122012-12-10 17:56:27 -0800349 AlphaVertex* buffer = vertexBuffer.alloc<AlphaVertex>(perimeter.size() * 3 + 2);
350
351 // generate alpha points - fill Alpha vertex gaps in between each point with
352 // alpha 0 vertex, offset by a scaled normal.
353 int currentIndex = 0;
354 const Vertex* last = &(perimeter[perimeter.size() - 1]);
355 const Vertex* current = &(perimeter[0]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700356 Vector2 lastNormal = {current->y - last->y, last->x - current->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800357 lastNormal.normalize();
358 for (unsigned int i = 0; i < perimeter.size(); i++) {
359 const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700360 Vector2 nextNormal = {next->y - current->y, current->x - next->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800361 nextNormal.normalize();
362
363 // AA point offset from original point is that point's normal, such that each side is offset
364 // by .5 pixels
John Reck1aa5d2d2014-07-24 13:38:28 -0700365 Vector2 totalOffset = paintInfo.deriveAAOffset(totalOffsetFromNormals(lastNormal, nextNormal));
Chris Craik65cd6122012-12-10 17:56:27 -0800366
367 AlphaVertex::set(&buffer[currentIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700368 current->x + totalOffset.x,
369 current->y + totalOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800370 0.0f);
371 AlphaVertex::set(&buffer[currentIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700372 current->x - totalOffset.x,
373 current->y - totalOffset.y,
Chris Craikf0a59072013-11-19 18:00:46 -0800374 maxAlpha);
Chris Craik65cd6122012-12-10 17:56:27 -0800375
376 last = current;
377 current = next;
378 lastNormal = nextNormal;
379 }
380
381 // wrap around to beginning
Chris Craik11a75672013-12-16 17:08:15 -0800382 buffer[currentIndex++] = buffer[0];
383 buffer[currentIndex++] = buffer[1];
Chris Craik65cd6122012-12-10 17:56:27 -0800384
385 // zig zag between all previous points on the inside of the hull to create a
386 // triangle strip that fills the hull, repeating the first inner point to
387 // create degenerate tris to start inside path
388 int srcAindex = 0;
389 int srcBindex = perimeter.size() - 1;
390 while (srcAindex <= srcBindex) {
Chris Craik11a75672013-12-16 17:08:15 -0800391 buffer[currentIndex++] = buffer[srcAindex * 2 + 1];
Chris Craik65cd6122012-12-10 17:56:27 -0800392 if (srcAindex == srcBindex) break;
Chris Craik11a75672013-12-16 17:08:15 -0800393 buffer[currentIndex++] = buffer[srcBindex * 2 + 1];
Chris Craik65cd6122012-12-10 17:56:27 -0800394 srcAindex++;
395 srcBindex--;
396 }
397
398 DEBUG_DUMP_BUFFER();
399}
400
401/**
402 * Stores geometry for a single, AA-perimeter (potentially rounded) cap
403 *
404 * For explanation of constants and general methodoloyg, see comments for
405 * getStrokeVerticesFromUnclosedVerticesAA() below.
406 */
Chris Craik6d29c8d2013-05-08 18:35:44 -0700407inline static void storeCapAA(const PaintInfo& paintInfo, const Vector<Vertex>& vertices,
John Reck1aa5d2d2014-07-24 13:38:28 -0700408 AlphaVertex* buffer, bool isFirst, Vector2 normal, int offset) {
Chris Craik65cd6122012-12-10 17:56:27 -0800409 const int extra = paintInfo.capExtraDivisions();
410 const int extraOffset = (extra + 1) / 2;
411 const int capIndex = isFirst
412 ? 2 * offset + 6 + 2 * (extra + extraOffset)
413 : offset + 2 + 2 * extraOffset;
414 if (isFirst) normal *= -1;
415
416 // TODO: this normal should be scaled by radialScale if extra != 0, see totalOffsetFromNormals()
John Reck1aa5d2d2014-07-24 13:38:28 -0700417 Vector2 AAOffset = paintInfo.deriveAAOffset(normal);
Chris Craik65cd6122012-12-10 17:56:27 -0800418
John Reck1aa5d2d2014-07-24 13:38:28 -0700419 Vector2 strokeOffset = normal;
Chris Craik65cd6122012-12-10 17:56:27 -0800420 paintInfo.scaleOffsetForStrokeWidth(strokeOffset);
John Reck1aa5d2d2014-07-24 13:38:28 -0700421 Vector2 outerOffset = strokeOffset + AAOffset;
422 Vector2 innerOffset = strokeOffset - AAOffset;
Chris Craik65cd6122012-12-10 17:56:27 -0800423
John Reck1aa5d2d2014-07-24 13:38:28 -0700424 Vector2 capAAOffset = {0, 0};
Chris Craik65cd6122012-12-10 17:56:27 -0800425 if (paintInfo.cap != SkPaint::kRound_Cap) {
426 // if the cap is square or butt, the inside primary cap vertices will be inset in two
427 // directions - both normal to the stroke, and parallel to it.
John Reck1aa5d2d2014-07-24 13:38:28 -0700428 capAAOffset = (Vector2){-AAOffset.y, AAOffset.x};
Chris Craik65cd6122012-12-10 17:56:27 -0800429 }
430
431 // determine referencePoint, the center point for the 4 primary cap vertices
432 const Vertex* point = isFirst ? vertices.begin() : (vertices.end() - 1);
John Reck1aa5d2d2014-07-24 13:38:28 -0700433 Vector2 referencePoint = {point->x, point->y};
Chris Craik65cd6122012-12-10 17:56:27 -0800434 if (paintInfo.cap == SkPaint::kSquare_Cap) {
435 // To account for square cap, move the primary cap vertices (that create the AA edge) by the
436 // stroke offset vector (rotated to be parallel to the stroke)
John Reck1aa5d2d2014-07-24 13:38:28 -0700437 Vector2 rotated = {-strokeOffset.y, strokeOffset.x};
438 referencePoint += rotated;
Chris Craik65cd6122012-12-10 17:56:27 -0800439 }
440
441 AlphaVertex::set(&buffer[capIndex + 0],
442 referencePoint.x + outerOffset.x + capAAOffset.x,
443 referencePoint.y + outerOffset.y + capAAOffset.y,
444 0.0f);
445 AlphaVertex::set(&buffer[capIndex + 1],
446 referencePoint.x + innerOffset.x - capAAOffset.x,
447 referencePoint.y + innerOffset.y - capAAOffset.y,
448 paintInfo.maxAlpha);
449
450 bool isRound = paintInfo.cap == SkPaint::kRound_Cap;
451
452 const int postCapIndex = (isRound && isFirst) ? (2 * extraOffset - 2) : capIndex + (2 * extra);
453 AlphaVertex::set(&buffer[postCapIndex + 2],
454 referencePoint.x - outerOffset.x + capAAOffset.x,
455 referencePoint.y - outerOffset.y + capAAOffset.y,
456 0.0f);
457 AlphaVertex::set(&buffer[postCapIndex + 3],
458 referencePoint.x - innerOffset.x - capAAOffset.x,
459 referencePoint.y - innerOffset.y - capAAOffset.y,
460 paintInfo.maxAlpha);
461
462 if (isRound) {
463 const float dTheta = PI / (extra + 1);
464 const float radialScale = 2.0f / (1 + cos(dTheta));
465 float theta = atan2(normal.y, normal.x);
466 int capPerimIndex = capIndex + 2;
467
468 for (int i = 0; i < extra; i++) {
469 theta += dTheta;
470
John Reck1aa5d2d2014-07-24 13:38:28 -0700471 Vector2 radialOffset = {cos(theta), sin(theta)};
Chris Craik65cd6122012-12-10 17:56:27 -0800472
473 // scale to compensate for pinching at sharp angles, see totalOffsetFromNormals()
474 radialOffset *= radialScale;
475
476 AAOffset = paintInfo.deriveAAOffset(radialOffset);
477 paintInfo.scaleOffsetForStrokeWidth(radialOffset);
478 AlphaVertex::set(&buffer[capPerimIndex++],
479 referencePoint.x + radialOffset.x + AAOffset.x,
480 referencePoint.y + radialOffset.y + AAOffset.y,
481 0.0f);
482 AlphaVertex::set(&buffer[capPerimIndex++],
483 referencePoint.x + radialOffset.x - AAOffset.x,
484 referencePoint.y + radialOffset.y - AAOffset.y,
485 paintInfo.maxAlpha);
486
487 if (isFirst && i == extra - extraOffset) {
488 //copy most recent two points to first two points
Chris Craik11a75672013-12-16 17:08:15 -0800489 buffer[0] = buffer[capPerimIndex - 2];
490 buffer[1] = buffer[capPerimIndex - 1];
Chris Craik65cd6122012-12-10 17:56:27 -0800491
492 capPerimIndex = 2; // start writing the rest of the round cap at index 2
493 }
494 }
495
496 if (isFirst) {
497 const int startCapFillIndex = capIndex + 2 * (extra - extraOffset) + 4;
498 int capFillIndex = startCapFillIndex;
499 for (int i = 0; i < extra + 2; i += 2) {
Chris Craik11a75672013-12-16 17:08:15 -0800500 buffer[capFillIndex++] = buffer[1 + i];
Chris Craik65cd6122012-12-10 17:56:27 -0800501 // TODO: to support odd numbers of divisions, break here on the last iteration
Chris Craik11a75672013-12-16 17:08:15 -0800502 buffer[capFillIndex++] = buffer[startCapFillIndex - 3 - i];
Chris Craik65cd6122012-12-10 17:56:27 -0800503 }
504 } else {
505 int capFillIndex = 6 * vertices.size() + 2 + 6 * extra - (extra + 2);
506 for (int i = 0; i < extra + 2; i += 2) {
Chris Craik11a75672013-12-16 17:08:15 -0800507 buffer[capFillIndex++] = buffer[capIndex + 1 + i];
Chris Craik65cd6122012-12-10 17:56:27 -0800508 // TODO: to support odd numbers of divisions, break here on the last iteration
Chris Craik11a75672013-12-16 17:08:15 -0800509 buffer[capFillIndex++] = buffer[capIndex + 3 + 2 * extra - i];
Chris Craik65cd6122012-12-10 17:56:27 -0800510 }
511 }
512 return;
513 }
514 if (isFirst) {
Chris Craik11a75672013-12-16 17:08:15 -0800515 buffer[0] = buffer[postCapIndex + 2];
516 buffer[1] = buffer[postCapIndex + 3];
517 buffer[postCapIndex + 4] = buffer[1]; // degenerate tris (the only two!)
518 buffer[postCapIndex + 5] = buffer[postCapIndex + 1];
Chris Craik65cd6122012-12-10 17:56:27 -0800519 } else {
Chris Craik11a75672013-12-16 17:08:15 -0800520 buffer[6 * vertices.size()] = buffer[postCapIndex + 1];
521 buffer[6 * vertices.size() + 1] = buffer[postCapIndex + 3];
Chris Craik65cd6122012-12-10 17:56:27 -0800522 }
523}
524
525/*
526the geometry for an aa, capped stroke consists of the following:
527
528 # vertices | function
529----------------------------------------------------------------------
530a) 2 | Start AA perimeter
531b) 2, 2 * roundDivOff | First half of begin cap's perimeter
532 |
533 2 * middlePts | 'Outer' or 'Top' AA perimeter half (between caps)
534 |
535a) 4 | End cap's
536b) 2, 2 * roundDivs, 2 | AA perimeter
537 |
538 2 * middlePts | 'Inner' or 'bottom' AA perimeter half
539 |
540a) 6 | Begin cap's perimeter
541b) 2, 2*(rD - rDO + 1), | Last half of begin cap's perimeter
542 roundDivs, 2 |
543 |
544 2 * middlePts | Stroke's full opacity center strip
545 |
546a) 2 | end stroke
547b) 2, roundDivs | (and end cap fill, for round)
548
549Notes:
550* rows starting with 'a)' denote the Butt or Square cap vertex use, 'b)' denote Round
551
552* 'middlePts' is (number of points in the unclosed input vertex list, minus 2) times two
553
554* 'roundDivs' or 'rD' is the number of extra vertices (beyond the minimum of 2) that define the
555 round cap's shape, and is at least two. This will increase with cap size to sufficiently
556 define the cap's level of tessellation.
557
558* 'roundDivOffset' or 'rDO' is the point about halfway along the start cap's round perimeter, where
559 the stream of vertices for the AA perimeter starts. By starting and ending the perimeter at
560 this offset, the fill of the stroke is drawn from this point with minimal extra vertices.
561
562This means the outer perimeter starts at:
563 outerIndex = (2) OR (2 + 2 * roundDivOff)
564the inner perimeter (since it is filled in reverse) starts at:
565 innerIndex = outerIndex + (4 * middlePts) + ((4) OR (4 + 2 * roundDivs)) - 1
566the stroke starts at:
567 strokeIndex = innerIndex + 1 + ((6) OR (6 + 3 * roundDivs - 2 * roundDivOffset))
568
569The total needed allocated space is either:
570 2 + 4 + 6 + 2 + 3 * (2 * middlePts) = 14 + 6 * middlePts = 2 + 6 * pts
571or, for rounded caps:
572 (2 + 2 * rDO) + (4 + 2 * rD) + (2 * (rD - rDO + 1)
573 + roundDivs + 4) + (2 + roundDivs) + 3 * (2 * middlePts)
574 = 14 + 6 * middlePts + 6 * roundDivs
575 = 2 + 6 * pts + 6 * roundDivs
576 */
577void getStrokeVerticesFromUnclosedVerticesAA(const PaintInfo& paintInfo,
578 const Vector<Vertex>& vertices, VertexBuffer& vertexBuffer) {
579
580 const int extra = paintInfo.capExtraDivisions();
581 const int allocSize = 6 * vertices.size() + 2 + 6 * extra;
582
583 AlphaVertex* buffer = vertexBuffer.alloc<AlphaVertex>(allocSize);
584
585 const int extraOffset = (extra + 1) / 2;
586 int offset = 2 * (vertices.size() - 2);
587 // there is no outer/inner here, using them for consistency with below approach
588 int currentAAOuterIndex = 2 + 2 * extraOffset;
589 int currentAAInnerIndex = currentAAOuterIndex + (2 * offset) + 3 + (2 * extra);
590 int currentStrokeIndex = currentAAInnerIndex + 7 + (3 * extra - 2 * extraOffset);
591
592 const Vertex* last = &(vertices[0]);
593 const Vertex* current = &(vertices[1]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700594 Vector2 lastNormal = {current->y - last->y, last->x - current->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800595 lastNormal.normalize();
596
597 // TODO: use normal from bezier traversal for cap, instead of from vertices
598 storeCapAA(paintInfo, vertices, buffer, true, lastNormal, offset);
599
600 for (unsigned int i = 1; i < vertices.size() - 1; i++) {
601 const Vertex* next = &(vertices[i + 1]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700602 Vector2 nextNormal = {next->y - current->y, current->x - next->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800603 nextNormal.normalize();
604
John Reck1aa5d2d2014-07-24 13:38:28 -0700605 Vector2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
606 Vector2 AAOffset = paintInfo.deriveAAOffset(totalOffset);
Chris Craik65cd6122012-12-10 17:56:27 -0800607
John Reck1aa5d2d2014-07-24 13:38:28 -0700608 Vector2 innerOffset = totalOffset;
Chris Craik65cd6122012-12-10 17:56:27 -0800609 paintInfo.scaleOffsetForStrokeWidth(innerOffset);
John Reck1aa5d2d2014-07-24 13:38:28 -0700610 Vector2 outerOffset = innerOffset + AAOffset;
Chris Craik65cd6122012-12-10 17:56:27 -0800611 innerOffset -= AAOffset;
612
613 AlphaVertex::set(&buffer[currentAAOuterIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700614 current->x + outerOffset.x,
615 current->y + outerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800616 0.0f);
617 AlphaVertex::set(&buffer[currentAAOuterIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700618 current->x + innerOffset.x,
619 current->y + innerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800620 paintInfo.maxAlpha);
621
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 AlphaVertex::set(&buffer[currentStrokeIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700627 current->x - innerOffset.x,
628 current->y - innerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800629 paintInfo.maxAlpha);
630
631 AlphaVertex::set(&buffer[currentAAInnerIndex--],
Romain Guy3380cfd2013-08-15 16:57:57 -0700632 current->x - innerOffset.x,
633 current->y - innerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800634 paintInfo.maxAlpha);
635 AlphaVertex::set(&buffer[currentAAInnerIndex--],
Romain Guy3380cfd2013-08-15 16:57:57 -0700636 current->x - outerOffset.x,
637 current->y - outerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800638 0.0f);
639
640 current = next;
641 lastNormal = nextNormal;
642 }
643
644 // TODO: use normal from bezier traversal for cap, instead of from vertices
645 storeCapAA(paintInfo, vertices, buffer, false, lastNormal, offset);
646
647 DEBUG_DUMP_ALPHA_BUFFER();
648}
649
650
651void getStrokeVerticesFromPerimeterAA(const PaintInfo& paintInfo, const Vector<Vertex>& perimeter,
652 VertexBuffer& vertexBuffer) {
653 AlphaVertex* buffer = vertexBuffer.alloc<AlphaVertex>(6 * perimeter.size() + 8);
654
655 int offset = 2 * perimeter.size() + 3;
656 int currentAAOuterIndex = 0;
657 int currentStrokeIndex = offset;
658 int currentAAInnerIndex = offset * 2;
659
660 const Vertex* last = &(perimeter[perimeter.size() - 1]);
661 const Vertex* current = &(perimeter[0]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700662 Vector2 lastNormal = {current->y - last->y, last->x - current->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800663 lastNormal.normalize();
664 for (unsigned int i = 0; i < perimeter.size(); i++) {
665 const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]);
John Reck1aa5d2d2014-07-24 13:38:28 -0700666 Vector2 nextNormal = {next->y - current->y, current->x - next->x};
Chris Craik65cd6122012-12-10 17:56:27 -0800667 nextNormal.normalize();
668
John Reck1aa5d2d2014-07-24 13:38:28 -0700669 Vector2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
670 Vector2 AAOffset = paintInfo.deriveAAOffset(totalOffset);
Chris Craik65cd6122012-12-10 17:56:27 -0800671
John Reck1aa5d2d2014-07-24 13:38:28 -0700672 Vector2 innerOffset = totalOffset;
Chris Craik65cd6122012-12-10 17:56:27 -0800673 paintInfo.scaleOffsetForStrokeWidth(innerOffset);
John Reck1aa5d2d2014-07-24 13:38:28 -0700674 Vector2 outerOffset = innerOffset + AAOffset;
Chris Craik65cd6122012-12-10 17:56:27 -0800675 innerOffset -= AAOffset;
676
677 AlphaVertex::set(&buffer[currentAAOuterIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700678 current->x + outerOffset.x,
679 current->y + outerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800680 0.0f);
681 AlphaVertex::set(&buffer[currentAAOuterIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700682 current->x + innerOffset.x,
683 current->y + innerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800684 paintInfo.maxAlpha);
685
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 AlphaVertex::set(&buffer[currentStrokeIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700691 current->x - innerOffset.x,
692 current->y - innerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800693 paintInfo.maxAlpha);
694
695 AlphaVertex::set(&buffer[currentAAInnerIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700696 current->x - innerOffset.x,
697 current->y - innerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800698 paintInfo.maxAlpha);
699 AlphaVertex::set(&buffer[currentAAInnerIndex++],
Romain Guy3380cfd2013-08-15 16:57:57 -0700700 current->x - outerOffset.x,
701 current->y - outerOffset.y,
Chris Craik65cd6122012-12-10 17:56:27 -0800702 0.0f);
703
704 last = current;
705 current = next;
706 lastNormal = nextNormal;
707 }
708
709 // wrap each strip around to beginning, creating degenerate tris to bridge strips
Chris Craik11a75672013-12-16 17:08:15 -0800710 buffer[currentAAOuterIndex++] = buffer[0];
711 buffer[currentAAOuterIndex++] = buffer[1];
712 buffer[currentAAOuterIndex++] = buffer[1];
Chris Craik65cd6122012-12-10 17:56:27 -0800713
Chris Craik11a75672013-12-16 17:08:15 -0800714 buffer[currentStrokeIndex++] = buffer[offset];
715 buffer[currentStrokeIndex++] = buffer[offset + 1];
716 buffer[currentStrokeIndex++] = buffer[offset + 1];
Chris Craik65cd6122012-12-10 17:56:27 -0800717
Chris Craik11a75672013-12-16 17:08:15 -0800718 buffer[currentAAInnerIndex++] = buffer[2 * offset];
719 buffer[currentAAInnerIndex++] = buffer[2 * offset + 1];
Chris Craik65cd6122012-12-10 17:56:27 -0800720 // don't need to create last degenerate tri
721
722 DEBUG_DUMP_ALPHA_BUFFER();
723}
724
725void PathTessellator::tessellatePath(const SkPath &path, const SkPaint* paint,
Chris Craikd6b65f62014-01-01 14:45:21 -0800726 const mat4& transform, VertexBuffer& vertexBuffer) {
Chris Craik65cd6122012-12-10 17:56:27 -0800727 ATRACE_CALL();
728
729 const PaintInfo paintInfo(paint, transform);
730
731 Vector<Vertex> tempVertices;
732 float threshInvScaleX = paintInfo.inverseScaleX;
733 float threshInvScaleY = paintInfo.inverseScaleY;
734 if (paintInfo.style == SkPaint::kStroke_Style) {
735 // alter the bezier recursion threshold values we calculate in order to compensate for
736 // expansion done after the path vertices are found
737 SkRect bounds = path.getBounds();
738 if (!bounds.isEmpty()) {
739 threshInvScaleX *= bounds.width() / (bounds.width() + paint->getStrokeWidth());
740 threshInvScaleY *= bounds.height() / (bounds.height() + paint->getStrokeWidth());
741 }
742 }
743
744 // force close if we're filling the path, since fill path expects closed perimeter.
745 bool forceClose = paintInfo.style != SkPaint::kStroke_Style;
746 bool wasClosed = approximatePathOutlineVertices(path, forceClose,
Chris Craik15a07a22014-01-26 13:43:53 -0800747 threshInvScaleX * threshInvScaleX, threshInvScaleY * threshInvScaleY,
748 OUTLINE_REFINE_THRESHOLD_SQUARED, tempVertices);
Chris Craik65cd6122012-12-10 17:56:27 -0800749
750 if (!tempVertices.size()) {
751 // path was empty, return without allocating vertex buffer
752 return;
753 }
754
755#if VERTEX_DEBUG
756 for (unsigned int i = 0; i < tempVertices.size(); i++) {
757 ALOGD("orig path: point at %f %f",
Romain Guy3380cfd2013-08-15 16:57:57 -0700758 tempVertices[i].x, tempVertices[i].y);
Chris Craik65cd6122012-12-10 17:56:27 -0800759 }
760#endif
761
762 if (paintInfo.style == SkPaint::kStroke_Style) {
763 if (!paintInfo.isAA) {
764 if (wasClosed) {
765 getStrokeVerticesFromPerimeter(paintInfo, tempVertices, vertexBuffer);
766 } else {
767 getStrokeVerticesFromUnclosedVertices(paintInfo, tempVertices, vertexBuffer);
768 }
769
770 } else {
771 if (wasClosed) {
772 getStrokeVerticesFromPerimeterAA(paintInfo, tempVertices, vertexBuffer);
773 } else {
774 getStrokeVerticesFromUnclosedVerticesAA(paintInfo, tempVertices, vertexBuffer);
775 }
776 }
777 } else {
778 // For kStrokeAndFill style, the path should be adjusted externally.
779 // It will be treated as a fill here.
780 if (!paintInfo.isAA) {
781 getFillVerticesFromPerimeter(tempVertices, vertexBuffer);
782 } else {
783 getFillVerticesFromPerimeterAA(paintInfo, tempVertices, vertexBuffer);
784 }
785 }
Chris Craik05f3d6e2014-06-02 16:27:04 -0700786
787 Rect bounds(path.getBounds());
788 paintInfo.expandBoundsForStroke(&bounds);
789 vertexBuffer.setBounds(bounds);
Chris Craik65cd6122012-12-10 17:56:27 -0800790}
791
Chris Craik6d29c8d2013-05-08 18:35:44 -0700792template <class TYPE>
793static void instanceVertices(VertexBuffer& srcBuffer, VertexBuffer& dstBuffer,
Chris Craik05f3d6e2014-06-02 16:27:04 -0700794 const float* points, int count, Rect& bounds) {
Chris Craik6d29c8d2013-05-08 18:35:44 -0700795 bounds.set(points[0], points[1], points[0], points[1]);
796
797 int numPoints = count / 2;
798 int verticesPerPoint = srcBuffer.getVertexCount();
799 dstBuffer.alloc<TYPE>(numPoints * verticesPerPoint + (numPoints - 1) * 2);
800
801 for (int i = 0; i < count; i += 2) {
Chris Craikc93e45c2014-07-16 10:15:56 -0700802 bounds.expandToCoverVertex(points[i + 0], points[i + 1]);
Chris Craik6d29c8d2013-05-08 18:35:44 -0700803 dstBuffer.copyInto<TYPE>(srcBuffer, points[i + 0], points[i + 1]);
804 }
805 dstBuffer.createDegenerateSeparators<TYPE>(verticesPerPoint);
806}
807
Chris Craikd218a922014-01-02 17:13:34 -0800808void PathTessellator::tessellatePoints(const float* points, int count, const SkPaint* paint,
Chris Craik05f3d6e2014-06-02 16:27:04 -0700809 const mat4& transform, VertexBuffer& vertexBuffer) {
Chris Craik6d29c8d2013-05-08 18:35:44 -0700810 const PaintInfo paintInfo(paint, transform);
811
812 // determine point shape
813 SkPath path;
814 float radius = paintInfo.halfStrokeWidth;
Rob Tsukb7c26562014-11-03 16:29:03 -0800815 if (radius == 0.0f) radius = 0.5f;
Chris Craik6d29c8d2013-05-08 18:35:44 -0700816
817 if (paintInfo.cap == SkPaint::kRound_Cap) {
818 path.addCircle(0, 0, radius);
819 } else {
820 path.addRect(-radius, -radius, radius, radius);
821 }
822
823 // calculate outline
824 Vector<Vertex> outlineVertices;
825 approximatePathOutlineVertices(path, true,
826 paintInfo.inverseScaleX * paintInfo.inverseScaleX,
Chris Craik15a07a22014-01-26 13:43:53 -0800827 paintInfo.inverseScaleY * paintInfo.inverseScaleY,
828 OUTLINE_REFINE_THRESHOLD_SQUARED, outlineVertices);
Chris Craik6d29c8d2013-05-08 18:35:44 -0700829
830 if (!outlineVertices.size()) return;
831
Chris Craik05f3d6e2014-06-02 16:27:04 -0700832 Rect bounds;
Chris Craik6d29c8d2013-05-08 18:35:44 -0700833 // tessellate, then duplicate outline across points
834 int numPoints = count / 2;
835 VertexBuffer tempBuffer;
836 if (!paintInfo.isAA) {
837 getFillVerticesFromPerimeter(outlineVertices, tempBuffer);
838 instanceVertices<Vertex>(tempBuffer, vertexBuffer, points, count, bounds);
839 } else {
Chris Craikf0a59072013-11-19 18:00:46 -0800840 // note: pass maxAlpha directly, since we want fill to be alpha modulated
841 getFillVerticesFromPerimeterAA(paintInfo, outlineVertices, tempBuffer, paintInfo.maxAlpha);
Chris Craik6d29c8d2013-05-08 18:35:44 -0700842 instanceVertices<AlphaVertex>(tempBuffer, vertexBuffer, points, count, bounds);
843 }
844
Chris Craikf0a59072013-11-19 18:00:46 -0800845 // expand bounds from vertex coords to pixel data
Chris Craik05f3d6e2014-06-02 16:27:04 -0700846 paintInfo.expandBoundsForStroke(&bounds);
847 vertexBuffer.setBounds(bounds);
Chris Craik65cd6122012-12-10 17:56:27 -0800848}
849
Chris Craikd218a922014-01-02 17:13:34 -0800850void PathTessellator::tessellateLines(const float* points, int count, const SkPaint* paint,
Chris Craik05f3d6e2014-06-02 16:27:04 -0700851 const mat4& transform, VertexBuffer& vertexBuffer) {
Chris Craik65cd6122012-12-10 17:56:27 -0800852 ATRACE_CALL();
853 const PaintInfo paintInfo(paint, transform);
854
855 const int extra = paintInfo.capExtraDivisions();
856 int numLines = count / 4;
857 int lineAllocSize;
858 // pre-allocate space for lines in the buffer, and degenerate tris in between
859 if (paintInfo.isAA) {
860 lineAllocSize = 6 * (2) + 2 + 6 * extra;
861 vertexBuffer.alloc<AlphaVertex>(numLines * lineAllocSize + (numLines - 1) * 2);
862 } else {
863 lineAllocSize = 2 * ((2) + extra);
864 vertexBuffer.alloc<Vertex>(numLines * lineAllocSize + (numLines - 1) * 2);
865 }
866
867 Vector<Vertex> tempVertices;
868 tempVertices.push();
869 tempVertices.push();
870 Vertex* tempVerticesData = tempVertices.editArray();
Chris Craik05f3d6e2014-06-02 16:27:04 -0700871 Rect bounds;
Chris Craik65cd6122012-12-10 17:56:27 -0800872 bounds.set(points[0], points[1], points[0], points[1]);
873 for (int i = 0; i < count; i += 4) {
874 Vertex::set(&(tempVerticesData[0]), points[i + 0], points[i + 1]);
875 Vertex::set(&(tempVerticesData[1]), points[i + 2], points[i + 3]);
876
877 if (paintInfo.isAA) {
878 getStrokeVerticesFromUnclosedVerticesAA(paintInfo, tempVertices, vertexBuffer);
879 } else {
880 getStrokeVerticesFromUnclosedVertices(paintInfo, tempVertices, vertexBuffer);
881 }
882
883 // calculate bounds
Chris Craikc93e45c2014-07-16 10:15:56 -0700884 bounds.expandToCoverVertex(tempVerticesData[0].x, tempVerticesData[0].y);
885 bounds.expandToCoverVertex(tempVerticesData[1].x, tempVerticesData[1].y);
Chris Craik65cd6122012-12-10 17:56:27 -0800886 }
887
Chris Craik65cd6122012-12-10 17:56:27 -0800888 // since multiple objects tessellated into buffer, separate them with degen tris
889 if (paintInfo.isAA) {
890 vertexBuffer.createDegenerateSeparators<AlphaVertex>(lineAllocSize);
891 } else {
892 vertexBuffer.createDegenerateSeparators<Vertex>(lineAllocSize);
893 }
Chris Craikf0a59072013-11-19 18:00:46 -0800894
895 // expand bounds from vertex coords to pixel data
Chris Craik05f3d6e2014-06-02 16:27:04 -0700896 paintInfo.expandBoundsForStroke(&bounds);
897 vertexBuffer.setBounds(bounds);
Chris Craik65cd6122012-12-10 17:56:27 -0800898}
899
900///////////////////////////////////////////////////////////////////////////////
901// Simple path line approximation
902///////////////////////////////////////////////////////////////////////////////
903
Chris Craik15a07a22014-01-26 13:43:53 -0800904bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, float thresholdSquared,
905 Vector<Vertex>& outputVertices) {
906 return approximatePathOutlineVertices(path, true, 1.0f, 1.0f, thresholdSquared, outputVertices);
907}
908
Chris Craik65cd6122012-12-10 17:56:27 -0800909void pushToVector(Vector<Vertex>& vertices, float x, float y) {
910 // TODO: make this not yuck
911 vertices.push();
912 Vertex* newVertex = &(vertices.editArray()[vertices.size() - 1]);
913 Vertex::set(newVertex, x, y);
914}
915
916bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool forceClose,
Chris Craik15a07a22014-01-26 13:43:53 -0800917 float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared,
918 Vector<Vertex>& outputVertices) {
Chris Craik65cd6122012-12-10 17:56:27 -0800919 ATRACE_CALL();
920
921 // TODO: to support joins other than sharp miter, join vertices should be labelled in the
922 // perimeter, or resolved into more vertices. Reconsider forceClose-ing in that case.
923 SkPath::Iter iter(path, forceClose);
924 SkPoint pts[4];
925 SkPath::Verb v;
926 while (SkPath::kDone_Verb != (v = iter.next(pts))) {
927 switch (v) {
928 case SkPath::kMove_Verb:
929 pushToVector(outputVertices, pts[0].x(), pts[0].y());
930 ALOGV("Move to pos %f %f", pts[0].x(), pts[0].y());
931 break;
932 case SkPath::kClose_Verb:
933 ALOGV("Close at pos %f %f", pts[0].x(), pts[0].y());
934 break;
935 case SkPath::kLine_Verb:
936 ALOGV("kLine_Verb %f %f -> %f %f", pts[0].x(), pts[0].y(), pts[1].x(), pts[1].y());
937 pushToVector(outputVertices, pts[1].x(), pts[1].y());
938 break;
939 case SkPath::kQuad_Verb:
940 ALOGV("kQuad_Verb");
941 recursiveQuadraticBezierVertices(
942 pts[0].x(), pts[0].y(),
943 pts[2].x(), pts[2].y(),
944 pts[1].x(), pts[1].y(),
Chris Craik15a07a22014-01-26 13:43:53 -0800945 sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices);
Chris Craik65cd6122012-12-10 17:56:27 -0800946 break;
947 case SkPath::kCubic_Verb:
948 ALOGV("kCubic_Verb");
949 recursiveCubicBezierVertices(
950 pts[0].x(), pts[0].y(),
951 pts[1].x(), pts[1].y(),
952 pts[3].x(), pts[3].y(),
953 pts[2].x(), pts[2].y(),
Chris Craik15a07a22014-01-26 13:43:53 -0800954 sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices);
Chris Craik65cd6122012-12-10 17:56:27 -0800955 break;
956 default:
957 break;
958 }
959 }
960
961 int size = outputVertices.size();
Romain Guy3380cfd2013-08-15 16:57:57 -0700962 if (size >= 2 && outputVertices[0].x == outputVertices[size - 1].x &&
963 outputVertices[0].y == outputVertices[size - 1].y) {
Chris Craik65cd6122012-12-10 17:56:27 -0800964 outputVertices.pop();
965 return true;
966 }
967 return false;
968}
969
970///////////////////////////////////////////////////////////////////////////////
971// Bezier approximation
972///////////////////////////////////////////////////////////////////////////////
973
974void PathTessellator::recursiveCubicBezierVertices(
975 float p1x, float p1y, float c1x, float c1y,
976 float p2x, float p2y, float c2x, float c2y,
Chris Craik15a07a22014-01-26 13:43:53 -0800977 float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared,
Chris Craik9c3dd622014-06-11 17:24:51 -0700978 Vector<Vertex>& outputVertices, int depth) {
Chris Craik65cd6122012-12-10 17:56:27 -0800979 float dx = p2x - p1x;
980 float dy = p2y - p1y;
981 float d1 = fabs((c1x - p2x) * dy - (c1y - p2y) * dx);
982 float d2 = fabs((c2x - p2x) * dy - (c2y - p2y) * dx);
983 float d = d1 + d2;
984
985 // multiplying by sqrInvScaleY/X equivalent to multiplying in dimensional scale factors
Chris Craik74cf7e62014-08-07 14:34:46 -0700986 if (depth >= MAX_DEPTH
987 || d * d <= thresholdSquared * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) {
Chris Craik65cd6122012-12-10 17:56:27 -0800988 // below thresh, draw line by adding endpoint
989 pushToVector(outputVertices, p2x, p2y);
990 } else {
991 float p1c1x = (p1x + c1x) * 0.5f;
992 float p1c1y = (p1y + c1y) * 0.5f;
993 float p2c2x = (p2x + c2x) * 0.5f;
994 float p2c2y = (p2y + c2y) * 0.5f;
995
996 float c1c2x = (c1x + c2x) * 0.5f;
997 float c1c2y = (c1y + c2y) * 0.5f;
998
999 float p1c1c2x = (p1c1x + c1c2x) * 0.5f;
1000 float p1c1c2y = (p1c1y + c1c2y) * 0.5f;
1001
1002 float p2c1c2x = (p2c2x + c1c2x) * 0.5f;
1003 float p2c1c2y = (p2c2y + c1c2y) * 0.5f;
1004
1005 float mx = (p1c1c2x + p2c1c2x) * 0.5f;
1006 float my = (p1c1c2y + p2c1c2y) * 0.5f;
1007
1008 recursiveCubicBezierVertices(
1009 p1x, p1y, p1c1x, p1c1y,
1010 mx, my, p1c1c2x, p1c1c2y,
Chris Craik9c3dd622014-06-11 17:24:51 -07001011 sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices, depth + 1);
Chris Craik65cd6122012-12-10 17:56:27 -08001012 recursiveCubicBezierVertices(
1013 mx, my, p2c1c2x, p2c1c2y,
1014 p2x, p2y, p2c2x, p2c2y,
Chris Craik9c3dd622014-06-11 17:24:51 -07001015 sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices, depth + 1);
Chris Craik65cd6122012-12-10 17:56:27 -08001016 }
1017}
1018
1019void PathTessellator::recursiveQuadraticBezierVertices(
1020 float ax, float ay,
1021 float bx, float by,
1022 float cx, float cy,
Chris Craik15a07a22014-01-26 13:43:53 -08001023 float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared,
Chris Craik9c3dd622014-06-11 17:24:51 -07001024 Vector<Vertex>& outputVertices, int depth) {
Chris Craik65cd6122012-12-10 17:56:27 -08001025 float dx = bx - ax;
1026 float dy = by - ay;
1027 float d = (cx - bx) * dy - (cy - by) * dx;
1028
Chris Craik74cf7e62014-08-07 14:34:46 -07001029 // multiplying by sqrInvScaleY/X equivalent to multiplying in dimensional scale factors
1030 if (depth >= MAX_DEPTH
1031 || d * d <= thresholdSquared * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) {
Chris Craik65cd6122012-12-10 17:56:27 -08001032 // below thresh, draw line by adding endpoint
1033 pushToVector(outputVertices, bx, by);
1034 } else {
1035 float acx = (ax + cx) * 0.5f;
1036 float bcx = (bx + cx) * 0.5f;
1037 float acy = (ay + cy) * 0.5f;
1038 float bcy = (by + cy) * 0.5f;
1039
1040 // midpoint
1041 float mx = (acx + bcx) * 0.5f;
1042 float my = (acy + bcy) * 0.5f;
1043
1044 recursiveQuadraticBezierVertices(ax, ay, mx, my, acx, acy,
Chris Craik9c3dd622014-06-11 17:24:51 -07001045 sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices, depth + 1);
Chris Craik65cd6122012-12-10 17:56:27 -08001046 recursiveQuadraticBezierVertices(mx, my, bx, by, bcx, bcy,
Chris Craik9c3dd622014-06-11 17:24:51 -07001047 sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices, depth + 1);
Chris Craik65cd6122012-12-10 17:56:27 -08001048 }
1049}
1050
1051}; // namespace uirenderer
1052}; // namespace android