blob: 2179f146115ad3c5c51dc4ec89f9ca4c7bf2116c [file] [log] [blame]
Doris Liu30bcf692015-11-04 14:56:24 -08001/*
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
Doris Liu1e67f082015-11-12 15:57:45 -080021#include <errno.h>
Doris Liu30bcf692015-11-04 14:56:24 -080022#include <utils/Log.h>
23#include <sstream>
24#include <stdlib.h>
25#include <string>
26#include <vector>
27
28namespace android {
29namespace uirenderer {
30
31static 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 */
55static 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
Doris Liu1e67f082015-11-12 15:57:45 -0800101static 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
Doris Liu30bcf692015-11-04 14:56:24 -0800118/**
Doris Liu1e67f082015-11-12 15:57:45 -0800119 * 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 */
124static void getFloats(std::vector<float>* outPoints, PathParser::ParseResult* result,
125 const char* pathStr, int start, int end) {
Doris Liu30bcf692015-11-04 14:56:24 -0800126
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) {
Doris Liu1e67f082015-11-12 15:57:45 -0800141 float currentValue = parseFloat(result, &pathStr[startPosition],
142 end - startPosition);
143 if (result->failureOccurred) {
144 return;
145 }
146 outPoints->push_back(currentValue);
Doris Liu30bcf692015-11-04 14:56:24 -0800147 }
148
149 if (endWithNegOrDot) {
150 // Keep the '-' or '.' sign with next number.
151 startPosition = endPosition;
152 } else {
153 startPosition = endPosition + 1;
154 }
155 }
Doris Liu1e67f082015-11-12 15:57:45 -0800156 return;
Doris Liu30bcf692015-11-04 14:56:24 -0800157}
158
Doris Liu0a1a5162016-04-07 15:03:11 -0700159bool 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
Doris Liub35da392016-04-12 11:06:23 -0700165void PathParser::getPathDataFromAsciiString(PathData* data, ParseResult* result,
Doris Liu1e67f082015-11-12 15:57:45 -0800166 const char* pathStr, size_t strLen) {
Doris Liu30bcf692015-11-04 14:56:24 -0800167 if (pathStr == NULL) {
Doris Liu1e67f082015-11-12 15:57:45 -0800168 result->failureOccurred = true;
169 result->failureMessage = "Path string cannot be NULL.";
Doris Liu30bcf692015-11-04 14:56:24 -0800170 return;
171 }
172
173 size_t start = 0;
Doris Liub35da392016-04-12 11:06:23 -0700174 // 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;
Doris Liu30bcf692015-11-04 14:56:24 -0800184
185 while (end < strLen) {
186 end = nextStart(pathStr, strLen, end);
187 std::vector<float> points;
Doris Liu1e67f082015-11-12 15:57:45 -0800188 getFloats(&points, result, pathStr, start, end);
Doris Liu0a1a5162016-04-07 15:03:11 -0700189 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.
Doris Liu1e67f082015-11-12 15:57:45 -0800195 if (result->failureOccurred) {
196 return;
197 }
Doris Liu30bcf692015-11-04 14:56:24 -0800198 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
Doris Liu1e67f082015-11-12 15:57:45 -0800205 if ((end - start) == 1 && start < strLen) {
Doris Liu0a1a5162016-04-07 15:03:11 -0700206 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 }
Doris Liu30bcf692015-11-04 14:56:24 -0800212 data->verbs.push_back(pathStr[start]);
213 data->verbSizes.push_back(0);
214 }
Doris Liu30bcf692015-11-04 14:56:24 -0800215}
216
217void 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];
Doris Liu1e67f082015-11-12 15:57:45 -0800223 os << ", verb size: " << data.verbSizes[i];
Doris Liu30bcf692015-11-04 14:56:24 -0800224 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
Doris Liub35da392016-04-12 11:06:23 -0700238void PathParser::parseAsciiStringForSkPath(SkPath* skPath, ParseResult* result, const char* pathStr, size_t strLen) {
Doris Liu30bcf692015-11-04 14:56:24 -0800239 PathData pathData;
Doris Liub35da392016-04-12 11:06:23 -0700240 getPathDataFromAsciiString(&pathData, result, pathStr, strLen);
Doris Liu1e67f082015-11-12 15:57:45 -0800241 if (result->failureOccurred) {
242 return;
243 }
Doris Liucdd23f92015-11-11 14:31:13 -0800244 // Check if there is valid data coming out of parsing the string.
245 if (pathData.verbs.size() == 0) {
Doris Liu1e67f082015-11-12 15:57:45 -0800246 result->failureOccurred = true;
Doris Liu0a1a5162016-04-07 15:03:11 -0700247 result->failureMessage = "No verbs found in the string for pathData: ";
248 result->failureMessage += pathStr;
Doris Liu1e67f082015-11-12 15:57:45 -0800249 return;
Doris Liucdd23f92015-11-11 14:31:13 -0800250 }
Doris Liu804618d2015-11-16 22:48:34 -0800251 VectorDrawableUtils::verbsToPath(skPath, pathData);
Doris Liu1e67f082015-11-12 15:57:45 -0800252 return;
Doris Liu30bcf692015-11-04 14:56:24 -0800253}
254
255}; // namespace uirenderer
256}; //namespace android