blob: dd13d79625776213b0b1d853daf8e8cbed031d57 [file] [log] [blame]
Chris Craik710f46d2012-09-17 17:25:49 -07001/*
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
17#define LOG_TAG "PathRenderer"
18#define LOG_NDEBUG 1
19#define ATRACE_TAG ATRACE_TAG_GRAPHICS
20
21#define VERTEX_DEBUG 0
22
23#include <SkPath.h>
Chris Craikcb4d6002012-09-25 12:00:29 -070024#include <SkPaint.h>
Chris Craik710f46d2012-09-17 17:25:49 -070025
26#include <stdlib.h>
27#include <stdint.h>
28#include <sys/types.h>
29
30#include <utils/Log.h>
31#include <utils/Trace.h>
32
33#include "PathRenderer.h"
34#include "Matrix.h"
35#include "Vector.h"
36#include "Vertex.h"
37
38namespace android {
39namespace uirenderer {
40
41#define THRESHOLD 0.5f
42
Chris Craikcb4d6002012-09-25 12:00:29 -070043SkRect PathRenderer::computePathBounds(const SkPath& path, const SkPaint* paint) {
44 SkRect bounds = path.getBounds();
45 if (paint->getStyle() != SkPaint::kFill_Style) {
46 float outset = paint->getStrokeWidth() * 0.5f;
47 bounds.outset(outset, outset);
48 }
49 return bounds;
50}
51
52void computeInverseScales(const mat4 *transform, float &inverseScaleX, float& inverseScaleY) {
Chris Craik710f46d2012-09-17 17:25:49 -070053 if (CC_UNLIKELY(!transform->isPureTranslate())) {
54 float m00 = transform->data[Matrix4::kScaleX];
55 float m01 = transform->data[Matrix4::kSkewY];
56 float m10 = transform->data[Matrix4::kSkewX];
57 float m11 = transform->data[Matrix4::kScaleY];
58 float scaleX = sqrt(m00 * m00 + m01 * m01);
59 float scaleY = sqrt(m10 * m10 + m11 * m11);
Chris Craik16b897c2012-09-27 13:10:56 -070060 inverseScaleX = (scaleX != 0) ? (1.0f / scaleX) : 1.0f;
61 inverseScaleY = (scaleY != 0) ? (1.0f / scaleY) : 1.0f;
Chris Craikcb4d6002012-09-25 12:00:29 -070062 } else {
63 inverseScaleX = 1.0f;
64 inverseScaleY = 1.0f;
Chris Craik710f46d2012-09-17 17:25:49 -070065 }
66}
67
Chris Craik16b897c2012-09-27 13:10:56 -070068inline void copyVertex(Vertex* destPtr, const Vertex* srcPtr) {
Chris Craikcb4d6002012-09-25 12:00:29 -070069 Vertex::set(destPtr, srcPtr->position[0], srcPtr->position[1]);
70}
Chris Craik710f46d2012-09-17 17:25:49 -070071
Chris Craik16b897c2012-09-27 13:10:56 -070072inline void copyAlphaVertex(AlphaVertex* destPtr, const AlphaVertex* srcPtr) {
Chris Craikcb4d6002012-09-25 12:00:29 -070073 AlphaVertex::set(destPtr, srcPtr->position[0], srcPtr->position[1], srcPtr->alpha);
74}
Chris Craik710f46d2012-09-17 17:25:49 -070075
Chris Craik16b897c2012-09-27 13:10:56 -070076/**
77 * Produces a pseudo-normal for a vertex, given the normals of the two incoming lines. If the offset
78 * from each vertex in a perimeter is calculated, the resultant lines connecting the offset vertices
79 * will be offset by 1.0
80 *
81 * Note that we can't add and normalize the two vectors, that would result in a rectangle having an
82 * offset of (sqrt(2)/2, sqrt(2)/2) at each corner, instead of (1, 1)
Chris Craik780c1282012-10-04 14:10:49 -070083 *
84 * NOTE: assumes angles between normals 90 degrees or less
Chris Craik16b897c2012-09-27 13:10:56 -070085 */
86inline vec2 totalOffsetFromNormals(const vec2& normalA, const vec2& normalB) {
87 return (normalA + normalB) / (1 + fabs(normalA.dot(normalB)));
88}
89
Chris Craik780c1282012-10-04 14:10:49 -070090inline void scaleOffsetForStrokeWidth(vec2& offset, float halfStrokeWidth,
91 float inverseScaleX, float inverseScaleY) {
92 if (halfStrokeWidth == 0.0f) {
93 // hairline - compensate for scale
94 offset.x *= 0.5f * inverseScaleX;
95 offset.y *= 0.5f * inverseScaleY;
96 } else {
97 offset *= halfStrokeWidth;
98 }
99}
100
Chris Craikcb4d6002012-09-25 12:00:29 -0700101void getFillVerticesFromPerimeter(const Vector<Vertex>& perimeter, VertexBuffer& vertexBuffer) {
102 Vertex* buffer = vertexBuffer.alloc<Vertex>(perimeter.size());
103
Chris Craik710f46d2012-09-17 17:25:49 -0700104 int currentIndex = 0;
Chris Craikcb4d6002012-09-25 12:00:29 -0700105 // zig zag between all previous points on the inside of the hull to create a
106 // triangle strip that fills the hull
107 int srcAindex = 0;
108 int srcBindex = perimeter.size() - 1;
109 while (srcAindex <= srcBindex) {
110 copyVertex(&buffer[currentIndex++], &perimeter[srcAindex]);
111 if (srcAindex == srcBindex) break;
112 copyVertex(&buffer[currentIndex++], &perimeter[srcBindex]);
113 srcAindex++;
114 srcBindex--;
Chris Craik710f46d2012-09-17 17:25:49 -0700115 }
Chris Craikcb4d6002012-09-25 12:00:29 -0700116}
117
118void getStrokeVerticesFromPerimeter(const Vector<Vertex>& perimeter, float halfStrokeWidth,
119 VertexBuffer& vertexBuffer, float inverseScaleX, float inverseScaleY) {
120 Vertex* buffer = vertexBuffer.alloc<Vertex>(perimeter.size() * 2 + 2);
121
122 int currentIndex = 0;
123 const Vertex* last = &(perimeter[perimeter.size() - 1]);
124 const Vertex* current = &(perimeter[0]);
125 vec2 lastNormal(current->position[1] - last->position[1],
126 last->position[0] - current->position[0]);
127 lastNormal.normalize();
128 for (unsigned int i = 0; i < perimeter.size(); i++) {
129 const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]);
130 vec2 nextNormal(next->position[1] - current->position[1],
131 current->position[0] - next->position[0]);
132 nextNormal.normalize();
133
Chris Craik16b897c2012-09-27 13:10:56 -0700134 vec2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
Chris Craik780c1282012-10-04 14:10:49 -0700135 scaleOffsetForStrokeWidth(totalOffset, halfStrokeWidth, inverseScaleX, inverseScaleY);
Chris Craikcb4d6002012-09-25 12:00:29 -0700136
137 Vertex::set(&buffer[currentIndex++],
138 current->position[0] + totalOffset.x,
139 current->position[1] + totalOffset.y);
140
141 Vertex::set(&buffer[currentIndex++],
142 current->position[0] - totalOffset.x,
143 current->position[1] - totalOffset.y);
144
145 last = current;
146 current = next;
147 lastNormal = nextNormal;
148 }
149
150 // wrap around to beginning
151 copyVertex(&buffer[currentIndex++], &buffer[0]);
152 copyVertex(&buffer[currentIndex++], &buffer[1]);
153}
154
Chris Craik780c1282012-10-04 14:10:49 -0700155void getStrokeVerticesFromUnclosedVertices(const Vector<Vertex>& vertices, float halfStrokeWidth,
156 VertexBuffer& vertexBuffer, float inverseScaleX, float inverseScaleY) {
157 Vertex* buffer = vertexBuffer.alloc<Vertex>(vertices.size() * 2);
158
159 int currentIndex = 0;
160 const Vertex* current = &(vertices[0]);
161 vec2 lastNormal;
162 for (unsigned int i = 0; i < vertices.size() - 1; i++) {
163 const Vertex* next = &(vertices[i + 1]);
164 vec2 nextNormal(next->position[1] - current->position[1],
165 current->position[0] - next->position[0]);
166 nextNormal.normalize();
167
168 vec2 totalOffset;
169 if (i == 0) {
170 totalOffset = nextNormal;
171 } else {
172 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
173 }
174 scaleOffsetForStrokeWidth(totalOffset, halfStrokeWidth, inverseScaleX, inverseScaleY);
175
176 Vertex::set(&buffer[currentIndex++],
177 current->position[0] + totalOffset.x,
178 current->position[1] + totalOffset.y);
179
180 Vertex::set(&buffer[currentIndex++],
181 current->position[0] - totalOffset.x,
182 current->position[1] - totalOffset.y);
183
184 current = next;
185 lastNormal = nextNormal;
186 }
187
188 vec2 totalOffset = lastNormal;
189 scaleOffsetForStrokeWidth(totalOffset, halfStrokeWidth, inverseScaleX, inverseScaleY);
190
191 Vertex::set(&buffer[currentIndex++],
192 current->position[0] + totalOffset.x,
193 current->position[1] + totalOffset.y);
194 Vertex::set(&buffer[currentIndex++],
195 current->position[0] - totalOffset.x,
196 current->position[1] - totalOffset.y);
197#if VERTEX_DEBUG
198 for (unsigned int i = 0; i < vertexBuffer.getSize(); i++) {
199 ALOGD("point at %f %f", buffer[i].position[0], buffer[i].position[1]);
200 }
201#endif
202}
203
Chris Craikcb4d6002012-09-25 12:00:29 -0700204void getFillVerticesFromPerimeterAA(const Vector<Vertex>& perimeter, VertexBuffer& vertexBuffer,
205 float inverseScaleX, float inverseScaleY) {
206 AlphaVertex* buffer = vertexBuffer.alloc<AlphaVertex>(perimeter.size() * 3 + 2);
Chris Craik710f46d2012-09-17 17:25:49 -0700207
208 // generate alpha points - fill Alpha vertex gaps in between each point with
209 // alpha 0 vertex, offset by a scaled normal.
Chris Craikcb4d6002012-09-25 12:00:29 -0700210 int currentIndex = 0;
211 const Vertex* last = &(perimeter[perimeter.size() - 1]);
212 const Vertex* current = &(perimeter[0]);
213 vec2 lastNormal(current->position[1] - last->position[1],
214 last->position[0] - current->position[0]);
215 lastNormal.normalize();
216 for (unsigned int i = 0; i < perimeter.size(); i++) {
217 const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]);
Chris Craik710f46d2012-09-17 17:25:49 -0700218 vec2 nextNormal(next->position[1] - current->position[1],
Chris Craikcb4d6002012-09-25 12:00:29 -0700219 current->position[0] - next->position[0]);
Chris Craik710f46d2012-09-17 17:25:49 -0700220 nextNormal.normalize();
221
Chris Craik16b897c2012-09-27 13:10:56 -0700222 // AA point offset from original point is that point's normal, such that each side is offset
223 // by .5 pixels
224 vec2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
225 totalOffset.x *= 0.5f * inverseScaleX;
226 totalOffset.y *= 0.5f * inverseScaleY;
Chris Craik710f46d2012-09-17 17:25:49 -0700227
228 AlphaVertex::set(&buffer[currentIndex++],
Chris Craikcb4d6002012-09-25 12:00:29 -0700229 current->position[0] + totalOffset.x,
230 current->position[1] + totalOffset.y,
231 0.0f);
Chris Craik710f46d2012-09-17 17:25:49 -0700232 AlphaVertex::set(&buffer[currentIndex++],
Chris Craikcb4d6002012-09-25 12:00:29 -0700233 current->position[0] - totalOffset.x,
234 current->position[1] - totalOffset.y,
235 1.0f);
236
Chris Craik710f46d2012-09-17 17:25:49 -0700237 last = current;
Chris Craikcb4d6002012-09-25 12:00:29 -0700238 current = next;
239 lastNormal = nextNormal;
Chris Craik710f46d2012-09-17 17:25:49 -0700240 }
241
242 // wrap around to beginning
Chris Craikcb4d6002012-09-25 12:00:29 -0700243 copyAlphaVertex(&buffer[currentIndex++], &buffer[0]);
244 copyAlphaVertex(&buffer[currentIndex++], &buffer[1]);
Chris Craik710f46d2012-09-17 17:25:49 -0700245
246 // zig zag between all previous points on the inside of the hull to create a
247 // triangle strip that fills the hull, repeating the first inner point to
248 // create degenerate tris to start inside path
249 int srcAindex = 0;
Chris Craikcb4d6002012-09-25 12:00:29 -0700250 int srcBindex = perimeter.size() - 1;
Chris Craik710f46d2012-09-17 17:25:49 -0700251 while (srcAindex <= srcBindex) {
Chris Craikcb4d6002012-09-25 12:00:29 -0700252 copyAlphaVertex(&buffer[currentIndex++], &buffer[srcAindex * 2 + 1]);
Chris Craik710f46d2012-09-17 17:25:49 -0700253 if (srcAindex == srcBindex) break;
Chris Craikcb4d6002012-09-25 12:00:29 -0700254 copyAlphaVertex(&buffer[currentIndex++], &buffer[srcBindex * 2 + 1]);
Chris Craik710f46d2012-09-17 17:25:49 -0700255 srcAindex++;
256 srcBindex--;
257 }
258
259#if VERTEX_DEBUG
Chris Craikcb4d6002012-09-25 12:00:29 -0700260 for (unsigned int i = 0; i < vertexBuffer.getSize(); i++) {
Chris Craik780c1282012-10-04 14:10:49 -0700261 ALOGD("point at %f %f, alpha %f", buffer[i].position[0], buffer[i].position[1], buffer[i].alpha);
Chris Craik710f46d2012-09-17 17:25:49 -0700262 }
263#endif
264}
265
Chris Craik780c1282012-10-04 14:10:49 -0700266
267void getStrokeVerticesFromUnclosedVerticesAA(const Vector<Vertex>& vertices, float halfStrokeWidth,
268 VertexBuffer& vertexBuffer, float inverseScaleX, float inverseScaleY) {
269 AlphaVertex* buffer = vertexBuffer.alloc<AlphaVertex>(6 * vertices.size() + 2);
270
271 // avoid lines smaller than hairline since they break triangle based sampling. instead reducing
272 // alpha value (TODO: support different X/Y scale)
273 float maxAlpha = 1.0f;
274 if (halfStrokeWidth != 0 && inverseScaleX == inverseScaleY &&
275 halfStrokeWidth * inverseScaleX < 0.5f) {
276 maxAlpha *= (2 * halfStrokeWidth) / inverseScaleX;
277 halfStrokeWidth = 0.0f;
278 }
279
280 // there is no outer/inner here, using them for consistency with below approach
281 int offset = 2 * (vertices.size() - 2);
282 int currentAAOuterIndex = 2;
283 int currentAAInnerIndex = 2 * offset + 5; // reversed
284 int currentStrokeIndex = currentAAInnerIndex + 7;
285
286 const Vertex* last = &(vertices[0]);
287 const Vertex* current = &(vertices[1]);
288 vec2 lastNormal(current->position[1] - last->position[1],
289 last->position[0] - current->position[0]);
290 lastNormal.normalize();
291
292 {
293 // start cap
294 vec2 totalOffset = lastNormal;
295 vec2 AAOffset = totalOffset;
296 AAOffset.x *= 0.5f * inverseScaleX;
297 AAOffset.y *= 0.5f * inverseScaleY;
298
299 vec2 innerOffset = totalOffset;
300 scaleOffsetForStrokeWidth(innerOffset, halfStrokeWidth, inverseScaleX, inverseScaleY);
301 vec2 outerOffset = innerOffset + AAOffset;
302 innerOffset -= AAOffset;
303
304 // TODO: support square cap by changing this offset to incorporate halfStrokeWidth
305 vec2 capAAOffset(AAOffset.y, -AAOffset.x);
306 AlphaVertex::set(&buffer[0],
307 last->position[0] + outerOffset.x + capAAOffset.x,
308 last->position[1] + outerOffset.y + capAAOffset.y,
309 0.0f);
310 AlphaVertex::set(&buffer[1],
311 last->position[0] + innerOffset.x - capAAOffset.x,
312 last->position[1] + innerOffset.y - capAAOffset.y,
313 maxAlpha);
314
315 AlphaVertex::set(&buffer[2 * offset + 6],
316 last->position[0] - outerOffset.x + capAAOffset.x,
317 last->position[1] - outerOffset.y + capAAOffset.y,
318 0.0f);
319 AlphaVertex::set(&buffer[2 * offset + 7],
320 last->position[0] - innerOffset.x - capAAOffset.x,
321 last->position[1] - innerOffset.y - capAAOffset.y,
322 maxAlpha);
323 copyAlphaVertex(&buffer[2 * offset + 8], &buffer[0]);
324 copyAlphaVertex(&buffer[2 * offset + 9], &buffer[1]);
325 copyAlphaVertex(&buffer[2 * offset + 10], &buffer[1]); // degenerate tris (the only two!)
326 copyAlphaVertex(&buffer[2 * offset + 11], &buffer[2 * offset + 7]);
327 }
328
329 for (unsigned int i = 1; i < vertices.size() - 1; i++) {
330 const Vertex* next = &(vertices[i + 1]);
331 vec2 nextNormal(next->position[1] - current->position[1],
332 current->position[0] - next->position[0]);
333 nextNormal.normalize();
334
335 vec2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
336 vec2 AAOffset = totalOffset;
337 AAOffset.x *= 0.5f * inverseScaleX;
338 AAOffset.y *= 0.5f * inverseScaleY;
339
340 vec2 innerOffset = totalOffset;
341 scaleOffsetForStrokeWidth(innerOffset, halfStrokeWidth, inverseScaleX, inverseScaleY);
342 vec2 outerOffset = innerOffset + AAOffset;
343 innerOffset -= AAOffset;
344
345 AlphaVertex::set(&buffer[currentAAOuterIndex++],
346 current->position[0] + outerOffset.x,
347 current->position[1] + outerOffset.y,
348 0.0f);
349 AlphaVertex::set(&buffer[currentAAOuterIndex++],
350 current->position[0] + innerOffset.x,
351 current->position[1] + innerOffset.y,
352 maxAlpha);
353
354 AlphaVertex::set(&buffer[currentStrokeIndex++],
355 current->position[0] + innerOffset.x,
356 current->position[1] + innerOffset.y,
357 maxAlpha);
358 AlphaVertex::set(&buffer[currentStrokeIndex++],
359 current->position[0] - innerOffset.x,
360 current->position[1] - innerOffset.y,
361 maxAlpha);
362
363 AlphaVertex::set(&buffer[currentAAInnerIndex--],
364 current->position[0] - innerOffset.x,
365 current->position[1] - innerOffset.y,
366 maxAlpha);
367 AlphaVertex::set(&buffer[currentAAInnerIndex--],
368 current->position[0] - outerOffset.x,
369 current->position[1] - outerOffset.y,
370 0.0f);
371
372 last = current;
373 current = next;
374 lastNormal = nextNormal;
375 }
376
377 {
378 // end cap
379 vec2 totalOffset = lastNormal;
380 vec2 AAOffset = totalOffset;
381 AAOffset.x *= 0.5f * inverseScaleX;
382 AAOffset.y *= 0.5f * inverseScaleY;
383
384 vec2 innerOffset = totalOffset;
385 scaleOffsetForStrokeWidth(innerOffset, halfStrokeWidth, inverseScaleX, inverseScaleY);
386 vec2 outerOffset = innerOffset + AAOffset;
387 innerOffset -= AAOffset;
388
389 // TODO: support square cap by changing this offset to incorporate halfStrokeWidth
390 vec2 capAAOffset(-AAOffset.y, AAOffset.x);
391
392 AlphaVertex::set(&buffer[offset + 2],
393 current->position[0] + outerOffset.x + capAAOffset.x,
394 current->position[1] + outerOffset.y + capAAOffset.y,
395 0.0f);
396 AlphaVertex::set(&buffer[offset + 3],
397 current->position[0] + innerOffset.x - capAAOffset.x,
398 current->position[1] + innerOffset.y - capAAOffset.y,
399 maxAlpha);
400
401 AlphaVertex::set(&buffer[offset + 4],
402 current->position[0] - outerOffset.x + capAAOffset.x,
403 current->position[1] - outerOffset.y + capAAOffset.y,
404 0.0f);
405 AlphaVertex::set(&buffer[offset + 5],
406 current->position[0] - innerOffset.x - capAAOffset.x,
407 current->position[1] - innerOffset.y - capAAOffset.y,
408 maxAlpha);
409
410 copyAlphaVertex(&buffer[vertexBuffer.getSize() - 2], &buffer[offset + 3]);
411 copyAlphaVertex(&buffer[vertexBuffer.getSize() - 1], &buffer[offset + 5]);
412 }
413
414#if VERTEX_DEBUG
415 for (unsigned int i = 0; i < vertexBuffer.getSize(); i++) {
416 ALOGD("point at %f %f, alpha %f", buffer[i].position[0], buffer[i].position[1], buffer[i].alpha);
417 }
418#endif
419}
420
421
Chris Craikcb4d6002012-09-25 12:00:29 -0700422void getStrokeVerticesFromPerimeterAA(const Vector<Vertex>& perimeter, float halfStrokeWidth,
423 VertexBuffer& vertexBuffer, float inverseScaleX, float inverseScaleY) {
424 AlphaVertex* buffer = vertexBuffer.alloc<AlphaVertex>(6 * perimeter.size() + 8);
Chris Craik710f46d2012-09-17 17:25:49 -0700425
Chris Craik16b897c2012-09-27 13:10:56 -0700426 // avoid lines smaller than hairline since they break triangle based sampling. instead reducing
427 // alpha value (TODO: support different X/Y scale)
428 float maxAlpha = 1.0f;
429 if (halfStrokeWidth != 0 && inverseScaleX == inverseScaleY &&
Chris Craik2154af22012-10-12 11:31:37 -0700430 halfStrokeWidth * inverseScaleX < 0.5f) {
Chris Craik16b897c2012-09-27 13:10:56 -0700431 maxAlpha *= (2 * halfStrokeWidth) / inverseScaleX;
432 halfStrokeWidth = 0.0f;
433 }
434
Chris Craikcb4d6002012-09-25 12:00:29 -0700435 int offset = 2 * perimeter.size() + 3;
436 int currentAAOuterIndex = 0;
437 int currentStrokeIndex = offset;
438 int currentAAInnerIndex = offset * 2;
439
440 const Vertex* last = &(perimeter[perimeter.size() - 1]);
441 const Vertex* current = &(perimeter[0]);
442 vec2 lastNormal(current->position[1] - last->position[1],
443 last->position[0] - current->position[0]);
444 lastNormal.normalize();
445 for (unsigned int i = 0; i < perimeter.size(); i++) {
446 const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]);
447 vec2 nextNormal(next->position[1] - current->position[1],
448 current->position[0] - next->position[0]);
449 nextNormal.normalize();
450
Chris Craik16b897c2012-09-27 13:10:56 -0700451 vec2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
452 vec2 AAOffset = totalOffset;
453 AAOffset.x *= 0.5f * inverseScaleX;
454 AAOffset.y *= 0.5f * inverseScaleY;
Chris Craikcb4d6002012-09-25 12:00:29 -0700455
Chris Craik16b897c2012-09-27 13:10:56 -0700456 vec2 innerOffset = totalOffset;
Chris Craik780c1282012-10-04 14:10:49 -0700457 scaleOffsetForStrokeWidth(innerOffset, halfStrokeWidth, inverseScaleX, inverseScaleY);
Chris Craikcb4d6002012-09-25 12:00:29 -0700458 vec2 outerOffset = innerOffset + AAOffset;
459 innerOffset -= AAOffset;
460
461 AlphaVertex::set(&buffer[currentAAOuterIndex++],
462 current->position[0] + outerOffset.x,
463 current->position[1] + outerOffset.y,
464 0.0f);
465 AlphaVertex::set(&buffer[currentAAOuterIndex++],
466 current->position[0] + innerOffset.x,
467 current->position[1] + innerOffset.y,
Chris Craik16b897c2012-09-27 13:10:56 -0700468 maxAlpha);
Chris Craikcb4d6002012-09-25 12:00:29 -0700469
470 AlphaVertex::set(&buffer[currentStrokeIndex++],
471 current->position[0] + innerOffset.x,
472 current->position[1] + innerOffset.y,
Chris Craik16b897c2012-09-27 13:10:56 -0700473 maxAlpha);
Chris Craikcb4d6002012-09-25 12:00:29 -0700474 AlphaVertex::set(&buffer[currentStrokeIndex++],
475 current->position[0] - innerOffset.x,
476 current->position[1] - innerOffset.y,
Chris Craik16b897c2012-09-27 13:10:56 -0700477 maxAlpha);
Chris Craikcb4d6002012-09-25 12:00:29 -0700478
479 AlphaVertex::set(&buffer[currentAAInnerIndex++],
480 current->position[0] - innerOffset.x,
481 current->position[1] - innerOffset.y,
Chris Craik16b897c2012-09-27 13:10:56 -0700482 maxAlpha);
Chris Craikcb4d6002012-09-25 12:00:29 -0700483 AlphaVertex::set(&buffer[currentAAInnerIndex++],
484 current->position[0] - outerOffset.x,
485 current->position[1] - outerOffset.y,
486 0.0f);
487
Chris Craikcb4d6002012-09-25 12:00:29 -0700488 last = current;
489 current = next;
490 lastNormal = nextNormal;
491 }
492
493 // wrap each strip around to beginning, creating degenerate tris to bridge strips
494 copyAlphaVertex(&buffer[currentAAOuterIndex++], &buffer[0]);
495 copyAlphaVertex(&buffer[currentAAOuterIndex++], &buffer[1]);
496 copyAlphaVertex(&buffer[currentAAOuterIndex++], &buffer[1]);
497
498 copyAlphaVertex(&buffer[currentStrokeIndex++], &buffer[offset]);
499 copyAlphaVertex(&buffer[currentStrokeIndex++], &buffer[offset + 1]);
500 copyAlphaVertex(&buffer[currentStrokeIndex++], &buffer[offset + 1]);
501
502 copyAlphaVertex(&buffer[currentAAInnerIndex++], &buffer[2 * offset]);
503 copyAlphaVertex(&buffer[currentAAInnerIndex++], &buffer[2 * offset + 1]);
504 // don't need to create last degenerate tri
Chris Craik780c1282012-10-04 14:10:49 -0700505
506#if VERTEX_DEBUG
507 for (unsigned int i = 0; i < vertexBuffer.getSize(); i++) {
508 ALOGD("point at %f %f, alpha %f", buffer[i].position[0], buffer[i].position[1], buffer[i].alpha);
509 }
510#endif
Chris Craikcb4d6002012-09-25 12:00:29 -0700511}
512
513void PathRenderer::convexPathVertices(const SkPath &path, const SkPaint* paint,
514 const mat4 *transform, VertexBuffer& vertexBuffer) {
515 ATRACE_CALL();
516
517 SkPaint::Style style = paint->getStyle();
518 bool isAA = paint->isAntiAlias();
519
520 float inverseScaleX, inverseScaleY;
521 computeInverseScales(transform, inverseScaleX, inverseScaleY);
522
523 Vector<Vertex> tempVertices;
Chris Craik16b897c2012-09-27 13:10:56 -0700524 float threshInvScaleX = inverseScaleX;
525 float threshInvScaleY = inverseScaleY;
526 if (style == SkPaint::kStroke_Style) {
527 // alter the bezier recursion threshold values we calculate in order to compensate for
528 // expansion done after the path vertices are found
529 SkRect bounds = path.getBounds();
530 if (!bounds.isEmpty()) {
531 threshInvScaleX *= bounds.width() / (bounds.width() + paint->getStrokeWidth());
532 threshInvScaleY *= bounds.height() / (bounds.height() + paint->getStrokeWidth());
533 }
534 }
Chris Craik780c1282012-10-04 14:10:49 -0700535
536 // force close if we're filling the path, since fill path expects closed perimeter.
537 bool forceClose = style != SkPaint::kStroke_Style;
538 bool wasClosed = convexPathPerimeterVertices(path, forceClose, threshInvScaleX * threshInvScaleX,
Chris Craik16b897c2012-09-27 13:10:56 -0700539 threshInvScaleY * threshInvScaleY, tempVertices);
Chris Craikcb4d6002012-09-25 12:00:29 -0700540
Chris Craikbf09ffb2012-10-01 13:50:37 -0700541 if (!tempVertices.size()) {
542 // path was empty, return without allocating vertex buffer
543 return;
544 }
545
Chris Craikcb4d6002012-09-25 12:00:29 -0700546#if VERTEX_DEBUG
547 for (unsigned int i = 0; i < tempVertices.size(); i++) {
548 ALOGD("orig path: point at %f %f", tempVertices[i].position[0], tempVertices[i].position[1]);
549 }
550#endif
551
552 if (style == SkPaint::kStroke_Style) {
553 float halfStrokeWidth = paint->getStrokeWidth() * 0.5f;
554 if (!isAA) {
Chris Craik780c1282012-10-04 14:10:49 -0700555 if (wasClosed) {
556 getStrokeVerticesFromPerimeter(tempVertices, halfStrokeWidth, vertexBuffer,
557 inverseScaleX, inverseScaleY);
558 } else {
559 getStrokeVerticesFromUnclosedVertices(tempVertices, halfStrokeWidth, vertexBuffer,
560 inverseScaleX, inverseScaleY);
561 }
562
Chris Craikcb4d6002012-09-25 12:00:29 -0700563 } else {
Chris Craik780c1282012-10-04 14:10:49 -0700564 if (wasClosed) {
565 getStrokeVerticesFromPerimeterAA(tempVertices, halfStrokeWidth, vertexBuffer,
566 inverseScaleX, inverseScaleY);
567 } else {
568 getStrokeVerticesFromUnclosedVerticesAA(tempVertices, halfStrokeWidth, vertexBuffer,
569 inverseScaleX, inverseScaleY);
570 }
Chris Craikcb4d6002012-09-25 12:00:29 -0700571 }
572 } else {
573 // For kStrokeAndFill style, the path should be adjusted externally, as it will be treated as a fill here.
574 if (!isAA) {
575 getFillVerticesFromPerimeter(tempVertices, vertexBuffer);
576 } else {
577 getFillVerticesFromPerimeterAA(tempVertices, vertexBuffer, inverseScaleX, inverseScaleY);
578 }
579 }
580}
581
582
Chris Craik780c1282012-10-04 14:10:49 -0700583void pushToVector(Vector<Vertex>& vertices, float x, float y) {
584 // TODO: make this not yuck
585 vertices.push();
586 Vertex* newVertex = &(vertices.editArray()[vertices.size() - 1]);
587 Vertex::set(newVertex, x, y);
588}
589
590bool PathRenderer::convexPathPerimeterVertices(const SkPath& path, bool forceClose,
Chris Craikcb4d6002012-09-25 12:00:29 -0700591 float sqrInvScaleX, float sqrInvScaleY, Vector<Vertex>& outputVertices) {
Chris Craik710f46d2012-09-17 17:25:49 -0700592 ATRACE_CALL();
593
Chris Craik780c1282012-10-04 14:10:49 -0700594 // TODO: to support joins other than sharp miter, join vertices should be labelled in the
595 // perimeter, or resolved into more vertices. Reconsider forceClose-ing in that case.
596 SkPath::Iter iter(path, forceClose);
Chris Craik710f46d2012-09-17 17:25:49 -0700597 SkPoint pts[4];
598 SkPath::Verb v;
599 Vertex* newVertex = 0;
600 while (SkPath::kDone_Verb != (v = iter.next(pts))) {
601 switch (v) {
602 case SkPath::kMove_Verb:
Chris Craik780c1282012-10-04 14:10:49 -0700603 pushToVector(outputVertices, pts[0].x(), pts[0].y());
Chris Craik710f46d2012-09-17 17:25:49 -0700604 ALOGV("Move to pos %f %f", pts[0].x(), pts[0].y());
605 break;
606 case SkPath::kClose_Verb:
607 ALOGV("Close at pos %f %f", pts[0].x(), pts[0].y());
608 break;
609 case SkPath::kLine_Verb:
610 ALOGV("kLine_Verb %f %f -> %f %f",
Chris Craikcb4d6002012-09-25 12:00:29 -0700611 pts[0].x(), pts[0].y(),
612 pts[1].x(), pts[1].y());
Chris Craik710f46d2012-09-17 17:25:49 -0700613
Chris Craik780c1282012-10-04 14:10:49 -0700614 pushToVector(outputVertices, pts[1].x(), pts[1].y());
Chris Craik710f46d2012-09-17 17:25:49 -0700615 break;
616 case SkPath::kQuad_Verb:
617 ALOGV("kQuad_Verb");
618 recursiveQuadraticBezierVertices(
Chris Craikcb4d6002012-09-25 12:00:29 -0700619 pts[0].x(), pts[0].y(),
620 pts[2].x(), pts[2].y(),
621 pts[1].x(), pts[1].y(),
622 sqrInvScaleX, sqrInvScaleY, outputVertices);
Chris Craik710f46d2012-09-17 17:25:49 -0700623 break;
624 case SkPath::kCubic_Verb:
625 ALOGV("kCubic_Verb");
626 recursiveCubicBezierVertices(
Chris Craikcb4d6002012-09-25 12:00:29 -0700627 pts[0].x(), pts[0].y(),
628 pts[1].x(), pts[1].y(),
629 pts[3].x(), pts[3].y(),
630 pts[2].x(), pts[2].y(),
631 sqrInvScaleX, sqrInvScaleY, outputVertices);
Chris Craik710f46d2012-09-17 17:25:49 -0700632 break;
633 default:
634 break;
635 }
636 }
Chris Craik780c1282012-10-04 14:10:49 -0700637
638 int size = outputVertices.size();
639 if (size >= 2 && outputVertices[0].position[0] == outputVertices[size - 1].position[0] &&
640 outputVertices[0].position[1] == outputVertices[size - 1].position[1]) {
641 outputVertices.pop();
642 return true;
643 }
644 return false;
Chris Craik710f46d2012-09-17 17:25:49 -0700645}
646
647void PathRenderer::recursiveCubicBezierVertices(
648 float p1x, float p1y, float c1x, float c1y,
649 float p2x, float p2y, float c2x, float c2y,
Chris Craikcb4d6002012-09-25 12:00:29 -0700650 float sqrInvScaleX, float sqrInvScaleY, Vector<Vertex>& outputVertices) {
Chris Craik710f46d2012-09-17 17:25:49 -0700651 float dx = p2x - p1x;
652 float dy = p2y - p1y;
653 float d1 = fabs((c1x - p2x) * dy - (c1y - p2y) * dx);
654 float d2 = fabs((c2x - p2x) * dy - (c2y - p2y) * dx);
655 float d = d1 + d2;
656
Chris Craikcb4d6002012-09-25 12:00:29 -0700657 // multiplying by sqrInvScaleY/X equivalent to multiplying in dimensional scale factors
658
659 if (d * d < THRESHOLD * THRESHOLD * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) {
Chris Craik710f46d2012-09-17 17:25:49 -0700660 // below thresh, draw line by adding endpoint
Chris Craik780c1282012-10-04 14:10:49 -0700661 pushToVector(outputVertices, p2x, p2y);
Chris Craik710f46d2012-09-17 17:25:49 -0700662 } else {
663 float p1c1x = (p1x + c1x) * 0.5f;
664 float p1c1y = (p1y + c1y) * 0.5f;
665 float p2c2x = (p2x + c2x) * 0.5f;
666 float p2c2y = (p2y + c2y) * 0.5f;
667
668 float c1c2x = (c1x + c2x) * 0.5f;
669 float c1c2y = (c1y + c2y) * 0.5f;
670
671 float p1c1c2x = (p1c1x + c1c2x) * 0.5f;
672 float p1c1c2y = (p1c1y + c1c2y) * 0.5f;
673
674 float p2c1c2x = (p2c2x + c1c2x) * 0.5f;
675 float p2c1c2y = (p2c2y + c1c2y) * 0.5f;
676
677 float mx = (p1c1c2x + p2c1c2x) * 0.5f;
678 float my = (p1c1c2y + p2c1c2y) * 0.5f;
679
680 recursiveCubicBezierVertices(
681 p1x, p1y, p1c1x, p1c1y,
682 mx, my, p1c1c2x, p1c1c2y,
Chris Craikcb4d6002012-09-25 12:00:29 -0700683 sqrInvScaleX, sqrInvScaleY, outputVertices);
Chris Craik710f46d2012-09-17 17:25:49 -0700684 recursiveCubicBezierVertices(
685 mx, my, p2c1c2x, p2c1c2y,
686 p2x, p2y, p2c2x, p2c2y,
Chris Craikcb4d6002012-09-25 12:00:29 -0700687 sqrInvScaleX, sqrInvScaleY, outputVertices);
Chris Craik710f46d2012-09-17 17:25:49 -0700688 }
689}
690
691void PathRenderer::recursiveQuadraticBezierVertices(
692 float ax, float ay,
693 float bx, float by,
694 float cx, float cy,
Chris Craikcb4d6002012-09-25 12:00:29 -0700695 float sqrInvScaleX, float sqrInvScaleY, Vector<Vertex>& outputVertices) {
Chris Craik710f46d2012-09-17 17:25:49 -0700696 float dx = bx - ax;
697 float dy = by - ay;
698 float d = (cx - bx) * dy - (cy - by) * dx;
699
Chris Craikcb4d6002012-09-25 12:00:29 -0700700 if (d * d < THRESHOLD * THRESHOLD * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) {
Chris Craik710f46d2012-09-17 17:25:49 -0700701 // below thresh, draw line by adding endpoint
Chris Craik780c1282012-10-04 14:10:49 -0700702 pushToVector(outputVertices, bx, by);
Chris Craik710f46d2012-09-17 17:25:49 -0700703 } else {
704 float acx = (ax + cx) * 0.5f;
705 float bcx = (bx + cx) * 0.5f;
706 float acy = (ay + cy) * 0.5f;
707 float bcy = (by + cy) * 0.5f;
708
709 // midpoint
710 float mx = (acx + bcx) * 0.5f;
711 float my = (acy + bcy) * 0.5f;
712
713 recursiveQuadraticBezierVertices(ax, ay, mx, my, acx, acy,
Chris Craikcb4d6002012-09-25 12:00:29 -0700714 sqrInvScaleX, sqrInvScaleY, outputVertices);
Chris Craik710f46d2012-09-17 17:25:49 -0700715 recursiveQuadraticBezierVertices(mx, my, bx, by, bcx, bcy,
Chris Craikcb4d6002012-09-25 12:00:29 -0700716 sqrInvScaleX, sqrInvScaleY, outputVertices);
Chris Craik710f46d2012-09-17 17:25:49 -0700717 }
718}
719
720}; // namespace uirenderer
721}; // namespace android