Home | History | Annotate | Download | only in hwui
      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 
     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  */
     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 
    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  */
    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 
    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 
    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 
    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 
    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