diff options
author | Doris Liu <tianliu@google.com> | 2015-11-04 14:56:24 -0800 |
---|---|---|
committer | Doris Liu <tianliu@google.com> | 2015-11-10 15:46:06 -0800 |
commit | 30bcf69df9cfae40b621335958656cb0e4afd7d5 (patch) | |
tree | d4cc05d731a54d19775a0fd5245d0764a5156bcf /libs/hwui/PathParser.cpp | |
parent | 429c5b93ff66e82fa3fd65475489fde133c66002 (diff) |
VectorDrawable native rendering - Step 1 of MANY
Implement path parsing from string to skia path in native. The parsing
contains two main stages:
1) Parse string into a list of nodes that contains one operation (such
as move) and a vector of floats as params for that operation.
2) Interpret the operations defined in the nodes into SkPath operations,
and create a skia path
Also provided unit test for parsing a string path into a list of nodes,
and then to a skia path.
Change-Id: I0ce13df5e3bb90987dcdc80fe8b039af175ad2e2
Diffstat (limited to 'libs/hwui/PathParser.cpp')
-rw-r--r-- | libs/hwui/PathParser.cpp | 193 |
1 files changed, 193 insertions, 0 deletions
diff --git a/libs/hwui/PathParser.cpp b/libs/hwui/PathParser.cpp new file mode 100644 index 000000000000..e8ed8a14100a --- /dev/null +++ b/libs/hwui/PathParser.cpp @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "PathParser.h" + +#include "jni.h" + +#include <utils/Log.h> +#include <sstream> +#include <stdlib.h> +#include <string> +#include <vector> + +namespace android { +namespace uirenderer { + +static size_t nextStart(const char* s, size_t length, size_t startIndex) { + size_t index = startIndex; + while (index < length) { + char c = s[index]; + // Note that 'e' or 'E' are not valid path commands, but could be + // used for floating point numbers' scientific notation. + // Therefore, when searching for next command, we should ignore 'e' + // and 'E'. + if ((((c - 'A') * (c - 'Z') <= 0) || ((c - 'a') * (c - 'z') <= 0)) + && c != 'e' && c != 'E') { + return index; + } + index++; + } + return index; +} + +/** + * Calculate the position of the next comma or space or negative sign + * @param s the string to search + * @param start the position to start searching + * @param result the result of the extraction, including the position of the + * the starting position of next number, whether it is ending with a '-'. + */ +static void extract(int* outEndPosition, bool* outEndWithNegOrDot, const char* s, int start, int end) { + // Now looking for ' ', ',', '.' or '-' from the start. + int currentIndex = start; + bool foundSeparator = false; + *outEndWithNegOrDot = false; + bool secondDot = false; + bool isExponential = false; + for (; currentIndex < end; currentIndex++) { + bool isPrevExponential = isExponential; + isExponential = false; + char currentChar = s[currentIndex]; + switch (currentChar) { + case ' ': + case ',': + foundSeparator = true; + break; + case '-': + // The negative sign following a 'e' or 'E' is not a separator. + if (currentIndex != start && !isPrevExponential) { + foundSeparator = true; + *outEndWithNegOrDot = true; + } + break; + case '.': + if (!secondDot) { + secondDot = true; + } else { + // This is the second dot, and it is considered as a separator. + foundSeparator = true; + *outEndWithNegOrDot = true; + } + break; + case 'e': + case 'E': + isExponential = true; + break; + } + if (foundSeparator) { + break; + } + } + // In the case where nothing is found, we put the end position to the end of + // our extract range. Otherwise, end position will be where separator is found. + *outEndPosition = currentIndex; +} + +/** +* Parse the floats in the string. +* This is an optimized version of parseFloat(s.split(",|\\s")); +* +* @param s the string containing a command and list of floats +* @return array of floats +*/ +static void getFloats(std::vector<float>* outPoints, const char* pathStr, int start, int end) { + + if (pathStr[start] == 'z' || pathStr[start] == 'Z') { + return; + } + int startPosition = start + 1; + int endPosition = start; + + // The startPosition should always be the first character of the + // current number, and endPosition is the character after the current + // number. + while (startPosition < end) { + bool endWithNegOrDot; + extract(&endPosition, &endWithNegOrDot, pathStr, startPosition, end); + + if (startPosition < endPosition) { + outPoints->push_back(strtof(&pathStr[startPosition], NULL)); + } + + if (endWithNegOrDot) { + // Keep the '-' or '.' sign with next number. + startPosition = endPosition; + } else { + startPosition = endPosition + 1; + } + } +} + +void PathParser::getPathDataFromString(PathData* data, const char* pathStr, size_t strLen) { + if (pathStr == NULL) { + return; + } + + size_t start = 0; + size_t end = 1; + + while (end < strLen) { + end = nextStart(pathStr, strLen, end); + std::vector<float> points; + getFloats(&points, pathStr, start, end); + data->verbs.push_back(pathStr[start]); + data->verbSizes.push_back(points.size()); + data->points.insert(data->points.end(), points.begin(), points.end()); + start = end; + end++; + } + + if ((end - start) == 1 && pathStr[start] != '\0') { + data->verbs.push_back(pathStr[start]); + data->verbSizes.push_back(0); + } + + int i = 0; + while(pathStr[i] != '\0') { + i++; + } + +} + +void PathParser::dump(const PathData& data) { + // Print out the path data. + size_t start = 0; + for (size_t i = 0; i < data.verbs.size(); i++) { + std::ostringstream os; + os << data.verbs[i]; + for (size_t j = 0; j < data.verbSizes[i]; j++) { + os << " " << data.points[start + j]; + } + start += data.verbSizes[i]; + ALOGD("%s", os.str().c_str()); + } + + std::ostringstream os; + for (size_t i = 0; i < data.points.size(); i++) { + os << data.points[i] << ", "; + } + ALOGD("points are : %s", os.str().c_str()); +} + +void PathParser::parseStringForSkPath(SkPath* skPath, const char* pathStr, size_t strLen) { + PathData pathData; + getPathDataFromString(&pathData, pathStr, strLen); + VectorDrawablePath::verbsToPath(skPath, &pathData); +} + +}; // namespace uirenderer +}; //namespace android |