blob: d222009b674259c37d9a58337edde9af9c3f9811 [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>
24
25#include <stdlib.h>
26#include <stdint.h>
27#include <sys/types.h>
28
29#include <utils/Log.h>
30#include <utils/Trace.h>
31
32#include "PathRenderer.h"
33#include "Matrix.h"
34#include "Vector.h"
35#include "Vertex.h"
36
37namespace android {
38namespace uirenderer {
39
40#define THRESHOLD 0.5f
41
42void PathRenderer::computeInverseScales(const mat4 *transform,
43 float &inverseScaleX, float& inverseScaleY) {
44 inverseScaleX = 1.0f;
45 inverseScaleY = 1.0f;
46 if (CC_UNLIKELY(!transform->isPureTranslate())) {
47 float m00 = transform->data[Matrix4::kScaleX];
48 float m01 = transform->data[Matrix4::kSkewY];
49 float m10 = transform->data[Matrix4::kSkewX];
50 float m11 = transform->data[Matrix4::kScaleY];
51 float scaleX = sqrt(m00 * m00 + m01 * m01);
52 float scaleY = sqrt(m10 * m10 + m11 * m11);
53 inverseScaleX = (scaleX != 0) ? (inverseScaleX / scaleX) : 0;
54 inverseScaleY = (scaleY != 0) ? (inverseScaleY / scaleY) : 0;
55 }
56}
57
58void PathRenderer::convexPathFillVertices(const SkPath &path, const mat4 *transform,
59 VertexBuffer &vertexBuffer, bool isAA) {
60 ATRACE_CALL();
61 float inverseScaleX;
62 float inverseScaleY;
63 computeInverseScales(transform, inverseScaleX, inverseScaleY);
64
65 Vector<Vertex> tempVertices;
66 float thresholdx = THRESHOLD * inverseScaleX;
67 float thresholdy = THRESHOLD * inverseScaleY;
68 convexPathVertices(path,
69 thresholdx * thresholdx,
70 thresholdy * thresholdy,
71 tempVertices);
72
73#if VERTEX_DEBUG
74 for (unsigned int i = 0; i < tempVertices.size(); i++) {
75 ALOGD("orig path: point at %f %f",
76 tempVertices[i].position[0],
77 tempVertices[i].position[1]);
78 }
79#endif
80 int currentIndex = 0;
81 if (!isAA) {
82 Vertex* buffer = vertexBuffer.alloc<Vertex>(tempVertices.size());
83
84 // zig zag between all previous points on the inside of the hull to create a
85 // triangle strip that fills the hull
86 int srcAindex = 0;
87 int srcBindex = tempVertices.size() - 1;
88 while (srcAindex <= srcBindex) {
89 Vertex::set(&buffer[currentIndex++],
90 tempVertices.editArray()[srcAindex].position[0],
91 tempVertices.editArray()[srcAindex].position[1]);
92 if (srcAindex == srcBindex) break;
93 Vertex::set(&buffer[currentIndex++],
94 tempVertices.editArray()[srcBindex].position[0],
95 tempVertices.editArray()[srcBindex].position[1]);
96 srcAindex++;
97 srcBindex--;
98 }
99 return;
100 }
101 AlphaVertex* buffer = vertexBuffer.alloc<AlphaVertex>(tempVertices.size() * 3 + 2);
102
103 // generate alpha points - fill Alpha vertex gaps in between each point with
104 // alpha 0 vertex, offset by a scaled normal.
105 Vertex* last = &(tempVertices.editArray()[tempVertices.size()-1]);
106
107 for (unsigned int i = 0; i<tempVertices.size(); i++) {
108 Vertex* current = &(tempVertices.editArray()[i]);
109 Vertex* next = &(tempVertices.editArray()[i + 1 >= tempVertices.size() ? 0 : i + 1]);
110
111 vec2 lastNormal(current->position[1] - last->position[1],
112 last->position[0] - current->position[0]);
113 lastNormal.normalize();
114 vec2 nextNormal(next->position[1] - current->position[1],
115 current->position[0] - next->position[0]);
116 nextNormal.normalize();
117
118 // AA point offset from original point is that point's normal, such that
119 // each side is offset by .5 pixels
120 vec2 totalOffset = (lastNormal + nextNormal) / (2 * (1 + lastNormal.dot(nextNormal)));
121 totalOffset.x *= inverseScaleX;
122 totalOffset.y *= inverseScaleY;
123
124 AlphaVertex::set(&buffer[currentIndex++],
125 current->position[0] + totalOffset.x,
126 current->position[1] + totalOffset.y,
127 0.0f);
128 AlphaVertex::set(&buffer[currentIndex++],
129 current->position[0] - totalOffset.x,
130 current->position[1] - totalOffset.y,
131 1.0f);
132 last = current;
133 }
134
135 // wrap around to beginning
136 AlphaVertex::set(&buffer[currentIndex++],
137 buffer[0].position[0],
138 buffer[0].position[1], 0.0f);
139 AlphaVertex::set(&buffer[currentIndex++],
140 buffer[1].position[0],
141 buffer[1].position[1], 1.0f);
142
143 // zig zag between all previous points on the inside of the hull to create a
144 // triangle strip that fills the hull, repeating the first inner point to
145 // create degenerate tris to start inside path
146 int srcAindex = 0;
147 int srcBindex = tempVertices.size() - 1;
148 while (srcAindex <= srcBindex) {
149 AlphaVertex::set(&buffer[currentIndex++],
150 buffer[srcAindex * 2 + 1].position[0],
151 buffer[srcAindex * 2 + 1].position[1],
152 1.0f);
153 if (srcAindex == srcBindex) break;
154 AlphaVertex::set(&buffer[currentIndex++],
155 buffer[srcBindex * 2 + 1].position[0],
156 buffer[srcBindex * 2 + 1].position[1],
157 1.0f);
158 srcAindex++;
159 srcBindex--;
160 }
161
162#if VERTEX_DEBUG
163 for (unsigned int i = 0; i < vertexBuffer.mSize; i++) {
164 ALOGD("point at %f %f",
165 buffer[i].position[0],
166 buffer[i].position[1]);
167 }
168#endif
169}
170
171
172void PathRenderer::convexPathVertices(const SkPath &path, float thresholdx, float thresholdy,
173 Vector<Vertex> &outputVertices) {
174 ATRACE_CALL();
175
176 SkPath::Iter iter(path, true);
177 SkPoint pos;
178 SkPoint pts[4];
179 SkPath::Verb v;
180 Vertex* newVertex = 0;
181 while (SkPath::kDone_Verb != (v = iter.next(pts))) {
182 switch (v) {
183 case SkPath::kMove_Verb:
184 pos = pts[0];
185 ALOGV("Move to pos %f %f", pts[0].x(), pts[0].y());
186 break;
187 case SkPath::kClose_Verb:
188 ALOGV("Close at pos %f %f", pts[0].x(), pts[0].y());
189 break;
190 case SkPath::kLine_Verb:
191 ALOGV("kLine_Verb %f %f -> %f %f",
192 pts[0].x(), pts[0].y(),
193 pts[1].x(), pts[1].y());
194
195 // TODO: make this not yuck
196 outputVertices.push();
197 newVertex = &(outputVertices.editArray()[outputVertices.size()-1]);
198 Vertex::set(newVertex, pts[1].x(), pts[1].y());
199 break;
200 case SkPath::kQuad_Verb:
201 ALOGV("kQuad_Verb");
202 recursiveQuadraticBezierVertices(
203 pts[0].x(), pts[0].y(),
204 pts[2].x(), pts[2].y(),
205 pts[1].x(), pts[1].y(),
206 thresholdx, thresholdy,
207 outputVertices);
208 break;
209 case SkPath::kCubic_Verb:
210 ALOGV("kCubic_Verb");
211 recursiveCubicBezierVertices(
212 pts[0].x(), pts[0].y(),
213 pts[1].x(), pts[1].y(),
214 pts[3].x(), pts[3].y(),
215 pts[2].x(), pts[2].y(),
216 thresholdx, thresholdy, outputVertices);
217 break;
218 default:
219 break;
220 }
221 }
222}
223
224void PathRenderer::recursiveCubicBezierVertices(
225 float p1x, float p1y, float c1x, float c1y,
226 float p2x, float p2y, float c2x, float c2y,
227 float thresholdx, float thresholdy, Vector<Vertex> &outputVertices) {
228 float dx = p2x - p1x;
229 float dy = p2y - p1y;
230 float d1 = fabs((c1x - p2x) * dy - (c1y - p2y) * dx);
231 float d2 = fabs((c2x - p2x) * dy - (c2y - p2y) * dx);
232 float d = d1 + d2;
233
234 if (d * d < (thresholdx * (dx * dx) + thresholdy * (dy * dy))) {
235 // below thresh, draw line by adding endpoint
236 // TODO: make this not yuck
237 outputVertices.push();
238 Vertex* newVertex = &(outputVertices.editArray()[outputVertices.size()-1]);
239 Vertex::set(newVertex, p2x, p2y);
240 } else {
241 float p1c1x = (p1x + c1x) * 0.5f;
242 float p1c1y = (p1y + c1y) * 0.5f;
243 float p2c2x = (p2x + c2x) * 0.5f;
244 float p2c2y = (p2y + c2y) * 0.5f;
245
246 float c1c2x = (c1x + c2x) * 0.5f;
247 float c1c2y = (c1y + c2y) * 0.5f;
248
249 float p1c1c2x = (p1c1x + c1c2x) * 0.5f;
250 float p1c1c2y = (p1c1y + c1c2y) * 0.5f;
251
252 float p2c1c2x = (p2c2x + c1c2x) * 0.5f;
253 float p2c1c2y = (p2c2y + c1c2y) * 0.5f;
254
255 float mx = (p1c1c2x + p2c1c2x) * 0.5f;
256 float my = (p1c1c2y + p2c1c2y) * 0.5f;
257
258 recursiveCubicBezierVertices(
259 p1x, p1y, p1c1x, p1c1y,
260 mx, my, p1c1c2x, p1c1c2y,
261 thresholdx, thresholdy,
262 outputVertices);
263 recursiveCubicBezierVertices(
264 mx, my, p2c1c2x, p2c1c2y,
265 p2x, p2y, p2c2x, p2c2y,
266 thresholdx, thresholdy,
267 outputVertices);
268 }
269}
270
271void PathRenderer::recursiveQuadraticBezierVertices(
272 float ax, float ay,
273 float bx, float by,
274 float cx, float cy,
275 float thresholdx, float thresholdy, Vector<Vertex> &outputVertices) {
276 float dx = bx - ax;
277 float dy = by - ay;
278 float d = (cx - bx) * dy - (cy - by) * dx;
279
280 if (d * d < (thresholdx * (dx * dx) + thresholdy * (dy * dy))) {
281 // below thresh, draw line by adding endpoint
282 // TODO: make this not yuck
283 outputVertices.push();
284 Vertex* newVertex = &(outputVertices.editArray()[outputVertices.size()-1]);
285 Vertex::set(newVertex, bx, by);
286 } else {
287 float acx = (ax + cx) * 0.5f;
288 float bcx = (bx + cx) * 0.5f;
289 float acy = (ay + cy) * 0.5f;
290 float bcy = (by + cy) * 0.5f;
291
292 // midpoint
293 float mx = (acx + bcx) * 0.5f;
294 float my = (acy + bcy) * 0.5f;
295
296 recursiveQuadraticBezierVertices(ax, ay, mx, my, acx, acy,
297 thresholdx, thresholdy, outputVertices);
298 recursiveQuadraticBezierVertices(mx, my, bx, by, bcx, bcy,
299 thresholdx, thresholdy, outputVertices);
300 }
301}
302
303}; // namespace uirenderer
304}; // namespace android