Home | History | Annotate | Download | only in unit
      1 /*
      2  * Copyright (C) 2015 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 #include <gtest/gtest.h>
     18 
     19 #include "PathParser.h"
     20 #include "VectorDrawable.h"
     21 #include "utils/MathUtils.h"
     22 #include "utils/VectorDrawableUtils.h"
     23 
     24 #include <functional>
     25 
     26 namespace android {
     27 namespace uirenderer {
     28 
     29 struct TestData {
     30     const char* pathString;
     31     const PathData pathData;
     32     const std::function<void(SkPath*)> skPathLamda;
     33 };
     34 
     35 const static TestData sTestDataSet[] = {
     36         // TestData with scientific notation -2e3 etc.
     37         {// Path
     38          "M2.000000,22.000000l20.000000,0.000000 1e0-2e3z",
     39          {
     40                  // Verbs
     41                  {'M', 'l', 'z'},
     42                  // Verb sizes
     43                  {2, 4, 0},
     44                  // Points
     45                  {2, 22, 20, 0, 1, -2000},
     46          },
     47          [](SkPath* outPath) {
     48              outPath->moveTo(2, 22);
     49              outPath->rLineTo(20, 0);
     50              outPath->rLineTo(1, -2000);
     51              outPath->close();
     52              outPath->moveTo(2, 22);
     53          }},
     54 
     55         // Comprehensive data, containing all the verbs possible.
     56         {// Path
     57          "M 1 1 m 2 2, l 3 3 L 3 3 H 4 h4 V5 v5, Q6 6 6 6 q 6 6 6 6t 7 7 T 7 7 C 8 8 8 8 8 8 c 8 8 "
     58          "8 8 8 8 S 9 9 9 9 s 9 9 9 9 A 10 10 0 1 1 10 10 a 10 10 0 1 1 10 10",
     59          {// Verbs
     60           {'M', 'm', 'l', 'L', 'H', 'h', 'V', 'v', 'Q', 'q', 't', 'T', 'C', 'c', 'S', 's', 'A',
     61            'a'},
     62           // VerbSizes
     63           {2, 2, 2, 2, 1, 1, 1, 1, 4, 4, 2, 2, 6, 6, 4, 4, 7, 7},
     64           // Points
     65           {
     66                   1.0,  1.0, 2.0, 2.0, 3.0,  3.0,  3.0,  3.0,  4.0, 4.0, 5.0, 5.0,  6.0,  6.0, 6.0,
     67                   6.0,  6.0, 6.0, 6.0, 6.0,  7.0,  7.0,  7.0,  7.0, 8.0, 8.0, 8.0,  8.0,  8.0, 8.0,
     68                   8.0,  8.0, 8.0, 8.0, 8.0,  8.0,  9.0,  9.0,  9.0, 9.0, 9.0, 9.0,  9.0,  9.0, 10.0,
     69                   10.0, 0.0, 1.0, 1.0, 10.0, 10.0, 10.0, 10.0, 0.0, 1.0, 1.0, 10.0, 10.0,
     70           }},
     71          [](SkPath* outPath) {
     72              outPath->moveTo(1.0, 1.0);
     73              outPath->rMoveTo(2.0, 2.0);
     74              outPath->rLineTo(3.0, 3.0);
     75              outPath->lineTo(3.0, 3.0);
     76              outPath->lineTo(4.0, 3.0);
     77              outPath->rLineTo(4.0, 0);
     78              outPath->lineTo(8.0, 5.0);
     79              outPath->rLineTo(0, 5.0);
     80              outPath->quadTo(6.0, 6.0, 6.0, 6.0);
     81              outPath->rQuadTo(6.0, 6.0, 6.0, 6.0);
     82              outPath->rQuadTo(0.0, 0.0, 7.0, 7.0);
     83              outPath->quadTo(26.0, 26.0, 7.0, 7.0);
     84              outPath->cubicTo(8.0, 8.0, 8.0, 8.0, 8.0, 8.0);
     85              outPath->rCubicTo(8.0, 8.0, 8.0, 8.0, 8.0, 8.0);
     86              outPath->cubicTo(16.0, 16.0, 9.0, 9.0, 9.0, 9.0);
     87              outPath->rCubicTo(0.0, 0.0, 9.0, 9.0, 9.0, 9.0);
     88              outPath->arcTo(10.0, 10.0, 0.0, SkPath::kLarge_ArcSize, SkPath::kCW_Direction, 10.0, 10.0);
     89              outPath->arcTo(10.0, 10.0, 0.0, SkPath::kLarge_ArcSize, SkPath::kCW_Direction, 20.0, 20.0);
     90          }},
     91 
     92         // Check box VectorDrawable path data
     93         {// Path
     94          "M 0.0,-1.0 l 0.0,0.0 c 0.5522847498,0.0 1.0,0.4477152502 1.0,1.0 l 0.0,0.0 c "
     95          "0.0,0.5522847498 -0.4477152502,1.0 -1.0,1.0 l 0.0,0.0 c -0.5522847498,0.0 "
     96          "-1.0,-0.4477152502 -1.0,-1.0 l 0.0,0.0 c 0.0,-0.5522847498 0.4477152502,-1.0 1.0,-1.0 Z "
     97          "M 7.0,-9.0 c 0.0,0.0 -14.0,0.0 -14.0,0.0 c -1.1044921875,0.0 -2.0,0.8955078125 -2.0,2.0 "
     98          "c 0.0,0.0 0.0,14.0 0.0,14.0 c 0.0,1.1044921875 0.8955078125,2.0 2.0,2.0 c 0.0,0.0 "
     99          "14.0,0.0 14.0,0.0 c 1.1044921875,0.0 2.0,-0.8955078125 2.0,-2.0 c 0.0,0.0 0.0,-14.0 "
    100          "0.0,-14.0 c 0.0,-1.1044921875 -0.8955078125,-2.0 -2.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z",
    101          {
    102                  {'M', 'l', 'c', 'l', 'c', 'l', 'c', 'l', 'c', 'Z', 'M',
    103                   'c', 'c', 'c', 'c', 'c', 'c', 'c', 'c', 'c', 'Z'},
    104                  {2, 2, 6, 2, 6, 2, 6, 2, 6, 0, 2, 6, 6, 6, 6, 6, 6, 6, 6, 6, 0},
    105                  {0.0,         -1.0,       0.0,         0.0,        0.5522848, 0.0,   1.0,
    106                   0.44771525,  1.0,        1.0,         0.0,        0.0,       0.0,   0.5522848,
    107                   -0.44771525, 1.0,        -1.0,        1.0,        0.0,       0.0,   -0.5522848,
    108                   0.0,         -1.0,       -0.44771525, -1.0,       -1.0,      0.0,   0.0,
    109                   0.0,         -0.5522848, 0.44771525,  -1.0,       1.0,       -1.0,  7.0,
    110                   -9.0,        0.0,        0.0,         -14.0,      0.0,       -14.0, 0.0,
    111                   -1.1044922,  0.0,        -2.0,        0.8955078,  -2.0,      2.0,   0.0,
    112                   0.0,         0.0,        14.0,        0.0,        14.0,      0.0,   1.1044922,
    113                   0.8955078,   2.0,        2.0,         2.0,        0.0,       0.0,   14.0,
    114                   0.0,         14.0,       0.0,         1.1044922,  0.0,       2.0,   -0.8955078,
    115                   2.0,         -2.0,       0.0,         0.0,        0.0,       -14.0, 0.0,
    116                   -14.0,       0.0,        -1.1044922,  -0.8955078, -2.0,      -2.0,  -2.0,
    117                   0.0,         0.0,        0.0,         0.0,        0.0,       0.0},
    118          },
    119          [](SkPath* outPath) {
    120              outPath->moveTo(0.0, -1.0);
    121              outPath->rLineTo(0.0, 0.0);
    122              outPath->rCubicTo(0.5522848, 0.0, 1.0, 0.44771525, 1.0, 1.0);
    123              outPath->rLineTo(0.0, 0.0);
    124              outPath->rCubicTo(0.0, 0.5522848, -0.44771525, 1.0, -1.0, 1.0);
    125              outPath->rLineTo(0.0, 0.0);
    126              outPath->rCubicTo(-0.5522848, 0.0, -1.0, -0.44771525, -1.0, -1.0);
    127              outPath->rLineTo(0.0, 0.0);
    128              outPath->rCubicTo(0.0, -0.5522848, 0.44771525, -1.0, 1.0, -1.0);
    129              outPath->close();
    130              outPath->moveTo(0.0, -1.0);
    131              outPath->moveTo(7.0, -9.0);
    132              outPath->rCubicTo(0.0, 0.0, -14.0, 0.0, -14.0, 0.0);
    133              outPath->rCubicTo(-1.1044922, 0.0, -2.0, 0.8955078, -2.0, 2.0);
    134              outPath->rCubicTo(0.0, 0.0, 0.0, 14.0, 0.0, 14.0);
    135              outPath->rCubicTo(0.0, 1.1044922, 0.8955078, 2.0, 2.0, 2.0);
    136              outPath->rCubicTo(0.0, 0.0, 14.0, 0.0, 14.0, 0.0);
    137              outPath->rCubicTo(1.1044922, 0.0, 2.0, -0.8955078, 2.0, -2.0);
    138              outPath->rCubicTo(0.0, 0.0, 0.0, -14.0, 0.0, -14.0);
    139              outPath->rCubicTo(0.0, -1.1044922, -0.8955078, -2.0, -2.0, -2.0);
    140              outPath->rCubicTo(0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
    141              outPath->close();
    142              outPath->moveTo(7.0, -9.0);
    143          }},
    144 
    145         // pie1 in progress bar
    146         {"M300,70 a230,230 0 1,0 1,0 z",
    147          {
    148                  {
    149                          'M', 'a', 'z',
    150                  },
    151                  {
    152                          2, 7, 0,
    153                  },
    154                  {
    155                          300.0, 70.0, 230.0, 230.0, 0.0, 1.0, 0.0, 1.0, 0.0,
    156                  },
    157          },
    158          [](SkPath* outPath) {
    159              outPath->moveTo(300.0, 70.0);
    160              outPath->arcTo(230.0, 230.0, 0.0, SkPath::kLarge_ArcSize, SkPath::kCCW_Direction, 301.0, 70.0);
    161              outPath->close();
    162              outPath->moveTo(300.0, 70.0);
    163          }},
    164 
    165         // Random long data
    166         {// Path
    167          "M5.3,13.2c-0.1,0.0 -0.3,0.0 -0.4,-0.1c-0.3,-0.2 -0.4,-0.7 -0.2,-1.0c1.3,-1.9 2.9,-3.4 "
    168          "4.9,-4.5c4.1,-2.2 9.3,-2.2 13.4,0.0c1.9,1.1 3.6,2.5 4.9,4.4c0.2,0.3 0.1,0.8 "
    169          "-0.2,1.0c-0.3,0.2 -0.8,0.1 -1.0,-0.2c-1.2,-1.7 -2.6,-3.0 -4.3,-4.0c-3.7,-2.0 -8.3,-2.0 "
    170          "-12.0,0.0c-1.7,0.9 -3.2,2.3 -4.3,4.0C5.7,13.1 5.5,13.2 5.3,13.2z",
    171          {
    172                  // Verbs
    173                  {'M', 'c', 'c', 'c', 'c', 'c', 'c', 'c', 'c', 'c', 'c', 'C', 'z'},
    174                  // Verb sizes
    175                  {2, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 0},
    176                  // Points
    177                  {5.3,  13.2, -0.1, 0,    -0.3, 0,    -0.4, -0.1, -0.3, -0.2, -0.4, -0.7, -0.2, -1,
    178                   1.3,  -1.9, 2.9,  -3.4, 4.9,  -4.5, 4.1,  -2.2, 9.3,  -2.2, 13.4, 0,    1.9,  1.1,
    179                   3.6,  2.5,  4.9,  4.4,  0.2,  0.3,  0.1,  0.8,  -0.2, 1,    -0.3, 0.2,  -0.8, 0.1,
    180                   -1,   -0.2, -1.2, -1.7, -2.6, -3,   -4.3, -4,   -3.7, -2,   -8.3, -2,   -12,  0,
    181                   -1.7, 0.9,  -3.2, 2.3,  -4.3, 4,    5.7,  13.1, 5.5,  13.2, 5.3,  13.2},
    182          },
    183          [](SkPath* outPath) {
    184              outPath->moveTo(5.3, 13.2);
    185              outPath->rCubicTo(-0.1, 0.0, -0.3, 0.0, -0.4, -0.1);
    186              outPath->rCubicTo(-0.3, -0.2, -0.4, -0.7, -0.2, -1.0);
    187              outPath->rCubicTo(1.3, -1.9, 2.9, -3.4, 4.9, -4.5);
    188              outPath->rCubicTo(4.1, -2.2, 9.3, -2.2, 13.4, 0.0);
    189              outPath->rCubicTo(1.9, 1.1, 3.6, 2.5, 4.9, 4.4);
    190              outPath->rCubicTo(0.2, 0.3, 0.1, 0.8, -0.2, 1.0);
    191              outPath->rCubicTo(-0.3, 0.2, -0.8, 0.1, -1.0, -0.2);
    192              outPath->rCubicTo(-1.2, -1.7, -2.6, -3.0, -4.3, -4.0);
    193              outPath->rCubicTo(-3.7, -2.0, -8.3, -2.0, -12.0, 0.0);
    194              outPath->rCubicTo(-1.7, 0.9, -3.2, 2.3, -4.3, 4.0);
    195              outPath->cubicTo(5.7, 13.1, 5.5, 13.2, 5.3, 13.2);
    196              outPath->close();
    197              outPath->moveTo(5.3, 13.2);
    198          }},
    199 
    200         // Extreme case with numbers and decimal points crunched together
    201         {// Path
    202          "l0.0.0.5.0.0.5-0.5.0.0-.5z",
    203          {
    204                  // Verbs
    205                  {'l', 'z'},
    206                  // Verb sizes
    207                  {10, 0},
    208                  // Points
    209                  {0, 0, 0.5, 0, 0, 0.5, -0.5, 0, 0, -0.5},
    210          },
    211          [](SkPath* outPath) {
    212              outPath->rLineTo(0.0, 0.0);
    213              outPath->rLineTo(0.5, 0.0);
    214              outPath->rLineTo(0.0, 0.5);
    215              outPath->rLineTo(-0.5, 0.0);
    216              outPath->rLineTo(0.0, -0.5);
    217              outPath->close();
    218              outPath->moveTo(0.0, 0.0);
    219          }},
    220 
    221         // Empty test data
    222         {"",
    223          {
    224                  // Verbs
    225                  {},
    226                  {},
    227                  {},
    228          },
    229          [](SkPath* outPath) {}}
    230 
    231 };
    232 
    233 struct StringPath {
    234     const char* stringPath;
    235     bool isValid;
    236 };
    237 
    238 const StringPath sStringPaths[] = {
    239         {"3e...3", false},     // Not starting with a verb and ill-formatted float
    240         {"L.M.F.A.O", false},  // No floats following verbs
    241         {"m 1 1", true},       // Valid path data
    242         {"\n \t   z", true},   // Valid path data with leading spaces
    243         {"1-2e34567", false},  // Not starting with a verb and ill-formatted float
    244         {"f 4 5", false},      // Invalid verb
    245         {"\r      ", false},   // Empty string
    246         {"L1,0 L1,1 L0,1 z M1000", false}    // Not enough floats following verb M.
    247 };
    248 
    249 static bool hasSameVerbs(const PathData& from, const PathData& to) {
    250     return from.verbs == to.verbs && from.verbSizes == to.verbSizes;
    251 }
    252 
    253 TEST(PathParser, parseStringForData) {
    254     for (TestData testData : sTestDataSet) {
    255         PathParser::ParseResult result;
    256         // Test generated path data against the given data.
    257         PathData pathData;
    258         size_t length = strlen(testData.pathString);
    259         PathParser::getPathDataFromAsciiString(&pathData, &result, testData.pathString, length);
    260         EXPECT_EQ(testData.pathData, pathData);
    261     }
    262 
    263     for (StringPath stringPath : sStringPaths) {
    264         PathParser::ParseResult result;
    265         PathData pathData;
    266         SkPath skPath;
    267         PathParser::getPathDataFromAsciiString(&pathData, &result, stringPath.stringPath,
    268                                                strlen(stringPath.stringPath));
    269         EXPECT_EQ(stringPath.isValid, !result.failureOccurred);
    270     }
    271 }
    272 
    273 TEST(VectorDrawableUtils, createSkPathFromPathData) {
    274     for (TestData testData : sTestDataSet) {
    275         SkPath expectedPath;
    276         testData.skPathLamda(&expectedPath);
    277         SkPath actualPath;
    278         VectorDrawableUtils::verbsToPath(&actualPath, testData.pathData);
    279         EXPECT_EQ(expectedPath, actualPath);
    280     }
    281 }
    282 
    283 TEST(PathParser, parseAsciiStringForSkPath) {
    284     for (TestData testData : sTestDataSet) {
    285         PathParser::ParseResult result;
    286         size_t length = strlen(testData.pathString);
    287         // Check the return value as well as the SkPath generated.
    288         SkPath actualPath;
    289         PathParser::parseAsciiStringForSkPath(&actualPath, &result, testData.pathString, length);
    290         bool hasValidData = !result.failureOccurred;
    291         EXPECT_EQ(hasValidData, testData.pathData.verbs.size() > 0);
    292         SkPath expectedPath;
    293         testData.skPathLamda(&expectedPath);
    294         EXPECT_EQ(expectedPath, actualPath);
    295     }
    296 
    297     for (StringPath stringPath : sStringPaths) {
    298         PathParser::ParseResult result;
    299         SkPath skPath;
    300         PathParser::parseAsciiStringForSkPath(&skPath, &result, stringPath.stringPath,
    301                                               strlen(stringPath.stringPath));
    302         EXPECT_EQ(stringPath.isValid, !result.failureOccurred);
    303     }
    304 }
    305 
    306 TEST(VectorDrawableUtils, morphPathData) {
    307     for (TestData fromData : sTestDataSet) {
    308         for (TestData toData : sTestDataSet) {
    309             bool canMorph = VectorDrawableUtils::canMorph(fromData.pathData, toData.pathData);
    310             if (fromData.pathData == toData.pathData) {
    311                 EXPECT_TRUE(canMorph);
    312             } else {
    313                 bool expectedToMorph = hasSameVerbs(fromData.pathData, toData.pathData);
    314                 EXPECT_EQ(expectedToMorph, canMorph);
    315             }
    316         }
    317     }
    318 }
    319 
    320 TEST(VectorDrawableUtils, interpolatePathData) {
    321     // Interpolate path data with itself and every other path data
    322     for (TestData fromData : sTestDataSet) {
    323         for (TestData toData : sTestDataSet) {
    324             PathData outData;
    325             bool success = VectorDrawableUtils::interpolatePathData(&outData, fromData.pathData,
    326                                                                     toData.pathData, 0.5);
    327             bool expectedToMorph = hasSameVerbs(fromData.pathData, toData.pathData);
    328             EXPECT_EQ(expectedToMorph, success);
    329         }
    330     }
    331 
    332     float fractions[] = {0, 0.00001, 0.28, 0.5, 0.7777, 0.9999999, 1};
    333     // Now try to interpolate with a slightly modified version of self and expect success
    334     for (TestData fromData : sTestDataSet) {
    335         PathData toPathData = fromData.pathData;
    336         for (size_t i = 0; i < toPathData.points.size(); i++) {
    337             toPathData.points[i]++;
    338         }
    339         const PathData& fromPathData = fromData.pathData;
    340         PathData outData;
    341         // Interpolate the two path data with different fractions
    342         for (float fraction : fractions) {
    343             bool success = VectorDrawableUtils::interpolatePathData(&outData, fromPathData,
    344                                                                     toPathData, fraction);
    345             EXPECT_TRUE(success);
    346             for (size_t i = 0; i < outData.points.size(); i++) {
    347                 float expectedResult =
    348                         fromPathData.points[i] * (1.0 - fraction) + toPathData.points[i] * fraction;
    349                 EXPECT_TRUE(MathUtils::areEqual(expectedResult, outData.points[i]));
    350             }
    351         }
    352     }
    353 }
    354 
    355 TEST(VectorDrawable, groupProperties) {
    356     // TODO: Also need to test property sync and dirty flag when properties change.
    357     VectorDrawable::Group group;
    358     VectorDrawable::Group::GroupProperties* properties = group.mutateProperties();
    359     // Test default values, change values through setters and verify the change through getters.
    360     EXPECT_EQ(0.0f, properties->getTranslateX());
    361     properties->setTranslateX(1.0f);
    362     EXPECT_EQ(1.0f, properties->getTranslateX());
    363 
    364     EXPECT_EQ(0.0f, properties->getTranslateY());
    365     properties->setTranslateY(1.0f);
    366     EXPECT_EQ(1.0f, properties->getTranslateY());
    367 
    368     EXPECT_EQ(0.0f, properties->getRotation());
    369     properties->setRotation(1.0f);
    370     EXPECT_EQ(1.0f, properties->getRotation());
    371 
    372     EXPECT_EQ(1.0f, properties->getScaleX());
    373     properties->setScaleX(0.0f);
    374     EXPECT_EQ(0.0f, properties->getScaleX());
    375 
    376     EXPECT_EQ(1.0f, properties->getScaleY());
    377     properties->setScaleY(0.0f);
    378     EXPECT_EQ(0.0f, properties->getScaleY());
    379 
    380     EXPECT_EQ(0.0f, properties->getPivotX());
    381     properties->setPivotX(1.0f);
    382     EXPECT_EQ(1.0f, properties->getPivotX());
    383 
    384     EXPECT_EQ(0.0f, properties->getPivotY());
    385     properties->setPivotY(1.0f);
    386     EXPECT_EQ(1.0f, properties->getPivotY());
    387 }
    388 
    389 TEST(VectorDrawable, drawPathWithoutIncrementingShaderRefCount) {
    390     VectorDrawable::FullPath path("m1 1", 4);
    391     SkBitmap bitmap;
    392     bitmap.allocN32Pixels(5, 5, false);
    393     SkCanvas canvas(bitmap);
    394 
    395     sk_sp<SkShader> shader = SkShader::MakeColorShader(SK_ColorBLACK);
    396     // Initial ref count is 1
    397     EXPECT_TRUE(shader->unique());
    398 
    399     // Setting the fill gradient increments the ref count of the shader by 1
    400     path.mutateStagingProperties()->setFillGradient(shader.get());
    401     EXPECT_FALSE(shader->unique());
    402     path.draw(&canvas, true);
    403     // Resetting the fill gradient decrements the ref count of the shader by 1
    404     path.mutateStagingProperties()->setFillGradient(nullptr);
    405 
    406     EXPECT_TRUE(shader->unique());
    407 }
    408 
    409 };  // namespace uirenderer
    410 };  // namespace android
    411