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