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)
    214             + " floats. However, " + std::to_string(points)
    215             + " float(s) are found. ";
    216 }
    217 
    218 void PathParser::getPathDataFromAsciiString(PathData* data, ParseResult* result,
    219                                             const char* pathStr, size_t strLen) {
    220     if (pathStr == NULL) {
    221         result->failureOccurred = true;
    222         result->failureMessage = "Path string cannot be NULL.";
    223         return;
    224     }
    225 
    226     size_t start = 0;
    227     // Skip leading spaces.
    228     while (isspace(pathStr[start]) && start < strLen) {
    229         start++;
    230     }
    231     if (start == strLen) {
    232         result->failureOccurred = true;
    233         result->failureMessage = "Path string cannot be empty.";
    234         return;
    235     }
    236     size_t end = start + 1;
    237 
    238     while (end < strLen) {
    239         end = nextStart(pathStr, strLen, end);
    240         std::vector<float> points;
    241         getFloats(&points, result, pathStr, start, end);
    242         validateVerbAndPoints(pathStr[start], points.size(), result);
    243         if (result->failureOccurred) {
    244             // If either verb or points is not valid, return immediately.
    245             result->failureMessage += "Failure occurred at position " +
    246                                      std::to_string(start) + " of path: " + pathStr;
    247             return;
    248         }
    249         data->verbs.push_back(pathStr[start]);
    250         data->verbSizes.push_back(points.size());
    251         data->points.insert(data->points.end(), points.begin(), points.end());
    252         start = end;
    253         end++;
    254     }
    255 
    256     if ((end - start) == 1 && start < strLen) {
    257         validateVerbAndPoints(pathStr[start], 0, result);
    258         if (result->failureOccurred) {
    259             // If either verb or points is not valid, return immediately.
    260             result->failureMessage += "Failure occurred at position " +
    261                                      std::to_string(start) + " of path: " + pathStr;
    262             return;
    263         }
    264         data->verbs.push_back(pathStr[start]);
    265         data->verbSizes.push_back(0);
    266     }
    267 }
    268 
    269 void PathParser::dump(const PathData& data) {
    270     // Print out the path data.
    271     size_t start = 0;
    272     for (size_t i = 0; i < data.verbs.size(); i++) {
    273         std::ostringstream os;
    274         os << data.verbs[i];
    275         os << ", verb size: " << data.verbSizes[i];
    276         for (size_t j = 0; j < data.verbSizes[i]; j++) {
    277             os << " " << data.points[start + j];
    278         }
    279         start += data.verbSizes[i];
    280         ALOGD("%s", os.str().c_str());
    281     }
    282 
    283     std::ostringstream os;
    284     for (size_t i = 0; i < data.points.size(); i++) {
    285         os << data.points[i] << ", ";
    286     }
    287     ALOGD("points are : %s", os.str().c_str());
    288 }
    289 
    290 void PathParser::parseAsciiStringForSkPath(SkPath* skPath, ParseResult* result, const char* pathStr,
    291                                            size_t strLen) {
    292     PathData pathData;
    293     getPathDataFromAsciiString(&pathData, result, pathStr, strLen);
    294     if (result->failureOccurred) {
    295         return;
    296     }
    297     // Check if there is valid data coming out of parsing the string.
    298     if (pathData.verbs.size() == 0) {
    299         result->failureOccurred = true;
    300         result->failureMessage = "No verbs found in the string for pathData: ";
    301         result->failureMessage += pathStr;
    302         return;
    303     }
    304     VectorDrawableUtils::verbsToPath(skPath, pathData);
    305     return;
    306 }
    307 
    308 };  // namespace uirenderer
    309 };  // namespace android
    310