• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 #include <SkPaint.h>
25 
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 
38 namespace android {
39 namespace uirenderer {
40 
41 #define THRESHOLD 0.5f
42 
computePathBounds(const SkPath & path,const SkPaint * paint)43 SkRect 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 
computeInverseScales(const mat4 * transform,float & inverseScaleX,float & inverseScaleY)52 void computeInverseScales(const mat4 *transform, float &inverseScaleX, float& inverseScaleY) {
53     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);
60         inverseScaleX = (scaleX != 0) ? (1.0f / scaleX) : 1.0f;
61         inverseScaleY = (scaleY != 0) ? (1.0f / scaleY) : 1.0f;
62     } else {
63         inverseScaleX = 1.0f;
64         inverseScaleY = 1.0f;
65     }
66 }
67 
copyVertex(Vertex * destPtr,const Vertex * srcPtr)68 inline void copyVertex(Vertex* destPtr, const Vertex* srcPtr) {
69     Vertex::set(destPtr, srcPtr->position[0], srcPtr->position[1]);
70 }
71 
copyAlphaVertex(AlphaVertex * destPtr,const AlphaVertex * srcPtr)72 inline void copyAlphaVertex(AlphaVertex* destPtr, const AlphaVertex* srcPtr) {
73     AlphaVertex::set(destPtr, srcPtr->position[0], srcPtr->position[1], srcPtr->alpha);
74 }
75 
76 /**
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)
83  *
84  * NOTE: assumes angles between normals 90 degrees or less
85  */
totalOffsetFromNormals(const vec2 & normalA,const vec2 & normalB)86 inline vec2 totalOffsetFromNormals(const vec2& normalA, const vec2& normalB) {
87     return (normalA + normalB) / (1 + fabs(normalA.dot(normalB)));
88 }
89 
scaleOffsetForStrokeWidth(vec2 & offset,float halfStrokeWidth,float inverseScaleX,float inverseScaleY)90 inline 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 
getFillVerticesFromPerimeter(const Vector<Vertex> & perimeter,VertexBuffer & vertexBuffer)101 void getFillVerticesFromPerimeter(const Vector<Vertex>& perimeter, VertexBuffer& vertexBuffer) {
102     Vertex* buffer = vertexBuffer.alloc<Vertex>(perimeter.size());
103 
104     int currentIndex = 0;
105     // 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--;
115     }
116 }
117 
getStrokeVerticesFromPerimeter(const Vector<Vertex> & perimeter,float halfStrokeWidth,VertexBuffer & vertexBuffer,float inverseScaleX,float inverseScaleY)118 void 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 
134         vec2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
135         scaleOffsetForStrokeWidth(totalOffset, halfStrokeWidth, inverseScaleX, inverseScaleY);
136 
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 
getStrokeVerticesFromUnclosedVertices(const Vector<Vertex> & vertices,float halfStrokeWidth,VertexBuffer & vertexBuffer,float inverseScaleX,float inverseScaleY)155 void 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 
getFillVerticesFromPerimeterAA(const Vector<Vertex> & perimeter,VertexBuffer & vertexBuffer,float inverseScaleX,float inverseScaleY)204 void getFillVerticesFromPerimeterAA(const Vector<Vertex>& perimeter, VertexBuffer& vertexBuffer,
205          float inverseScaleX, float inverseScaleY) {
206     AlphaVertex* buffer = vertexBuffer.alloc<AlphaVertex>(perimeter.size() * 3 + 2);
207 
208     // generate alpha points - fill Alpha vertex gaps in between each point with
209     // alpha 0 vertex, offset by a scaled normal.
210     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]);
218         vec2 nextNormal(next->position[1] - current->position[1],
219                 current->position[0] - next->position[0]);
220         nextNormal.normalize();
221 
222         // 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;
227 
228         AlphaVertex::set(&buffer[currentIndex++],
229                 current->position[0] + totalOffset.x,
230                 current->position[1] + totalOffset.y,
231                 0.0f);
232         AlphaVertex::set(&buffer[currentIndex++],
233                 current->position[0] - totalOffset.x,
234                 current->position[1] - totalOffset.y,
235                 1.0f);
236 
237         last = current;
238         current = next;
239         lastNormal = nextNormal;
240     }
241 
242     // wrap around to beginning
243     copyAlphaVertex(&buffer[currentIndex++], &buffer[0]);
244     copyAlphaVertex(&buffer[currentIndex++], &buffer[1]);
245 
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;
250     int srcBindex = perimeter.size() - 1;
251     while (srcAindex <= srcBindex) {
252         copyAlphaVertex(&buffer[currentIndex++], &buffer[srcAindex * 2 + 1]);
253         if (srcAindex == srcBindex) break;
254         copyAlphaVertex(&buffer[currentIndex++], &buffer[srcBindex * 2 + 1]);
255         srcAindex++;
256         srcBindex--;
257     }
258 
259 #if VERTEX_DEBUG
260     for (unsigned int i = 0; i < vertexBuffer.getSize(); i++) {
261         ALOGD("point at %f %f, alpha %f", buffer[i].position[0], buffer[i].position[1], buffer[i].alpha);
262     }
263 #endif
264 }
265 
266 
getStrokeVerticesFromUnclosedVerticesAA(const Vector<Vertex> & vertices,float halfStrokeWidth,VertexBuffer & vertexBuffer,float inverseScaleX,float inverseScaleY)267 void 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 
getStrokeVerticesFromPerimeterAA(const Vector<Vertex> & perimeter,float halfStrokeWidth,VertexBuffer & vertexBuffer,float inverseScaleX,float inverseScaleY)422 void getStrokeVerticesFromPerimeterAA(const Vector<Vertex>& perimeter, float halfStrokeWidth,
423         VertexBuffer& vertexBuffer, float inverseScaleX, float inverseScaleY) {
424     AlphaVertex* buffer = vertexBuffer.alloc<AlphaVertex>(6 * perimeter.size() + 8);
425 
426     // 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 &&
430             halfStrokeWidth * inverseScaleX < 0.5f) {
431         maxAlpha *= (2 * halfStrokeWidth) / inverseScaleX;
432         halfStrokeWidth = 0.0f;
433     }
434 
435     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 
451         vec2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
452         vec2 AAOffset = totalOffset;
453         AAOffset.x *= 0.5f * inverseScaleX;
454         AAOffset.y *= 0.5f * inverseScaleY;
455 
456         vec2 innerOffset = totalOffset;
457         scaleOffsetForStrokeWidth(innerOffset, halfStrokeWidth, inverseScaleX, inverseScaleY);
458         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,
468                 maxAlpha);
469 
470         AlphaVertex::set(&buffer[currentStrokeIndex++],
471                 current->position[0] + innerOffset.x,
472                 current->position[1] + innerOffset.y,
473                 maxAlpha);
474         AlphaVertex::set(&buffer[currentStrokeIndex++],
475                 current->position[0] - innerOffset.x,
476                 current->position[1] - innerOffset.y,
477                 maxAlpha);
478 
479         AlphaVertex::set(&buffer[currentAAInnerIndex++],
480                 current->position[0] - innerOffset.x,
481                 current->position[1] - innerOffset.y,
482                 maxAlpha);
483         AlphaVertex::set(&buffer[currentAAInnerIndex++],
484                 current->position[0] - outerOffset.x,
485                 current->position[1] - outerOffset.y,
486                 0.0f);
487 
488         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
505 
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
511 }
512 
convexPathVertices(const SkPath & path,const SkPaint * paint,const mat4 * transform,VertexBuffer & vertexBuffer)513 void 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;
524     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     }
535 
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,
539             threshInvScaleY * threshInvScaleY, tempVertices);
540 
541     if (!tempVertices.size()) {
542         // path was empty, return without allocating vertex buffer
543         return;
544     }
545 
546 #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) {
555             if (wasClosed) {
556                 getStrokeVerticesFromPerimeter(tempVertices, halfStrokeWidth, vertexBuffer,
557                         inverseScaleX, inverseScaleY);
558             } else {
559                 getStrokeVerticesFromUnclosedVertices(tempVertices, halfStrokeWidth, vertexBuffer,
560                         inverseScaleX, inverseScaleY);
561             }
562 
563         } else {
564             if (wasClosed) {
565                 getStrokeVerticesFromPerimeterAA(tempVertices, halfStrokeWidth, vertexBuffer,
566                         inverseScaleX, inverseScaleY);
567             } else {
568                 getStrokeVerticesFromUnclosedVerticesAA(tempVertices, halfStrokeWidth, vertexBuffer,
569                         inverseScaleX, inverseScaleY);
570             }
571         }
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 
pushToVector(Vector<Vertex> & vertices,float x,float y)583 void 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 
convexPathPerimeterVertices(const SkPath & path,bool forceClose,float sqrInvScaleX,float sqrInvScaleY,Vector<Vertex> & outputVertices)590 bool PathRenderer::convexPathPerimeterVertices(const SkPath& path, bool forceClose,
591         float sqrInvScaleX, float sqrInvScaleY, Vector<Vertex>& outputVertices) {
592     ATRACE_CALL();
593 
594     // 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);
597     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:
603                     pushToVector(outputVertices, pts[0].x(), pts[0].y());
604                     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",
611                             pts[0].x(), pts[0].y(),
612                             pts[1].x(), pts[1].y());
613 
614                     pushToVector(outputVertices, pts[1].x(), pts[1].y());
615                     break;
616                 case SkPath::kQuad_Verb:
617                     ALOGV("kQuad_Verb");
618                     recursiveQuadraticBezierVertices(
619                             pts[0].x(), pts[0].y(),
620                             pts[2].x(), pts[2].y(),
621                             pts[1].x(), pts[1].y(),
622                             sqrInvScaleX, sqrInvScaleY, outputVertices);
623                     break;
624                 case SkPath::kCubic_Verb:
625                     ALOGV("kCubic_Verb");
626                     recursiveCubicBezierVertices(
627                             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);
632                     break;
633                 default:
634                     break;
635             }
636     }
637 
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;
645 }
646 
recursiveCubicBezierVertices(float p1x,float p1y,float c1x,float c1y,float p2x,float p2y,float c2x,float c2y,float sqrInvScaleX,float sqrInvScaleY,Vector<Vertex> & outputVertices)647 void PathRenderer::recursiveCubicBezierVertices(
648         float p1x, float p1y, float c1x, float c1y,
649         float p2x, float p2y, float c2x, float c2y,
650         float sqrInvScaleX, float sqrInvScaleY, Vector<Vertex>& outputVertices) {
651     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 
657     // multiplying by sqrInvScaleY/X equivalent to multiplying in dimensional scale factors
658 
659     if (d * d < THRESHOLD * THRESHOLD * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) {
660         // below thresh, draw line by adding endpoint
661         pushToVector(outputVertices, p2x, p2y);
662     } 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,
683                 sqrInvScaleX, sqrInvScaleY, outputVertices);
684         recursiveCubicBezierVertices(
685                 mx, my, p2c1c2x, p2c1c2y,
686                 p2x, p2y, p2c2x, p2c2y,
687                 sqrInvScaleX, sqrInvScaleY, outputVertices);
688     }
689 }
690 
recursiveQuadraticBezierVertices(float ax,float ay,float bx,float by,float cx,float cy,float sqrInvScaleX,float sqrInvScaleY,Vector<Vertex> & outputVertices)691 void PathRenderer::recursiveQuadraticBezierVertices(
692         float ax, float ay,
693         float bx, float by,
694         float cx, float cy,
695         float sqrInvScaleX, float sqrInvScaleY, Vector<Vertex>& outputVertices) {
696     float dx = bx - ax;
697     float dy = by - ay;
698     float d = (cx - bx) * dy - (cy - by) * dx;
699 
700     if (d * d < THRESHOLD * THRESHOLD * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) {
701         // below thresh, draw line by adding endpoint
702         pushToVector(outputVertices, bx, by);
703     } 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,
714                 sqrInvScaleX, sqrInvScaleY, outputVertices);
715         recursiveQuadraticBezierVertices(mx, my, bx, by, bcx, bcy,
716                 sqrInvScaleX, sqrInvScaleY, outputVertices);
717     }
718 }
719 
720 }; // namespace uirenderer
721 }; // namespace android
722