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 "VectorDrawableUtils.h" 18 19 #include "PathParser.h" 20 21 #include <math.h> 22 #include <utils/Log.h> 23 24 namespace android { 25 namespace uirenderer { 26 27 class PathResolver { 28 public: 29 float currentX = 0; 30 float currentY = 0; 31 float ctrlPointX = 0; 32 float ctrlPointY = 0; 33 float currentSegmentStartX = 0; 34 float currentSegmentStartY = 0; 35 void addCommand(SkPath* outPath, char previousCmd, char cmd, const std::vector<float>* points, 36 size_t start, size_t end); 37 }; 38 39 bool VectorDrawableUtils::canMorph(const PathData& morphFrom, const PathData& morphTo) { 40 if (morphFrom.verbs.size() != morphTo.verbs.size()) { 41 return false; 42 } 43 44 for (unsigned int i = 0; i < morphFrom.verbs.size(); i++) { 45 if (morphFrom.verbs[i] != morphTo.verbs[i] || 46 morphFrom.verbSizes[i] != morphTo.verbSizes[i]) { 47 return false; 48 } 49 } 50 return true; 51 } 52 53 bool VectorDrawableUtils::interpolatePathData(PathData* outData, const PathData& morphFrom, 54 const PathData& morphTo, float fraction) { 55 if (!canMorph(morphFrom, morphTo)) { 56 return false; 57 } 58 interpolatePaths(outData, morphFrom, morphTo, fraction); 59 return true; 60 } 61 62 /** 63 * Convert an array of PathVerb to Path. 64 */ 65 void VectorDrawableUtils::verbsToPath(SkPath* outPath, const PathData& data) { 66 PathResolver resolver; 67 char previousCommand = 'm'; 68 size_t start = 0; 69 outPath->reset(); 70 for (unsigned int i = 0; i < data.verbs.size(); i++) { 71 size_t verbSize = data.verbSizes[i]; 72 resolver.addCommand(outPath, previousCommand, data.verbs[i], &data.points, start, 73 start + verbSize); 74 previousCommand = data.verbs[i]; 75 start += verbSize; 76 } 77 } 78 79 /** 80 * The current PathVerb will be interpolated between the 81 * <code>nodeFrom</code> and <code>nodeTo</code> according to the 82 * <code>fraction</code>. 83 * 84 * @param nodeFrom The start value as a PathVerb. 85 * @param nodeTo The end value as a PathVerb 86 * @param fraction The fraction to interpolate. 87 */ 88 void VectorDrawableUtils::interpolatePaths(PathData* outData, const PathData& from, 89 const PathData& to, float fraction) { 90 outData->points.resize(from.points.size()); 91 outData->verbSizes = from.verbSizes; 92 outData->verbs = from.verbs; 93 94 for (size_t i = 0; i < from.points.size(); i++) { 95 outData->points[i] = from.points[i] * (1 - fraction) + to.points[i] * fraction; 96 } 97 } 98 99 // Use the given verb, and points in the range [start, end) to insert a command into the SkPath. 100 void PathResolver::addCommand(SkPath* outPath, char previousCmd, char cmd, 101 const std::vector<float>* points, size_t start, size_t end) { 102 int incr = 2; 103 float reflectiveCtrlPointX; 104 float reflectiveCtrlPointY; 105 106 switch (cmd) { 107 case 'z': 108 case 'Z': 109 outPath->close(); 110 // Path is closed here, but we need to move the pen to the 111 // closed position. So we cache the segment's starting position, 112 // and restore it here. 113 currentX = currentSegmentStartX; 114 currentY = currentSegmentStartY; 115 ctrlPointX = currentSegmentStartX; 116 ctrlPointY = currentSegmentStartY; 117 outPath->moveTo(currentX, currentY); 118 break; 119 case 'm': 120 case 'M': 121 case 'l': 122 case 'L': 123 case 't': 124 case 'T': 125 incr = 2; 126 break; 127 case 'h': 128 case 'H': 129 case 'v': 130 case 'V': 131 incr = 1; 132 break; 133 case 'c': 134 case 'C': 135 incr = 6; 136 break; 137 case 's': 138 case 'S': 139 case 'q': 140 case 'Q': 141 incr = 4; 142 break; 143 case 'a': 144 case 'A': 145 incr = 7; 146 break; 147 } 148 149 for (unsigned int k = start; k < end; k += incr) { 150 switch (cmd) { 151 case 'm': // moveto - Start a new sub-path (relative) 152 currentX += points->at(k + 0); 153 currentY += points->at(k + 1); 154 if (k > start) { 155 // According to the spec, if a moveto is followed by multiple 156 // pairs of coordinates, the subsequent pairs are treated as 157 // implicit lineto commands. 158 outPath->rLineTo(points->at(k + 0), points->at(k + 1)); 159 } else { 160 outPath->rMoveTo(points->at(k + 0), points->at(k + 1)); 161 currentSegmentStartX = currentX; 162 currentSegmentStartY = currentY; 163 } 164 break; 165 case 'M': // moveto - Start a new sub-path 166 currentX = points->at(k + 0); 167 currentY = points->at(k + 1); 168 if (k > start) { 169 // According to the spec, if a moveto is followed by multiple 170 // pairs of coordinates, the subsequent pairs are treated as 171 // implicit lineto commands. 172 outPath->lineTo(points->at(k + 0), points->at(k + 1)); 173 } else { 174 outPath->moveTo(points->at(k + 0), points->at(k + 1)); 175 currentSegmentStartX = currentX; 176 currentSegmentStartY = currentY; 177 } 178 break; 179 case 'l': // lineto - Draw a line from the current point (relative) 180 outPath->rLineTo(points->at(k + 0), points->at(k + 1)); 181 currentX += points->at(k + 0); 182 currentY += points->at(k + 1); 183 break; 184 case 'L': // lineto - Draw a line from the current point 185 outPath->lineTo(points->at(k + 0), points->at(k + 1)); 186 currentX = points->at(k + 0); 187 currentY = points->at(k + 1); 188 break; 189 case 'h': // horizontal lineto - Draws a horizontal line (relative) 190 outPath->rLineTo(points->at(k + 0), 0); 191 currentX += points->at(k + 0); 192 break; 193 case 'H': // horizontal lineto - Draws a horizontal line 194 outPath->lineTo(points->at(k + 0), currentY); 195 currentX = points->at(k + 0); 196 break; 197 case 'v': // vertical lineto - Draws a vertical line from the current point (r) 198 outPath->rLineTo(0, points->at(k + 0)); 199 currentY += points->at(k + 0); 200 break; 201 case 'V': // vertical lineto - Draws a vertical line from the current point 202 outPath->lineTo(currentX, points->at(k + 0)); 203 currentY = points->at(k + 0); 204 break; 205 case 'c': // curveto - Draws a cubic Bzier curve (relative) 206 outPath->rCubicTo(points->at(k + 0), points->at(k + 1), points->at(k + 2), 207 points->at(k + 3), points->at(k + 4), points->at(k + 5)); 208 209 ctrlPointX = currentX + points->at(k + 2); 210 ctrlPointY = currentY + points->at(k + 3); 211 currentX += points->at(k + 4); 212 currentY += points->at(k + 5); 213 214 break; 215 case 'C': // curveto - Draws a cubic Bzier curve 216 outPath->cubicTo(points->at(k + 0), points->at(k + 1), points->at(k + 2), 217 points->at(k + 3), points->at(k + 4), points->at(k + 5)); 218 currentX = points->at(k + 4); 219 currentY = points->at(k + 5); 220 ctrlPointX = points->at(k + 2); 221 ctrlPointY = points->at(k + 3); 222 break; 223 case 's': // smooth curveto - Draws a cubic Bzier curve (reflective cp) 224 reflectiveCtrlPointX = 0; 225 reflectiveCtrlPointY = 0; 226 if (previousCmd == 'c' || previousCmd == 's' || previousCmd == 'C' || 227 previousCmd == 'S') { 228 reflectiveCtrlPointX = currentX - ctrlPointX; 229 reflectiveCtrlPointY = currentY - ctrlPointY; 230 } 231 outPath->rCubicTo(reflectiveCtrlPointX, reflectiveCtrlPointY, points->at(k + 0), 232 points->at(k + 1), points->at(k + 2), points->at(k + 3)); 233 ctrlPointX = currentX + points->at(k + 0); 234 ctrlPointY = currentY + points->at(k + 1); 235 currentX += points->at(k + 2); 236 currentY += points->at(k + 3); 237 break; 238 case 'S': // shorthand/smooth curveto Draws a cubic Bzier curve(reflective cp) 239 reflectiveCtrlPointX = currentX; 240 reflectiveCtrlPointY = currentY; 241 if (previousCmd == 'c' || previousCmd == 's' || previousCmd == 'C' || 242 previousCmd == 'S') { 243 reflectiveCtrlPointX = 2 * currentX - ctrlPointX; 244 reflectiveCtrlPointY = 2 * currentY - ctrlPointY; 245 } 246 outPath->cubicTo(reflectiveCtrlPointX, reflectiveCtrlPointY, points->at(k + 0), 247 points->at(k + 1), points->at(k + 2), points->at(k + 3)); 248 ctrlPointX = points->at(k + 0); 249 ctrlPointY = points->at(k + 1); 250 currentX = points->at(k + 2); 251 currentY = points->at(k + 3); 252 break; 253 case 'q': // Draws a quadratic Bzier (relative) 254 outPath->rQuadTo(points->at(k + 0), points->at(k + 1), points->at(k + 2), 255 points->at(k + 3)); 256 ctrlPointX = currentX + points->at(k + 0); 257 ctrlPointY = currentY + points->at(k + 1); 258 currentX += points->at(k + 2); 259 currentY += points->at(k + 3); 260 break; 261 case 'Q': // Draws a quadratic Bzier 262 outPath->quadTo(points->at(k + 0), points->at(k + 1), points->at(k + 2), 263 points->at(k + 3)); 264 ctrlPointX = points->at(k + 0); 265 ctrlPointY = points->at(k + 1); 266 currentX = points->at(k + 2); 267 currentY = points->at(k + 3); 268 break; 269 case 't': // Draws a quadratic Bzier curve(reflective control point)(relative) 270 reflectiveCtrlPointX = 0; 271 reflectiveCtrlPointY = 0; 272 if (previousCmd == 'q' || previousCmd == 't' || previousCmd == 'Q' || 273 previousCmd == 'T') { 274 reflectiveCtrlPointX = currentX - ctrlPointX; 275 reflectiveCtrlPointY = currentY - ctrlPointY; 276 } 277 outPath->rQuadTo(reflectiveCtrlPointX, reflectiveCtrlPointY, points->at(k + 0), 278 points->at(k + 1)); 279 ctrlPointX = currentX + reflectiveCtrlPointX; 280 ctrlPointY = currentY + reflectiveCtrlPointY; 281 currentX += points->at(k + 0); 282 currentY += points->at(k + 1); 283 break; 284 case 'T': // Draws a quadratic Bzier curve (reflective control point) 285 reflectiveCtrlPointX = currentX; 286 reflectiveCtrlPointY = currentY; 287 if (previousCmd == 'q' || previousCmd == 't' || previousCmd == 'Q' || 288 previousCmd == 'T') { 289 reflectiveCtrlPointX = 2 * currentX - ctrlPointX; 290 reflectiveCtrlPointY = 2 * currentY - ctrlPointY; 291 } 292 outPath->quadTo(reflectiveCtrlPointX, reflectiveCtrlPointY, points->at(k + 0), 293 points->at(k + 1)); 294 ctrlPointX = reflectiveCtrlPointX; 295 ctrlPointY = reflectiveCtrlPointY; 296 currentX = points->at(k + 0); 297 currentY = points->at(k + 1); 298 break; 299 case 'a': // Draws an elliptical arc 300 // (rx ry x-axis-rotation large-arc-flag sweep-flag x y) 301 outPath->arcTo(points->at(k + 0), points->at(k + 1), points->at(k + 2), 302 (SkPath::ArcSize) (points->at(k + 3) != 0), 303 (SkPath::Direction) (points->at(k + 4) == 0), 304 points->at(k + 5) + currentX, points->at(k + 6) + currentY); 305 currentX += points->at(k + 5); 306 currentY += points->at(k + 6); 307 ctrlPointX = currentX; 308 ctrlPointY = currentY; 309 break; 310 case 'A': // Draws an elliptical arc 311 outPath->arcTo(points->at(k + 0), points->at(k + 1), points->at(k + 2), 312 (SkPath::ArcSize) (points->at(k + 3) != 0), 313 (SkPath::Direction) (points->at(k + 4) == 0), 314 points->at(k + 5), points->at(k + 6)); 315 currentX = points->at(k + 5); 316 currentY = points->at(k + 6); 317 ctrlPointX = currentX; 318 ctrlPointY = currentY; 319 break; 320 default: 321 LOG_ALWAYS_FATAL("Unsupported command: %c", cmd); 322 break; 323 } 324 previousCmd = cmd; 325 } 326 } 327 328 } // namespace uirenderer 329 } // namespace android 330