blob: ef4ade24bf26ac2d0fe306e8a9b19dbf4b020a7a [file] [log] [blame]
jvanverth@google.com8ffb7042012-12-13 19:53:18 +00001/*
2 * Copyright 2012 The Android Open Source Project
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#define LOG_TAG "PathRenderer"
9#define LOG_NDEBUG 1
10#define ATRACE_TAG ATRACE_TAG_GRAPHICS
11
12#define VERTEX_DEBUG 0
13
14#include <SkPath.h>
15#include <SkPaint.h>
16
17#include <stdlib.h>
18#include <stdint.h>
19#include <sys/types.h>
20
jvanverth@google.com24b4f972012-12-18 14:13:46 +000021#include <SkTypes.h>
22#include <SkTrace.h>
23#include <SkMatrix.h>
24#include <SkPoint.h>
jvanverth@google.com8ffb7042012-12-13 19:53:18 +000025
jvanverth@google.com24b4f972012-12-18 14:13:46 +000026#ifdef VERBOSE
27#define ALOGV SkDebugf
28#else
29#define ALOGV(x, ...)
30#endif
31
32#include "AndroidPathRenderer.h"
jvanverth@google.com8ffb7042012-12-13 19:53:18 +000033#include "Vertex.h"
34
jvanverth@google.com24b4f972012-12-18 14:13:46 +000035#include "cutils/compiler.h"
36
jvanverth@google.com8ffb7042012-12-13 19:53:18 +000037namespace android {
38namespace uirenderer {
39
40#define THRESHOLD 0.5f
41
jvanverth@google.com24b4f972012-12-18 14:13:46 +000042SkRect PathRenderer::ComputePathBounds(const SkPath& path, const SkPaint* paint) {
jvanverth@google.com8ffb7042012-12-13 19:53:18 +000043 SkRect bounds = path.getBounds();
44 if (paint->getStyle() != SkPaint::kFill_Style) {
45 float outset = paint->getStrokeWidth() * 0.5f;
46 bounds.outset(outset, outset);
47 }
48 return bounds;
49}
50
jvanverth@google.com24b4f972012-12-18 14:13:46 +000051void computeInverseScales(const SkMatrix* transform, float &inverseScaleX, float& inverseScaleY) {
52 if (CC_UNLIKELY(transform && transform->getType() & (SkMatrix::kScale_Mask|SkMatrix::kAffine_Mask|SkMatrix::kPerspective_Mask))) {
53 float m00 = transform->getScaleX();
54 float m01 = transform->getSkewY();
55 float m10 = transform->getSkewX();
56 float m11 = transform->getScaleY();
jvanverth@google.com8ffb7042012-12-13 19:53:18 +000057 float scaleX = sqrt(m00 * m00 + m01 * m01);
58 float scaleY = sqrt(m10 * m10 + m11 * m11);
59 inverseScaleX = (scaleX != 0) ? (1.0f / scaleX) : 1.0f;
60 inverseScaleY = (scaleY != 0) ? (1.0f / scaleY) : 1.0f;
61 } else {
62 inverseScaleX = 1.0f;
63 inverseScaleY = 1.0f;
64 }
65}
66
67inline void copyVertex(Vertex* destPtr, const Vertex* srcPtr) {
68 Vertex::set(destPtr, srcPtr->position[0], srcPtr->position[1]);
69}
70
71inline void copyAlphaVertex(AlphaVertex* destPtr, const AlphaVertex* srcPtr) {
72 AlphaVertex::set(destPtr, srcPtr->position[0], srcPtr->position[1], srcPtr->alpha);
73}
74
75/**
76 * Produces a pseudo-normal for a vertex, given the normals of the two incoming lines. If the offset
77 * from each vertex in a perimeter is calculated, the resultant lines connecting the offset vertices
78 * will be offset by 1.0
79 *
80 * Note that we can't add and normalize the two vectors, that would result in a rectangle having an
81 * offset of (sqrt(2)/2, sqrt(2)/2) at each corner, instead of (1, 1)
82 *
83 * NOTE: assumes angles between normals 90 degrees or less
84 */
jvanverth@google.com24b4f972012-12-18 14:13:46 +000085inline SkVector totalOffsetFromNormals(const SkVector& normalA, const SkVector& normalB) {
86 SkVector pseudoNormal = normalA + normalB;
87 pseudoNormal.scale(1.0f / (1.0f + fabs(normalA.dot(normalB))));
88 return pseudoNormal;
jvanverth@google.com8ffb7042012-12-13 19:53:18 +000089}
90
jvanverth@google.com24b4f972012-12-18 14:13:46 +000091inline void scaleOffsetForStrokeWidth(SkVector& offset, float halfStrokeWidth,
jvanverth@google.com8ffb7042012-12-13 19:53:18 +000092 float inverseScaleX, float inverseScaleY) {
93 if (halfStrokeWidth == 0.0f) {
94 // hairline - compensate for scale
jvanverth@google.com24b4f972012-12-18 14:13:46 +000095 offset.fX *= 0.5f * inverseScaleX;
96 offset.fY *= 0.5f * inverseScaleY;
jvanverth@google.com8ffb7042012-12-13 19:53:18 +000097 } else {
jvanverth@google.com24b4f972012-12-18 14:13:46 +000098 offset.scale(halfStrokeWidth);
jvanverth@google.com8ffb7042012-12-13 19:53:18 +000099 }
100}
101
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000102void getFillVerticesFromPerimeter(const SkTArray<Vertex, true>& perimeter, VertexBuffer* vertexBuffer) {
103 Vertex* buffer = vertexBuffer->alloc<Vertex>(perimeter.count());
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000104
105 int currentIndex = 0;
106 // zig zag between all previous points on the inside of the hull to create a
107 // triangle strip that fills the hull
108 int srcAindex = 0;
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000109 int srcBindex = perimeter.count() - 1;
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000110 while (srcAindex <= srcBindex) {
111 copyVertex(&buffer[currentIndex++], &perimeter[srcAindex]);
112 if (srcAindex == srcBindex) break;
113 copyVertex(&buffer[currentIndex++], &perimeter[srcBindex]);
114 srcAindex++;
115 srcBindex--;
116 }
117}
118
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000119void getStrokeVerticesFromPerimeter(const SkTArray<Vertex, true>& perimeter, float halfStrokeWidth,
120 VertexBuffer* vertexBuffer, float inverseScaleX, float inverseScaleY) {
121 Vertex* buffer = vertexBuffer->alloc<Vertex>(perimeter.count() * 2 + 2);
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000122
123 int currentIndex = 0;
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000124 const Vertex* last = &(perimeter[perimeter.count() - 1]);
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000125 const Vertex* current = &(perimeter[0]);
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000126 SkVector lastNormal;
127 lastNormal.set(current->position[1] - last->position[1],
128 last->position[0] - current->position[0]);
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000129 lastNormal.normalize();
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000130 for (int i = 0; i < perimeter.count(); i++) {
131 const Vertex* next = &(perimeter[i + 1 >= perimeter.count() ? 0 : i + 1]);
132 SkVector nextNormal;
133 nextNormal.set(next->position[1] - current->position[1],
134 current->position[0] - next->position[0]);
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000135 nextNormal.normalize();
136
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000137 SkVector totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000138 scaleOffsetForStrokeWidth(totalOffset, halfStrokeWidth, inverseScaleX, inverseScaleY);
139
140 Vertex::set(&buffer[currentIndex++],
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000141 current->position[0] + totalOffset.fX,
142 current->position[1] + totalOffset.fY);
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000143
144 Vertex::set(&buffer[currentIndex++],
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000145 current->position[0] - totalOffset.fX,
146 current->position[1] - totalOffset.fY);
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000147
148 last = current;
149 current = next;
150 lastNormal = nextNormal;
151 }
152
153 // wrap around to beginning
154 copyVertex(&buffer[currentIndex++], &buffer[0]);
155 copyVertex(&buffer[currentIndex++], &buffer[1]);
156}
157
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000158void getStrokeVerticesFromUnclosedVertices(const SkTArray<Vertex, true>& vertices, float halfStrokeWidth,
159 VertexBuffer* vertexBuffer, float inverseScaleX, float inverseScaleY) {
160 Vertex* buffer = vertexBuffer->alloc<Vertex>(vertices.count() * 2);
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000161
162 int currentIndex = 0;
163 const Vertex* current = &(vertices[0]);
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000164 SkVector lastNormal;
165 for (int i = 0; i < vertices.count() - 1; i++) {
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000166 const Vertex* next = &(vertices[i + 1]);
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000167 SkVector nextNormal;
168 nextNormal.set(next->position[1] - current->position[1],
169 current->position[0] - next->position[0]);
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000170 nextNormal.normalize();
171
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000172 SkVector totalOffset;
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000173 if (i == 0) {
174 totalOffset = nextNormal;
175 } else {
176 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
177 }
178 scaleOffsetForStrokeWidth(totalOffset, halfStrokeWidth, inverseScaleX, inverseScaleY);
179
180 Vertex::set(&buffer[currentIndex++],
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000181 current->position[0] + totalOffset.fX,
182 current->position[1] + totalOffset.fY);
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000183
184 Vertex::set(&buffer[currentIndex++],
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000185 current->position[0] - totalOffset.fX,
186 current->position[1] - totalOffset.fY);
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000187
188 current = next;
189 lastNormal = nextNormal;
190 }
191
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000192 SkVector totalOffset = lastNormal;
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000193 scaleOffsetForStrokeWidth(totalOffset, halfStrokeWidth, inverseScaleX, inverseScaleY);
194
195 Vertex::set(&buffer[currentIndex++],
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000196 current->position[0] + totalOffset.fX,
197 current->position[1] + totalOffset.fY);
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000198 Vertex::set(&buffer[currentIndex++],
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000199 current->position[0] - totalOffset.fX,
200 current->position[1] - totalOffset.fY);
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000201#if VERTEX_DEBUG
202 for (unsigned int i = 0; i < vertexBuffer.getSize(); i++) {
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000203 SkDebugf("point at %f %f", buffer[i].position[0], buffer[i].position[1]);
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000204 }
205#endif
206}
207
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000208void getFillVerticesFromPerimeterAA(const SkTArray<Vertex, true>& perimeter, VertexBuffer* vertexBuffer,
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000209 float inverseScaleX, float inverseScaleY) {
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000210 AlphaVertex* buffer = vertexBuffer->alloc<AlphaVertex>(perimeter.count() * 3 + 2);
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000211
212 // generate alpha points - fill Alpha vertex gaps in between each point with
213 // alpha 0 vertex, offset by a scaled normal.
214 int currentIndex = 0;
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000215 const Vertex* last = &(perimeter[perimeter.count() - 1]);
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000216 const Vertex* current = &(perimeter[0]);
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000217 SkVector lastNormal;
218 lastNormal.set(current->position[1] - last->position[1],
219 last->position[0] - current->position[0]);
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000220 lastNormal.normalize();
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000221 for (int i = 0; i < perimeter.count(); i++) {
222 const Vertex* next = &(perimeter[i + 1 >= perimeter.count() ? 0 : i + 1]);
223 SkVector nextNormal;
224 nextNormal.set(next->position[1] - current->position[1],
225 current->position[0] - next->position[0]);
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000226 nextNormal.normalize();
227
228 // AA point offset from original point is that point's normal, such that each side is offset
229 // by .5 pixels
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000230 SkVector totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
231 totalOffset.fX *= 0.5f * inverseScaleX;
232 totalOffset.fY *= 0.5f * inverseScaleY;
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000233
234 AlphaVertex::set(&buffer[currentIndex++],
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000235 current->position[0] + totalOffset.fX,
236 current->position[1] + totalOffset.fY,
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000237 0.0f);
238 AlphaVertex::set(&buffer[currentIndex++],
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000239 current->position[0] - totalOffset.fX,
240 current->position[1] - totalOffset.fY,
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000241 1.0f);
242
243 last = current;
244 current = next;
245 lastNormal = nextNormal;
246 }
247
248 // wrap around to beginning
249 copyAlphaVertex(&buffer[currentIndex++], &buffer[0]);
250 copyAlphaVertex(&buffer[currentIndex++], &buffer[1]);
251
252 // zig zag between all previous points on the inside of the hull to create a
253 // triangle strip that fills the hull, repeating the first inner point to
254 // create degenerate tris to start inside path
255 int srcAindex = 0;
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000256 int srcBindex = perimeter.count() - 1;
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000257 while (srcAindex <= srcBindex) {
258 copyAlphaVertex(&buffer[currentIndex++], &buffer[srcAindex * 2 + 1]);
259 if (srcAindex == srcBindex) break;
260 copyAlphaVertex(&buffer[currentIndex++], &buffer[srcBindex * 2 + 1]);
261 srcAindex++;
262 srcBindex--;
263 }
264
265#if VERTEX_DEBUG
266 for (unsigned int i = 0; i < vertexBuffer.getSize(); i++) {
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000267 SkDebugf("point at %f %f, alpha %f", buffer[i].position[0], buffer[i].position[1], buffer[i].alpha);
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000268 }
269#endif
270}
271
272
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000273void getStrokeVerticesFromUnclosedVerticesAA(const SkTArray<Vertex, true>& vertices, float halfStrokeWidth,
274 VertexBuffer* vertexBuffer, float inverseScaleX, float inverseScaleY) {
275 AlphaVertex* buffer = vertexBuffer->alloc<AlphaVertex>(6 * vertices.count() + 2);
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000276
277 // avoid lines smaller than hairline since they break triangle based sampling. instead reducing
278 // alpha value (TODO: support different X/Y scale)
279 float maxAlpha = 1.0f;
280 if (halfStrokeWidth != 0 && inverseScaleX == inverseScaleY &&
281 halfStrokeWidth * inverseScaleX < 0.5f) {
282 maxAlpha *= (2 * halfStrokeWidth) / inverseScaleX;
283 halfStrokeWidth = 0.0f;
284 }
285
286 // there is no outer/inner here, using them for consistency with below approach
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000287 int offset = 2 * (vertices.count() - 2);
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000288 int currentAAOuterIndex = 2;
289 int currentAAInnerIndex = 2 * offset + 5; // reversed
290 int currentStrokeIndex = currentAAInnerIndex + 7;
291
292 const Vertex* last = &(vertices[0]);
293 const Vertex* current = &(vertices[1]);
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000294 SkVector lastNormal;
295 lastNormal.set(current->position[1] - last->position[1],
296 last->position[0] - current->position[0]);
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000297 lastNormal.normalize();
298
299 {
300 // start cap
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000301 SkVector totalOffset = lastNormal;
302 SkVector AAOffset = totalOffset;
303 AAOffset.fX *= 0.5f * inverseScaleX;
304 AAOffset.fY *= 0.5f * inverseScaleY;
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000305
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000306 SkVector innerOffset = totalOffset;
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000307 scaleOffsetForStrokeWidth(innerOffset, halfStrokeWidth, inverseScaleX, inverseScaleY);
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000308 SkVector outerOffset = innerOffset + AAOffset;
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000309 innerOffset -= AAOffset;
310
311 // TODO: support square cap by changing this offset to incorporate halfStrokeWidth
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000312 SkVector capAAOffset;
313 capAAOffset.set(AAOffset.fY, -AAOffset.fX);
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000314 AlphaVertex::set(&buffer[0],
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000315 last->position[0] + outerOffset.fX + capAAOffset.fX,
316 last->position[1] + outerOffset.fY + capAAOffset.fY,
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000317 0.0f);
318 AlphaVertex::set(&buffer[1],
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000319 last->position[0] + innerOffset.fX - capAAOffset.fX,
320 last->position[1] + innerOffset.fY - capAAOffset.fY,
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000321 maxAlpha);
322
323 AlphaVertex::set(&buffer[2 * offset + 6],
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000324 last->position[0] - outerOffset.fX + capAAOffset.fX,
325 last->position[1] - outerOffset.fY + capAAOffset.fY,
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000326 0.0f);
327 AlphaVertex::set(&buffer[2 * offset + 7],
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000328 last->position[0] - innerOffset.fX - capAAOffset.fX,
329 last->position[1] - innerOffset.fY - capAAOffset.fY,
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000330 maxAlpha);
331 copyAlphaVertex(&buffer[2 * offset + 8], &buffer[0]);
332 copyAlphaVertex(&buffer[2 * offset + 9], &buffer[1]);
333 copyAlphaVertex(&buffer[2 * offset + 10], &buffer[1]); // degenerate tris (the only two!)
334 copyAlphaVertex(&buffer[2 * offset + 11], &buffer[2 * offset + 7]);
335 }
336
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000337 for (int i = 1; i < vertices.count() - 1; i++) {
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000338 const Vertex* next = &(vertices[i + 1]);
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000339 SkVector nextNormal;
340 nextNormal.set(next->position[1] - current->position[1],
341 current->position[0] - next->position[0]);
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000342 nextNormal.normalize();
343
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000344 SkVector totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
345 SkVector AAOffset = totalOffset;
346 AAOffset.fX *= 0.5f * inverseScaleX;
347 AAOffset.fY *= 0.5f * inverseScaleY;
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000348
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000349 SkVector innerOffset = totalOffset;
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000350 scaleOffsetForStrokeWidth(innerOffset, halfStrokeWidth, inverseScaleX, inverseScaleY);
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000351 SkVector outerOffset = innerOffset + AAOffset;
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000352 innerOffset -= AAOffset;
353
354 AlphaVertex::set(&buffer[currentAAOuterIndex++],
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000355 current->position[0] + outerOffset.fX,
356 current->position[1] + outerOffset.fY,
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000357 0.0f);
358 AlphaVertex::set(&buffer[currentAAOuterIndex++],
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000359 current->position[0] + innerOffset.fX,
360 current->position[1] + innerOffset.fY,
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000361 maxAlpha);
362
363 AlphaVertex::set(&buffer[currentStrokeIndex++],
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000364 current->position[0] + innerOffset.fX,
365 current->position[1] + innerOffset.fY,
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000366 maxAlpha);
367 AlphaVertex::set(&buffer[currentStrokeIndex++],
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000368 current->position[0] - innerOffset.fX,
369 current->position[1] - innerOffset.fY,
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000370 maxAlpha);
371
372 AlphaVertex::set(&buffer[currentAAInnerIndex--],
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000373 current->position[0] - innerOffset.fX,
374 current->position[1] - innerOffset.fY,
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000375 maxAlpha);
376 AlphaVertex::set(&buffer[currentAAInnerIndex--],
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000377 current->position[0] - outerOffset.fX,
378 current->position[1] - outerOffset.fY,
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000379 0.0f);
380
381 last = current;
382 current = next;
383 lastNormal = nextNormal;
384 }
385
386 {
387 // end cap
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000388 SkVector totalOffset = lastNormal;
389 SkVector AAOffset = totalOffset;
390 AAOffset.fX *= 0.5f * inverseScaleX;
391 AAOffset.fY *= 0.5f * inverseScaleY;
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000392
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000393 SkVector innerOffset = totalOffset;
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000394 scaleOffsetForStrokeWidth(innerOffset, halfStrokeWidth, inverseScaleX, inverseScaleY);
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000395 SkVector outerOffset = innerOffset + AAOffset;
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000396 innerOffset -= AAOffset;
397
398 // TODO: support square cap by changing this offset to incorporate halfStrokeWidth
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000399 SkVector capAAOffset;
400 capAAOffset.set(-AAOffset.fY, AAOffset.fX);
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000401
402 AlphaVertex::set(&buffer[offset + 2],
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000403 current->position[0] + outerOffset.fX + capAAOffset.fX,
404 current->position[1] + outerOffset.fY + capAAOffset.fY,
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000405 0.0f);
406 AlphaVertex::set(&buffer[offset + 3],
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000407 current->position[0] + innerOffset.fX - capAAOffset.fX,
408 current->position[1] + innerOffset.fY - capAAOffset.fY,
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000409 maxAlpha);
410
411 AlphaVertex::set(&buffer[offset + 4],
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000412 current->position[0] - outerOffset.fX + capAAOffset.fX,
413 current->position[1] - outerOffset.fY + capAAOffset.fY,
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000414 0.0f);
415 AlphaVertex::set(&buffer[offset + 5],
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000416 current->position[0] - innerOffset.fX - capAAOffset.fX,
417 current->position[1] - innerOffset.fY - capAAOffset.fY,
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000418 maxAlpha);
419
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000420 copyAlphaVertex(&buffer[vertexBuffer->getSize() - 2], &buffer[offset + 3]);
421 copyAlphaVertex(&buffer[vertexBuffer->getSize() - 1], &buffer[offset + 5]);
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000422 }
423
424#if VERTEX_DEBUG
425 for (unsigned int i = 0; i < vertexBuffer.getSize(); i++) {
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000426 SkDebugf("point at %f %f, alpha %f", buffer[i].position[0], buffer[i].position[1], buffer[i].alpha);
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000427 }
428#endif
429}
430
431
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000432void getStrokeVerticesFromPerimeterAA(const SkTArray<Vertex, true>& perimeter, float halfStrokeWidth,
433 VertexBuffer* vertexBuffer, float inverseScaleX, float inverseScaleY) {
434 AlphaVertex* buffer = vertexBuffer->alloc<AlphaVertex>(6 * perimeter.count() + 8);
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000435
436 // avoid lines smaller than hairline since they break triangle based sampling. instead reducing
437 // alpha value (TODO: support different X/Y scale)
438 float maxAlpha = 1.0f;
439 if (halfStrokeWidth != 0 && inverseScaleX == inverseScaleY &&
440 halfStrokeWidth * inverseScaleX < 0.5f) {
441 maxAlpha *= (2 * halfStrokeWidth) / inverseScaleX;
442 halfStrokeWidth = 0.0f;
443 }
444
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000445 int offset = 2 * perimeter.count() + 3;
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000446 int currentAAOuterIndex = 0;
447 int currentStrokeIndex = offset;
448 int currentAAInnerIndex = offset * 2;
449
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000450 const Vertex* last = &(perimeter[perimeter.count() - 1]);
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000451 const Vertex* current = &(perimeter[0]);
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000452 SkVector lastNormal;
453 lastNormal.set(current->position[1] - last->position[1],
454 last->position[0] - current->position[0]);
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000455 lastNormal.normalize();
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000456 for (int i = 0; i < perimeter.count(); i++) {
457 const Vertex* next = &(perimeter[i + 1 >= perimeter.count() ? 0 : i + 1]);
458 SkVector nextNormal;
459 nextNormal.set(next->position[1] - current->position[1],
460 current->position[0] - next->position[0]);
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000461 nextNormal.normalize();
462
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000463 SkVector totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
464 SkVector AAOffset = totalOffset;
465 AAOffset.fX *= 0.5f * inverseScaleX;
466 AAOffset.fY *= 0.5f * inverseScaleY;
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000467
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000468 SkVector innerOffset = totalOffset;
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000469 scaleOffsetForStrokeWidth(innerOffset, halfStrokeWidth, inverseScaleX, inverseScaleY);
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000470 SkVector outerOffset = innerOffset + AAOffset;
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000471 innerOffset -= AAOffset;
472
473 AlphaVertex::set(&buffer[currentAAOuterIndex++],
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000474 current->position[0] + outerOffset.fX,
475 current->position[1] + outerOffset.fY,
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000476 0.0f);
477 AlphaVertex::set(&buffer[currentAAOuterIndex++],
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000478 current->position[0] + innerOffset.fX,
479 current->position[1] + innerOffset.fY,
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000480 maxAlpha);
481
482 AlphaVertex::set(&buffer[currentStrokeIndex++],
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000483 current->position[0] + innerOffset.fX,
484 current->position[1] + innerOffset.fY,
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000485 maxAlpha);
486 AlphaVertex::set(&buffer[currentStrokeIndex++],
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000487 current->position[0] - innerOffset.fX,
488 current->position[1] - innerOffset.fY,
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000489 maxAlpha);
490
491 AlphaVertex::set(&buffer[currentAAInnerIndex++],
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000492 current->position[0] - innerOffset.fX,
493 current->position[1] - innerOffset.fY,
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000494 maxAlpha);
495 AlphaVertex::set(&buffer[currentAAInnerIndex++],
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000496 current->position[0] - outerOffset.fX,
497 current->position[1] - outerOffset.fY,
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000498 0.0f);
499
500 last = current;
501 current = next;
502 lastNormal = nextNormal;
503 }
504
505 // wrap each strip around to beginning, creating degenerate tris to bridge strips
506 copyAlphaVertex(&buffer[currentAAOuterIndex++], &buffer[0]);
507 copyAlphaVertex(&buffer[currentAAOuterIndex++], &buffer[1]);
508 copyAlphaVertex(&buffer[currentAAOuterIndex++], &buffer[1]);
509
510 copyAlphaVertex(&buffer[currentStrokeIndex++], &buffer[offset]);
511 copyAlphaVertex(&buffer[currentStrokeIndex++], &buffer[offset + 1]);
512 copyAlphaVertex(&buffer[currentStrokeIndex++], &buffer[offset + 1]);
513
514 copyAlphaVertex(&buffer[currentAAInnerIndex++], &buffer[2 * offset]);
515 copyAlphaVertex(&buffer[currentAAInnerIndex++], &buffer[2 * offset + 1]);
516 // don't need to create last degenerate tri
517
518#if VERTEX_DEBUG
519 for (unsigned int i = 0; i < vertexBuffer.getSize(); i++) {
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000520 SkDebugf("point at %f %f, alpha %f", buffer[i].position[0], buffer[i].position[1], buffer[i].alpha);
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000521 }
522#endif
523}
524
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000525void PathRenderer::ConvexPathVertices(const SkPath &path, const SkPaint* paint,
526 const SkMatrix* transform, VertexBuffer* vertexBuffer) {
527 SK_TRACE_EVENT0("PathRenderer::convexPathVertices");
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000528
529 SkPaint::Style style = paint->getStyle();
530 bool isAA = paint->isAntiAlias();
531
532 float inverseScaleX, inverseScaleY;
533 computeInverseScales(transform, inverseScaleX, inverseScaleY);
534
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000535 SkTArray<Vertex, true> tempVertices;
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000536 float threshInvScaleX = inverseScaleX;
537 float threshInvScaleY = inverseScaleY;
538 if (style == SkPaint::kStroke_Style) {
539 // alter the bezier recursion threshold values we calculate in order to compensate for
540 // expansion done after the path vertices are found
541 SkRect bounds = path.getBounds();
542 if (!bounds.isEmpty()) {
543 threshInvScaleX *= bounds.width() / (bounds.width() + paint->getStrokeWidth());
544 threshInvScaleY *= bounds.height() / (bounds.height() + paint->getStrokeWidth());
545 }
546 }
547
548 // force close if we're filling the path, since fill path expects closed perimeter.
549 bool forceClose = style != SkPaint::kStroke_Style;
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000550 bool wasClosed = ConvexPathPerimeterVertices(path, forceClose, threshInvScaleX * threshInvScaleX,
551 threshInvScaleY * threshInvScaleY, &tempVertices);
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000552
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000553 if (!tempVertices.count()) {
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000554 // path was empty, return without allocating vertex buffer
555 return;
556 }
557
558#if VERTEX_DEBUG
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000559 for (unsigned int i = 0; i < tempVertices.count(); i++) {
560 SkDebugf("orig path: point at %f %f", tempVertices[i].position[0], tempVertices[i].position[1]);
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000561 }
562#endif
563
564 if (style == SkPaint::kStroke_Style) {
565 float halfStrokeWidth = paint->getStrokeWidth() * 0.5f;
566 if (!isAA) {
567 if (wasClosed) {
568 getStrokeVerticesFromPerimeter(tempVertices, halfStrokeWidth, vertexBuffer,
569 inverseScaleX, inverseScaleY);
570 } else {
571 getStrokeVerticesFromUnclosedVertices(tempVertices, halfStrokeWidth, vertexBuffer,
572 inverseScaleX, inverseScaleY);
573 }
574
575 } else {
576 if (wasClosed) {
577 getStrokeVerticesFromPerimeterAA(tempVertices, halfStrokeWidth, vertexBuffer,
578 inverseScaleX, inverseScaleY);
579 } else {
580 getStrokeVerticesFromUnclosedVerticesAA(tempVertices, halfStrokeWidth, vertexBuffer,
581 inverseScaleX, inverseScaleY);
582 }
583 }
584 } else {
585 // For kStrokeAndFill style, the path should be adjusted externally, as it will be treated as a fill here.
586 if (!isAA) {
587 getFillVerticesFromPerimeter(tempVertices, vertexBuffer);
588 } else {
589 getFillVerticesFromPerimeterAA(tempVertices, vertexBuffer, inverseScaleX, inverseScaleY);
590 }
591 }
592}
593
594
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000595void pushToVector(SkTArray<Vertex, true>* vertices, float x, float y) {
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000596 // TODO: make this not yuck
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000597 vertices->push_back();
598 Vertex* newVertex = &((*vertices)[vertices->count() - 1]);
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000599 Vertex::set(newVertex, x, y);
600}
601
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000602bool PathRenderer::ConvexPathPerimeterVertices(const SkPath& path, bool forceClose,
603 float sqrInvScaleX, float sqrInvScaleY, SkTArray<Vertex, true>* outputVertices) {
604 SK_TRACE_EVENT0("PathRenderer::convexPathPerimeterVertices");
605
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000606
607 // TODO: to support joins other than sharp miter, join vertices should be labelled in the
608 // perimeter, or resolved into more vertices. Reconsider forceClose-ing in that case.
609 SkPath::Iter iter(path, forceClose);
610 SkPoint pts[4];
611 SkPath::Verb v;
612 Vertex* newVertex = 0;
613 while (SkPath::kDone_Verb != (v = iter.next(pts))) {
614 switch (v) {
615 case SkPath::kMove_Verb:
616 pushToVector(outputVertices, pts[0].x(), pts[0].y());
617 ALOGV("Move to pos %f %f", pts[0].x(), pts[0].y());
618 break;
619 case SkPath::kClose_Verb:
620 ALOGV("Close at pos %f %f", pts[0].x(), pts[0].y());
621 break;
622 case SkPath::kLine_Verb:
623 ALOGV("kLine_Verb %f %f -> %f %f",
624 pts[0].x(), pts[0].y(),
625 pts[1].x(), pts[1].y());
626
627 pushToVector(outputVertices, pts[1].x(), pts[1].y());
628 break;
629 case SkPath::kQuad_Verb:
630 ALOGV("kQuad_Verb");
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000631 RecursiveQuadraticBezierVertices(
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000632 pts[0].x(), pts[0].y(),
633 pts[2].x(), pts[2].y(),
634 pts[1].x(), pts[1].y(),
635 sqrInvScaleX, sqrInvScaleY, outputVertices);
636 break;
637 case SkPath::kCubic_Verb:
638 ALOGV("kCubic_Verb");
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000639 RecursiveCubicBezierVertices(
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000640 pts[0].x(), pts[0].y(),
641 pts[1].x(), pts[1].y(),
642 pts[3].x(), pts[3].y(),
643 pts[2].x(), pts[2].y(),
644 sqrInvScaleX, sqrInvScaleY, outputVertices);
645 break;
646 default:
647 break;
648 }
649 }
650
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000651 int size = outputVertices->count();
652 if (size >= 2 && (*outputVertices)[0].position[0] == (*outputVertices)[size - 1].position[0] &&
653 (*outputVertices)[0].position[1] == (*outputVertices)[size - 1].position[1]) {
654 outputVertices->pop_back();
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000655 return true;
656 }
657 return false;
658}
659
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000660void PathRenderer::RecursiveCubicBezierVertices(
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000661 float p1x, float p1y, float c1x, float c1y,
662 float p2x, float p2y, float c2x, float c2y,
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000663 float sqrInvScaleX, float sqrInvScaleY, SkTArray<Vertex, true>* outputVertices) {
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000664 float dx = p2x - p1x;
665 float dy = p2y - p1y;
666 float d1 = fabs((c1x - p2x) * dy - (c1y - p2y) * dx);
667 float d2 = fabs((c2x - p2x) * dy - (c2y - p2y) * dx);
668 float d = d1 + d2;
669
670 // multiplying by sqrInvScaleY/X equivalent to multiplying in dimensional scale factors
671
672 if (d * d < THRESHOLD * THRESHOLD * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) {
673 // below thresh, draw line by adding endpoint
674 pushToVector(outputVertices, p2x, p2y);
675 } else {
676 float p1c1x = (p1x + c1x) * 0.5f;
677 float p1c1y = (p1y + c1y) * 0.5f;
678 float p2c2x = (p2x + c2x) * 0.5f;
679 float p2c2y = (p2y + c2y) * 0.5f;
680
681 float c1c2x = (c1x + c2x) * 0.5f;
682 float c1c2y = (c1y + c2y) * 0.5f;
683
684 float p1c1c2x = (p1c1x + c1c2x) * 0.5f;
685 float p1c1c2y = (p1c1y + c1c2y) * 0.5f;
686
687 float p2c1c2x = (p2c2x + c1c2x) * 0.5f;
688 float p2c1c2y = (p2c2y + c1c2y) * 0.5f;
689
690 float mx = (p1c1c2x + p2c1c2x) * 0.5f;
691 float my = (p1c1c2y + p2c1c2y) * 0.5f;
692
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000693 RecursiveCubicBezierVertices(
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000694 p1x, p1y, p1c1x, p1c1y,
695 mx, my, p1c1c2x, p1c1c2y,
696 sqrInvScaleX, sqrInvScaleY, outputVertices);
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000697 RecursiveCubicBezierVertices(
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000698 mx, my, p2c1c2x, p2c1c2y,
699 p2x, p2y, p2c2x, p2c2y,
700 sqrInvScaleX, sqrInvScaleY, outputVertices);
701 }
702}
703
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000704void PathRenderer::RecursiveQuadraticBezierVertices(
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000705 float ax, float ay,
706 float bx, float by,
707 float cx, float cy,
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000708 float sqrInvScaleX, float sqrInvScaleY, SkTArray<Vertex, true>* outputVertices) {
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000709 float dx = bx - ax;
710 float dy = by - ay;
711 float d = (cx - bx) * dy - (cy - by) * dx;
712
713 if (d * d < THRESHOLD * THRESHOLD * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) {
714 // below thresh, draw line by adding endpoint
715 pushToVector(outputVertices, bx, by);
716 } else {
717 float acx = (ax + cx) * 0.5f;
718 float bcx = (bx + cx) * 0.5f;
719 float acy = (ay + cy) * 0.5f;
720 float bcy = (by + cy) * 0.5f;
721
722 // midpoint
723 float mx = (acx + bcx) * 0.5f;
724 float my = (acy + bcy) * 0.5f;
725
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000726 RecursiveQuadraticBezierVertices(ax, ay, mx, my, acx, acy,
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000727 sqrInvScaleX, sqrInvScaleY, outputVertices);
jvanverth@google.com24b4f972012-12-18 14:13:46 +0000728 RecursiveQuadraticBezierVertices(mx, my, bx, by, bcx, bcy,
jvanverth@google.com8ffb7042012-12-13 19:53:18 +0000729 sqrInvScaleX, sqrInvScaleY, outputVertices);
730 }
731}
732
733}; // namespace uirenderer
734}; // namespace android