Home | History | Annotate | Download | only in hwui
      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 
     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 
     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 
     68 inline void copyVertex(Vertex* destPtr, const Vertex* srcPtr) {
     69     Vertex::set(destPtr, srcPtr->position[0], srcPtr->position[1]);
     70 }
     71 
     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  */
     86 inline vec2 totalOffsetFromNormals(const vec2& normalA, const vec2& normalB) {
     87     return (normalA + normalB) / (1 + fabs(normalA.dot(normalB)));
     88 }
     89 
     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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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