• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "PathParser.h"
18 
19 #include "jni.h"
20 
21 #include <errno.h>
22 #include <utils/Log.h>
23 #include <sstream>
24 #include <stdlib.h>
25 #include <string>
26 #include <vector>
27 
28 namespace android {
29 namespace uirenderer {
30 
nextStart(const char * s,size_t length,size_t startIndex)31 static size_t nextStart(const char* s, size_t length, size_t startIndex) {
32     size_t index = startIndex;
33     while (index < length) {
34         char c = s[index];
35         // Note that 'e' or 'E' are not valid path commands, but could be
36         // used for floating point numbers' scientific notation.
37         // Therefore, when searching for next command, we should ignore 'e'
38         // and 'E'.
39         if ((((c - 'A') * (c - 'Z') <= 0) || ((c - 'a') * (c - 'z') <= 0))
40                 && c != 'e' && c != 'E') {
41             return index;
42         }
43         index++;
44     }
45     return index;
46 }
47 
48 /**
49  * Calculate the position of the next comma or space or negative sign
50  * @param s the string to search
51  * @param start the position to start searching
52  * @param result the result of the extraction, including the position of the
53  * the starting position of next number, whether it is ending with a '-'.
54  */
extract(int * outEndPosition,bool * outEndWithNegOrDot,const char * s,int start,int end)55 static void extract(int* outEndPosition, bool* outEndWithNegOrDot, const char* s, int start, int end) {
56     // Now looking for ' ', ',', '.' or '-' from the start.
57     int currentIndex = start;
58     bool foundSeparator = false;
59     *outEndWithNegOrDot = false;
60     bool secondDot = false;
61     bool isExponential = false;
62     for (; currentIndex < end; currentIndex++) {
63         bool isPrevExponential = isExponential;
64         isExponential = false;
65         char currentChar = s[currentIndex];
66         switch (currentChar) {
67         case ' ':
68         case ',':
69             foundSeparator = true;
70             break;
71         case '-':
72             // The negative sign following a 'e' or 'E' is not a separator.
73             if (currentIndex != start && !isPrevExponential) {
74                 foundSeparator = true;
75                 *outEndWithNegOrDot = true;
76             }
77             break;
78         case '.':
79             if (!secondDot) {
80                 secondDot = true;
81             } else {
82                 // This is the second dot, and it is considered as a separator.
83                 foundSeparator = true;
84                 *outEndWithNegOrDot = true;
85             }
86             break;
87         case 'e':
88         case 'E':
89             isExponential = true;
90             break;
91         }
92         if (foundSeparator) {
93             break;
94         }
95     }
96     // In the case where nothing is found, we put the end position to the end of
97     // our extract range. Otherwise, end position will be where separator is found.
98     *outEndPosition = currentIndex;
99 }
100 
parseFloat(PathParser::ParseResult * result,const char * startPtr,size_t expectedLength)101 static float parseFloat(PathParser::ParseResult* result, const char* startPtr, size_t expectedLength) {
102     char* endPtr = NULL;
103     float currentValue = strtof(startPtr, &endPtr);
104     if ((currentValue == HUGE_VALF || currentValue == -HUGE_VALF) && errno == ERANGE) {
105         result->failureOccurred = true;
106         result->failureMessage = "Float out of range:  ";
107         result->failureMessage.append(startPtr, expectedLength);
108     }
109     if (currentValue == 0 && endPtr == startPtr) {
110         // No conversion is done.
111         result->failureOccurred = true;
112         result->failureMessage = "Float format error when parsing: ";
113         result->failureMessage.append(startPtr, expectedLength);
114     }
115     return currentValue;
116 }
117 
118 /**
119  * Parse the floats in the string.
120  *
121  * @param s the string containing a command and list of floats
122  * @return true on success
123  */
getFloats(std::vector<float> * outPoints,PathParser::ParseResult * result,const char * pathStr,int start,int end)124 static void getFloats(std::vector<float>* outPoints, PathParser::ParseResult* result,
125         const char* pathStr, int start, int end) {
126 
127     if (pathStr[start] == 'z' || pathStr[start] == 'Z') {
128         return;
129     }
130     int startPosition = start + 1;
131     int endPosition = start;
132 
133     // The startPosition should always be the first character of the
134     // current number, and endPosition is the character after the current
135     // number.
136     while (startPosition < end) {
137         bool endWithNegOrDot;
138         extract(&endPosition, &endWithNegOrDot, pathStr, startPosition, end);
139 
140         if (startPosition < endPosition) {
141             float currentValue = parseFloat(result, &pathStr[startPosition],
142                     end - startPosition);
143             if (result->failureOccurred) {
144                 return;
145             }
146             outPoints->push_back(currentValue);
147         }
148 
149         if (endWithNegOrDot) {
150             // Keep the '-' or '.' sign with next number.
151             startPosition = endPosition;
152         } else {
153             startPosition = endPosition + 1;
154         }
155     }
156     return;
157 }
158 
isVerbValid(char verb)159 bool PathParser::isVerbValid(char verb) {
160     verb = tolower(verb);
161     return verb == 'a' || verb == 'c' || verb == 'h' || verb == 'l' || verb == 'm' || verb == 'q'
162             || verb == 's' || verb == 't' || verb == 'v' || verb == 'z';
163 }
164 
getPathDataFromAsciiString(PathData * data,ParseResult * result,const char * pathStr,size_t strLen)165 void PathParser::getPathDataFromAsciiString(PathData* data, ParseResult* result,
166         const char* pathStr, size_t strLen) {
167     if (pathStr == NULL) {
168         result->failureOccurred = true;
169         result->failureMessage = "Path string cannot be NULL.";
170         return;
171     }
172 
173     size_t start = 0;
174     // Skip leading spaces.
175     while (isspace(pathStr[start]) && start < strLen) {
176         start++;
177     }
178     if (start == strLen) {
179         result->failureOccurred = true;
180         result->failureMessage = "Path string cannot be empty.";
181         return;
182     }
183     size_t end = start + 1;
184 
185     while (end < strLen) {
186         end = nextStart(pathStr, strLen, end);
187         std::vector<float> points;
188         getFloats(&points, result, pathStr, start, end);
189         if (!isVerbValid(pathStr[start])) {
190             result->failureOccurred = true;
191             result->failureMessage = "Invalid pathData. Failure occurred at position "
192                     + std::to_string(start) + " of path: " + pathStr;
193         }
194         // If either verb or points is not valid, return immediately.
195         if (result->failureOccurred) {
196             return;
197         }
198         data->verbs.push_back(pathStr[start]);
199         data->verbSizes.push_back(points.size());
200         data->points.insert(data->points.end(), points.begin(), points.end());
201         start = end;
202         end++;
203     }
204 
205     if ((end - start) == 1 && start < strLen) {
206         if (!isVerbValid(pathStr[start])) {
207             result->failureOccurred = true;
208             result->failureMessage = "Invalid pathData. Failure occurred at position "
209                     + std::to_string(start) + " of path: " + pathStr;
210             return;
211         }
212         data->verbs.push_back(pathStr[start]);
213         data->verbSizes.push_back(0);
214     }
215 }
216 
dump(const PathData & data)217 void PathParser::dump(const PathData& data) {
218     // Print out the path data.
219     size_t start = 0;
220     for (size_t i = 0; i < data.verbs.size(); i++) {
221         std::ostringstream os;
222         os << data.verbs[i];
223         os << ", verb size: " << data.verbSizes[i];
224         for (size_t j = 0; j < data.verbSizes[i]; j++) {
225             os << " " << data.points[start + j];
226         }
227         start += data.verbSizes[i];
228         ALOGD("%s", os.str().c_str());
229     }
230 
231     std::ostringstream os;
232     for (size_t i = 0; i < data.points.size(); i++) {
233         os << data.points[i] << ", ";
234     }
235     ALOGD("points are : %s", os.str().c_str());
236 }
237 
parseAsciiStringForSkPath(SkPath * skPath,ParseResult * result,const char * pathStr,size_t strLen)238 void PathParser::parseAsciiStringForSkPath(SkPath* skPath, ParseResult* result, const char* pathStr, size_t strLen) {
239     PathData pathData;
240     getPathDataFromAsciiString(&pathData, result, pathStr, strLen);
241     if (result->failureOccurred) {
242         return;
243     }
244     // Check if there is valid data coming out of parsing the string.
245     if (pathData.verbs.size() == 0) {
246         result->failureOccurred = true;
247         result->failureMessage = "No verbs found in the string for pathData: ";
248         result->failureMessage += pathStr;
249         return;
250     }
251     VectorDrawableUtils::verbsToPath(skPath, pathData);
252     return;
253 }
254 
255 }; // namespace uirenderer
256 }; //namespace android
257