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 <stdlib.h>
     23 #include <utils/Log.h>
     24 #include <sstream>
     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)) && c != 'e' &&
     40             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,
     56                     int end) {
     57     // Now looking for ' ', ',', '.' or '-' from the start.
     58     int currentIndex = start;
     59     bool foundSeparator = false;
     60     *outEndWithNegOrDot = false;
     61     bool secondDot = false;
     62     bool isExponential = false;
     63     for (; currentIndex < end; currentIndex++) {
     64         bool isPrevExponential = isExponential;
     65         isExponential = false;
     66         char currentChar = s[currentIndex];
     67         switch (currentChar) {
     68             case ' ':
     69             case ',':
     70                 foundSeparator = true;
     71                 break;
     72             case '-':
     73                 // The negative sign following a 'e' or 'E' is not a separator.
     74                 if (currentIndex != start && !isPrevExponential) {
     75                     foundSeparator = true;
     76                     *outEndWithNegOrDot = true;
     77                 }
     78                 break;
     79             case '.':
     80                 if (!secondDot) {
     81                     secondDot = true;
     82                 } else {
     83                     // This is the second dot, and it is considered as a separator.
     84                     foundSeparator = true;
     85                     *outEndWithNegOrDot = true;
     86                 }
     87                 break;
     88             case 'e':
     89             case 'E':
     90                 isExponential = true;
     91                 break;
     92         }
     93         if (foundSeparator) {
     94             break;
     95         }
     96     }
     97     // In the case where nothing is found, we put the end position to the end of
     98     // our extract range. Otherwise, end position will be where separator is found.
     99     *outEndPosition = currentIndex;
    100 }
    101 
    102 static float parseFloat(PathParser::ParseResult* result, const char* startPtr,
    103                         size_t expectedLength) {
    104     char* endPtr = NULL;
    105     float currentValue = strtof(startPtr, &endPtr);
    106     if ((currentValue == HUGE_VALF || currentValue == -HUGE_VALF) && errno == ERANGE) {
    107         result->failureOccurred = true;
    108         result->failureMessage = "Float out of range:  ";
    109         result->failureMessage.append(startPtr, expectedLength);
    110     }
    111     if (currentValue == 0 && endPtr == startPtr) {
    112         // No conversion is done.
    113         result->failureOccurred = true;
    114         result->failureMessage = "Float format error when parsing: ";
    115         result->failureMessage.append(startPtr, expectedLength);
    116     }
    117     return currentValue;
    118 }
    119 
    120 /**
    121  * Parse the floats in the string.
    122  *
    123  * @param s the string containing a command and list of floats
    124  * @return true on success
    125  */
    126 static void getFloats(std::vector<float>* outPoints, PathParser::ParseResult* result,
    127                       const char* pathStr, int start, int end) {
    128     if (pathStr[start] == 'z' || pathStr[start] == 'Z') {
    129         return;
    130     }
    131     int startPosition = start + 1;
    132     int endPosition = start;
    133 
    134     // The startPosition should always be the first character of the
    135     // current number, and endPosition is the character after the current
    136     // number.
    137     while (startPosition < end) {
    138         bool endWithNegOrDot;
    139         extract(&endPosition, &endWithNegOrDot, pathStr, startPosition, end);
    140 
    141         if (startPosition < endPosition) {
    142             float currentValue = parseFloat(result, &pathStr[startPosition], 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 void PathParser::validateVerbAndPoints(char verb, size_t points, PathParser::ParseResult* result) {
    160     size_t numberOfPointsExpected = -1;
    161     switch (verb) {
    162         case 'z':
    163         case 'Z':
    164             numberOfPointsExpected = 0;
    165             break;
    166         case 'm':
    167         case 'l':
    168         case 't':
    169         case 'M':
    170         case 'L':
    171         case 'T':
    172             numberOfPointsExpected = 2;
    173             break;
    174         case 'h':
    175         case 'v':
    176         case 'H':
    177         case 'V':
    178             numberOfPointsExpected = 1;
    179             break;
    180         case 'c':
    181         case 'C':
    182             numberOfPointsExpected = 6;
    183             break;
    184         case 's':
    185         case 'q':
    186         case 'S':
    187         case 'Q':
    188             numberOfPointsExpected = 4;
    189             break;
    190         case 'a':
    191         case 'A':
    192             numberOfPointsExpected = 7;
    193             break;
    194         default:
    195             result->failureOccurred = true;
    196             result->failureMessage += verb;
    197             result->failureMessage += " is not a valid verb. ";
    198             return;
    199     }
    200     if (numberOfPointsExpected == 0 && points == 0) {
    201         return;
    202     }
    203     if (numberOfPointsExpected > 0 && points % numberOfPointsExpected == 0) {
    204         return;
    205     }
    206 
    207     result->failureOccurred = true;
    208     result->failureMessage += verb;
    209     result->failureMessage += " needs to be followed by ";
    210     if (numberOfPointsExpected > 0) {
    211         result->failureMessage += "a multiple of ";
    212     }
    213     result->failureMessage += std::to_string(numberOfPointsExpected) + " floats. However, " +
    214                               std::to_string(points) + " float(s) are found. ";
    215 }
    216 
    217 void PathParser::getPathDataFromAsciiString(PathData* data, ParseResult* result,
    218                                             const char* pathStr, size_t strLen) {
    219     if (pathStr == NULL) {
    220         result->failureOccurred = true;
    221         result->failureMessage = "Path string cannot be NULL.";
    222         return;
    223     }
    224 
    225     size_t start = 0;
    226     // Skip leading spaces.
    227     while (isspace(pathStr[start]) && start < strLen) {
    228         start++;
    229     }
    230     if (start == strLen) {
    231         result->failureOccurred = true;
    232         result->failureMessage = "Path string cannot be empty.";
    233         return;
    234     }
    235     size_t end = start + 1;
    236 
    237     while (end < strLen) {
    238         end = nextStart(pathStr, strLen, end);
    239         std::vector<float> points;
    240         getFloats(&points, result, pathStr, start, end);
    241         validateVerbAndPoints(pathStr[start], points.size(), result);
    242         if (result->failureOccurred) {
    243             // If either verb or points is not valid, return immediately.
    244             result->failureMessage += "Failure occurred at position " + std::to_string(start) +
    245                                       " of path: " + pathStr;
    246             return;
    247         }
    248         data->verbs.push_back(pathStr[start]);
    249         data->verbSizes.push_back(points.size());
    250         data->points.insert(data->points.end(), points.begin(), points.end());
    251         start = end;
    252         end++;
    253     }
    254 
    255     if ((end - start) == 1 && start < strLen) {
    256         validateVerbAndPoints(pathStr[start], 0, result);
    257         if (result->failureOccurred) {
    258             // If either verb or points is not valid, return immediately.
    259             result->failureMessage += "Failure occurred at position " + std::to_string(start) +
    260                                       " of path: " + pathStr;
    261             return;
    262         }
    263         data->verbs.push_back(pathStr[start]);
    264         data->verbSizes.push_back(0);
    265     }
    266 }
    267 
    268 void PathParser::dump(const PathData& data) {
    269     // Print out the path data.
    270     size_t start = 0;
    271     for (size_t i = 0; i < data.verbs.size(); i++) {
    272         std::ostringstream os;
    273         os << data.verbs[i];
    274         os << ", verb size: " << data.verbSizes[i];
    275         for (size_t j = 0; j < data.verbSizes[i]; j++) {
    276             os << " " << data.points[start + j];
    277         }
    278         start += data.verbSizes[i];
    279         ALOGD("%s", os.str().c_str());
    280     }
    281 
    282     std::ostringstream os;
    283     for (size_t i = 0; i < data.points.size(); i++) {
    284         os << data.points[i] << ", ";
    285     }
    286     ALOGD("points are : %s", os.str().c_str());
    287 }
    288 
    289 void PathParser::parseAsciiStringForSkPath(SkPath* skPath, ParseResult* result, const char* pathStr,
    290                                            size_t strLen) {
    291     PathData pathData;
    292     getPathDataFromAsciiString(&pathData, result, pathStr, strLen);
    293     if (result->failureOccurred) {
    294         return;
    295     }
    296     // Check if there is valid data coming out of parsing the string.
    297     if (pathData.verbs.size() == 0) {
    298         result->failureOccurred = true;
    299         result->failureMessage = "No verbs found in the string for pathData: ";
    300         result->failureMessage += pathStr;
    301         return;
    302     }
    303     VectorDrawableUtils::verbsToPath(skPath, pathData);
    304     return;
    305 }
    306 
    307 }  // namespace uirenderer
    308 }  // namespace android
    309