blob: 61b9d2114a624f3ea8862dd18e310ec952e18186 [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
21#include <utils/Log.h>
22#include <sstream>
23#include <stdlib.h>
24#include <string>
25#include <vector>
26
27namespace android {
28namespace uirenderer {
29
30static size_t nextStart(const char* s, size_t length, size_t startIndex) {
31 size_t index = startIndex;
32 while (index < length) {
33 char c = s[index];
34 // Note that 'e' or 'E' are not valid path commands, but could be
35 // used for floating point numbers' scientific notation.
36 // Therefore, when searching for next command, we should ignore 'e'
37 // and 'E'.
38 if ((((c - 'A') * (c - 'Z') <= 0) || ((c - 'a') * (c - 'z') <= 0))
39 && c != 'e' && c != 'E') {
40 return index;
41 }
42 index++;
43 }
44 return index;
45}
46
47/**
48 * Calculate the position of the next comma or space or negative sign
49 * @param s the string to search
50 * @param start the position to start searching
51 * @param result the result of the extraction, including the position of the
52 * the starting position of next number, whether it is ending with a '-'.
53 */
54static void extract(int* outEndPosition, bool* outEndWithNegOrDot, const char* s, int start, int end) {
55 // Now looking for ' ', ',', '.' or '-' from the start.
56 int currentIndex = start;
57 bool foundSeparator = false;
58 *outEndWithNegOrDot = false;
59 bool secondDot = false;
60 bool isExponential = false;
61 for (; currentIndex < end; currentIndex++) {
62 bool isPrevExponential = isExponential;
63 isExponential = false;
64 char currentChar = s[currentIndex];
65 switch (currentChar) {
66 case ' ':
67 case ',':
68 foundSeparator = true;
69 break;
70 case '-':
71 // The negative sign following a 'e' or 'E' is not a separator.
72 if (currentIndex != start && !isPrevExponential) {
73 foundSeparator = true;
74 *outEndWithNegOrDot = true;
75 }
76 break;
77 case '.':
78 if (!secondDot) {
79 secondDot = true;
80 } else {
81 // This is the second dot, and it is considered as a separator.
82 foundSeparator = true;
83 *outEndWithNegOrDot = true;
84 }
85 break;
86 case 'e':
87 case 'E':
88 isExponential = true;
89 break;
90 }
91 if (foundSeparator) {
92 break;
93 }
94 }
95 // In the case where nothing is found, we put the end position to the end of
96 // our extract range. Otherwise, end position will be where separator is found.
97 *outEndPosition = currentIndex;
98}
99
100/**
101* Parse the floats in the string.
102* This is an optimized version of parseFloat(s.split(",|\\s"));
103*
104* @param s the string containing a command and list of floats
105* @return array of floats
106*/
107static void getFloats(std::vector<float>* outPoints, const char* pathStr, int start, int end) {
108
109 if (pathStr[start] == 'z' || pathStr[start] == 'Z') {
110 return;
111 }
112 int startPosition = start + 1;
113 int endPosition = start;
114
115 // The startPosition should always be the first character of the
116 // current number, and endPosition is the character after the current
117 // number.
118 while (startPosition < end) {
119 bool endWithNegOrDot;
120 extract(&endPosition, &endWithNegOrDot, pathStr, startPosition, end);
121
122 if (startPosition < endPosition) {
123 outPoints->push_back(strtof(&pathStr[startPosition], NULL));
124 }
125
126 if (endWithNegOrDot) {
127 // Keep the '-' or '.' sign with next number.
128 startPosition = endPosition;
129 } else {
130 startPosition = endPosition + 1;
131 }
132 }
133}
134
135void PathParser::getPathDataFromString(PathData* data, const char* pathStr, size_t strLen) {
136 if (pathStr == NULL) {
137 return;
138 }
139
140 size_t start = 0;
141 size_t end = 1;
142
143 while (end < strLen) {
144 end = nextStart(pathStr, strLen, end);
145 std::vector<float> points;
146 getFloats(&points, pathStr, start, end);
147 data->verbs.push_back(pathStr[start]);
148 data->verbSizes.push_back(points.size());
149 data->points.insert(data->points.end(), points.begin(), points.end());
150 start = end;
151 end++;
152 }
153
154 if ((end - start) == 1 && pathStr[start] != '\0') {
155 data->verbs.push_back(pathStr[start]);
156 data->verbSizes.push_back(0);
157 }
158
159 int i = 0;
160 while(pathStr[i] != '\0') {
161 i++;
162 }
163
164}
165
166void PathParser::dump(const PathData& data) {
167 // Print out the path data.
168 size_t start = 0;
169 for (size_t i = 0; i < data.verbs.size(); i++) {
170 std::ostringstream os;
171 os << data.verbs[i];
172 for (size_t j = 0; j < data.verbSizes[i]; j++) {
173 os << " " << data.points[start + j];
174 }
175 start += data.verbSizes[i];
176 ALOGD("%s", os.str().c_str());
177 }
178
179 std::ostringstream os;
180 for (size_t i = 0; i < data.points.size(); i++) {
181 os << data.points[i] << ", ";
182 }
183 ALOGD("points are : %s", os.str().c_str());
184}
185
Doris Liucdd23f92015-11-11 14:31:13 -0800186bool PathParser::parseStringForSkPath(SkPath* skPath, const char* pathStr, size_t strLen) {
Doris Liu30bcf692015-11-04 14:56:24 -0800187 PathData pathData;
188 getPathDataFromString(&pathData, pathStr, strLen);
Doris Liucdd23f92015-11-11 14:31:13 -0800189
190 // Check if there is valid data coming out of parsing the string.
191 if (pathData.verbs.size() == 0) {
192 return false;
193 }
Doris Liu30bcf692015-11-04 14:56:24 -0800194 VectorDrawablePath::verbsToPath(skPath, &pathData);
Doris Liucdd23f92015-11-11 14:31:13 -0800195 return true;
Doris Liu30bcf692015-11-04 14:56:24 -0800196}
197
198}; // namespace uirenderer
199}; //namespace android