| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 * Copyright 2006 The Android Open Source Project | |
| 3 * | |
| 4 * Use of this source code is governed by a BSD-style license that can be | |
| 5 * found in the LICENSE file. | |
| 6 */ | |
| 7 | |
| 8 | |
| 9 #include "SkSVGPaintState.h" | |
| 10 #include "SkSVGElements.h" | |
| 11 #include "SkSVGParser.h" | |
| 12 #include "SkParse.h" | |
| 13 | |
| 14 SkSVGAttribute SkSVGPaint::gAttributes[] = { | |
| 15 SVG_LITERAL_ATTRIBUTE(clip-path, f_clipPath), | |
| 16 SVG_LITERAL_ATTRIBUTE(clip-rule, f_clipRule), | |
| 17 SVG_LITERAL_ATTRIBUTE(enable-background, f_enableBackground), | |
| 18 SVG_ATTRIBUTE(fill), | |
| 19 SVG_LITERAL_ATTRIBUTE(fill-rule, f_fillRule), | |
| 20 SVG_ATTRIBUTE(filter), | |
| 21 SVG_LITERAL_ATTRIBUTE(font-family, f_fontFamily), | |
| 22 SVG_LITERAL_ATTRIBUTE(font-size, f_fontSize), | |
| 23 SVG_LITERAL_ATTRIBUTE(letter-spacing, f_letterSpacing), | |
| 24 SVG_ATTRIBUTE(mask), | |
| 25 SVG_ATTRIBUTE(opacity), | |
| 26 SVG_LITERAL_ATTRIBUTE(stop-color, f_stopColor), | |
| 27 SVG_LITERAL_ATTRIBUTE(stop-opacity, f_stopOpacity), | |
| 28 SVG_ATTRIBUTE(stroke), | |
| 29 SVG_LITERAL_ATTRIBUTE(stroke-dasharray, f_strokeDasharray), | |
| 30 SVG_LITERAL_ATTRIBUTE(stroke-linecap, f_strokeLinecap), | |
| 31 SVG_LITERAL_ATTRIBUTE(stroke-linejoin, f_strokeLinejoin), | |
| 32 SVG_LITERAL_ATTRIBUTE(stroke-miterlimit, f_strokeMiterlimit), | |
| 33 SVG_LITERAL_ATTRIBUTE(stroke-width, f_strokeWidth), | |
| 34 SVG_ATTRIBUTE(style), | |
| 35 SVG_ATTRIBUTE(transform) | |
| 36 }; | |
| 37 | |
| 38 const int SkSVGPaint::kAttributesSize = SK_ARRAY_COUNT(SkSVGPaint::gAttributes); | |
| 39 | |
| 40 SkSVGPaint::SkSVGPaint() : fNext(nullptr) { | |
| 41 } | |
| 42 | |
| 43 SkString* SkSVGPaint::operator[](int index) { | |
| 44 SkASSERT(index >= 0); | |
| 45 SkASSERT(index < &fTerminal - &fInitial); | |
| 46 SkASSERT(&fTerminal - &fInitial == kTerminal - kInitial); | |
| 47 SkString* result = &fInitial + index + 1; | |
| 48 return result; | |
| 49 } | |
| 50 | |
| 51 void SkSVGPaint::addAttribute(SkSVGParser& parser, int attrIndex, | |
| 52 const char* attrValue, size_t attrLength) { | |
| 53 SkString* attr = (*this)[attrIndex]; | |
| 54 switch(attrIndex) { | |
| 55 case kClipPath: | |
| 56 case kClipRule: | |
| 57 case kEnableBackground: | |
| 58 case kFill: | |
| 59 case kFillRule: | |
| 60 case kFilter: | |
| 61 case kFontFamily: | |
| 62 case kFontSize: | |
| 63 case kLetterSpacing: | |
| 64 case kMask: | |
| 65 case kOpacity: | |
| 66 case kStopColor: | |
| 67 case kStopOpacity: | |
| 68 case kStroke: | |
| 69 case kStroke_Dasharray: | |
| 70 case kStroke_Linecap: | |
| 71 case kStroke_Linejoin: | |
| 72 case kStroke_Miterlimit: | |
| 73 case kStroke_Width: | |
| 74 case kTransform: | |
| 75 attr->set(attrValue, attrLength); | |
| 76 return; | |
| 77 case kStyle: { | |
| 78 // iterate through colon / semi-colon delimited pairs | |
| 79 int pairs = SkParse::Count(attrValue, ';'); | |
| 80 const char* attrEnd = attrValue + attrLength; | |
| 81 do { | |
| 82 const char* end = strchr(attrValue, ';'); | |
| 83 if (end == nullptr) | |
| 84 end = attrEnd; | |
| 85 const char* delimiter = strchr(attrValue, ':'); | |
| 86 SkASSERT(delimiter != 0 && delimiter < end); | |
| 87 int index = parser.findAttribute(this, attrValue, (int) (delimit
er - attrValue), true); | |
| 88 SkASSERT(index >= 0); | |
| 89 delimiter++; | |
| 90 addAttribute(parser, index, delimiter, (int) (end - delimiter)); | |
| 91 attrValue = end + 1; | |
| 92 } while (--pairs); | |
| 93 return; | |
| 94 } | |
| 95 default: | |
| 96 SkASSERT(0); | |
| 97 } | |
| 98 } | |
| 99 | |
| 100 bool SkSVGPaint::flush(SkSVGParser& parser, bool isFlushable, bool isDef) { | |
| 101 SkSVGPaint current; | |
| 102 SkSVGPaint* walking = parser.fHead; | |
| 103 int index; | |
| 104 while (walking != nullptr) { | |
| 105 for (index = kInitial + 1; index < kTerminal; index++) { | |
| 106 SkString* lastAttr = (*walking)[index]; | |
| 107 if (lastAttr->size() == 0) | |
| 108 continue; | |
| 109 if (current[index]->size() > 0) | |
| 110 continue; | |
| 111 current[index]->set(*lastAttr); | |
| 112 } | |
| 113 walking = walking->fNext; | |
| 114 } | |
| 115 bool paintChanged = false; | |
| 116 SkSVGPaint& lastState = parser.fLastFlush; | |
| 117 if (isFlushable == false) { | |
| 118 if (isDef == true) { | |
| 119 if (current.f_mask.size() > 0 && current.f_mask.equals(lastState.f_m
ask) == false) { | |
| 120 SkSVGElement* found; | |
| 121 const char* idStart = strchr(current.f_mask.c_str(), '#'); | |
| 122 SkASSERT(idStart); | |
| 123 SkString id(idStart + 1, strlen(idStart) - 2); | |
| 124 bool itsFound = parser.fIDs.find(id.c_str(), &found); | |
| 125 SkASSERT(itsFound); | |
| 126 SkSVGElement* gradient = found->getGradient(); | |
| 127 if (gradient) { | |
| 128 gradient->write(parser, current.f_fill); | |
| 129 gradient->write(parser, current.f_stroke); | |
| 130 } | |
| 131 } | |
| 132 } | |
| 133 goto setLast; | |
| 134 } | |
| 135 { | |
| 136 bool changed[kTerminal]; | |
| 137 memset(changed, 0, sizeof(changed)); | |
| 138 for (index = kInitial + 1; index < kTerminal; index++) { | |
| 139 if (index == kTransform || index == kClipPath || index == kStopColor
|| index == kStopOpacity || | |
| 140 index == kClipRule || index == kFillRule) | |
| 141 continue; | |
| 142 SkString* lastAttr = lastState[index]; | |
| 143 SkString* currentAttr = current[index]; | |
| 144 paintChanged |= changed[index] = lastAttr->equals(*currentAttr) == f
alse; | |
| 145 } | |
| 146 if (paintChanged) { | |
| 147 if (current.f_mask.size() > 0) { | |
| 148 if (current.f_fill.equals("none") == false && strncmp(current.f_
fill.c_str(), "url(#", 5) != 0) { | |
| 149 SkASSERT(current.f_fill.c_str()[0] == '#'); | |
| 150 SkString replacement("url(#mask"); | |
| 151 replacement.append(current.f_fill.c_str() + 1); | |
| 152 replacement.appendUnichar(')'); | |
| 153 current.f_fill.set(replacement); | |
| 154 } | |
| 155 if (current.f_stroke.equals("none") == false && strncmp(current.
f_stroke.c_str(), "url(#", 5) != 0) { | |
| 156 SkASSERT(current.f_stroke.c_str()[0] == '#'); | |
| 157 SkString replacement("url(#mask"); | |
| 158 replacement.append(current.f_stroke.c_str() + 1); | |
| 159 replacement.appendUnichar(')'); | |
| 160 current.f_stroke.set(replacement); | |
| 161 } | |
| 162 } | |
| 163 if (current.f_fill.equals("none") && current.f_stroke.equals("none")
) | |
| 164 current.f_opacity.set("0"); | |
| 165 if (parser.fSuppressPaint == false) { | |
| 166 parser._startElement("paint"); | |
| 167 bool success = writeChangedAttributes(parser, current, changed); | |
| 168 if (success == false) | |
| 169 return paintChanged; | |
| 170 success = writeChangedElements(parser, current, changed); | |
| 171 if (success == false) | |
| 172 return paintChanged; | |
| 173 parser._endElement(); // paint | |
| 174 } | |
| 175 } | |
| 176 } | |
| 177 setLast: | |
| 178 for (index = kInitial + 1; index < kTerminal; index++) { | |
| 179 SkString* lastAttr = lastState[index]; | |
| 180 SkString* currentAttr = current[index]; | |
| 181 lastAttr->set(*currentAttr); | |
| 182 } | |
| 183 return paintChanged; | |
| 184 } | |
| 185 | |
| 186 int SkSVGPaint::getAttributes(const SkSVGAttribute** attrPtr) { | |
| 187 *attrPtr = gAttributes; | |
| 188 return kAttributesSize; | |
| 189 } | |
| 190 | |
| 191 void SkSVGPaint::setSave(SkSVGParser& parser) { | |
| 192 SkTDArray<SkString*> clips; | |
| 193 SkSVGPaint* walking = parser.fHead; | |
| 194 int index; | |
| 195 SkMatrix sum; | |
| 196 sum.reset(); | |
| 197 while (walking != nullptr) { | |
| 198 for (index = kInitial + 1; index < kTerminal; index++) { | |
| 199 SkString* lastAttr = (*walking)[index]; | |
| 200 if (lastAttr->size() == 0) | |
| 201 continue; | |
| 202 if (index == kTransform) { | |
| 203 const char* str = lastAttr->c_str(); | |
| 204 SkASSERT(strncmp(str, "matrix(", 7) == 0); | |
| 205 str += 6; | |
| 206 const char* strEnd = strrchr(str, ')'); | |
| 207 SkASSERT(strEnd != nullptr); | |
| 208 SkString mat(str, strEnd - str); | |
| 209 SkSVGParser::ConvertToArray(mat); | |
| 210 SkScalar values[6]; | |
| 211 SkParse::FindScalars(mat.c_str() + 1, values, 6); | |
| 212 SkMatrix matrix; | |
| 213 matrix.reset(); | |
| 214 matrix.setScaleX(values[0]); | |
| 215 matrix.setSkewY(values[1]); | |
| 216 matrix.setSkewX(values[2]); | |
| 217 matrix.setScaleY(values[3]); | |
| 218 matrix.setTranslateX(values[4]); | |
| 219 matrix.setTranslateY(values[5]); | |
| 220 sum.setConcat(matrix, sum); | |
| 221 continue; | |
| 222 } | |
| 223 if ( index == kClipPath) | |
| 224 *clips.insert(0) = lastAttr; | |
| 225 } | |
| 226 walking = walking->fNext; | |
| 227 } | |
| 228 if ((sum == parser.fLastTransform) == false) { | |
| 229 SkMatrix inverse; | |
| 230 bool success = parser.fLastTransform.invert(&inverse); | |
| 231 SkASSERT(success == true); | |
| 232 SkMatrix output; | |
| 233 output.setConcat(inverse, sum); | |
| 234 parser.fLastTransform = sum; | |
| 235 SkString outputStr; | |
| 236 outputStr.appendUnichar('['); | |
| 237 outputStr.appendScalar(output.getScaleX()); | |
| 238 outputStr.appendUnichar(','); | |
| 239 outputStr.appendScalar(output.getSkewX()); | |
| 240 outputStr.appendUnichar(','); | |
| 241 outputStr.appendScalar(output.getTranslateX()); | |
| 242 outputStr.appendUnichar(','); | |
| 243 outputStr.appendScalar(output.getSkewY()); | |
| 244 outputStr.appendUnichar(','); | |
| 245 outputStr.appendScalar(output.getScaleY()); | |
| 246 outputStr.appendUnichar(','); | |
| 247 outputStr.appendScalar(output.getTranslateY()); | |
| 248 outputStr.appendUnichar(','); | |
| 249 outputStr.appendScalar(output.getPerspX()); | |
| 250 outputStr.appendUnichar(','); | |
| 251 outputStr.appendScalar(output.getPerspY()); | |
| 252 outputStr.append(",1]"); | |
| 253 parser._startElement("matrix"); | |
| 254 parser._addAttributeLen("matrix", outputStr.c_str(), outputStr.size()); | |
| 255 parser._endElement(); | |
| 256 } | |
| 257 #if 0 // incomplete | |
| 258 if (parser.fTransformClips.size() > 0) { | |
| 259 // need to reset the clip when the 'g' scope is ended | |
| 260 parser._startElement("add"); | |
| 261 const char* start = strchr(current->f_clipPath.c_str(), '#') + 1; | |
| 262 SkASSERT(start); | |
| 263 parser._addAttributeLen("use", start, strlen(start) - 1); | |
| 264 parser._endElement(); // clip | |
| 265 } | |
| 266 #endif | |
| 267 } | |
| 268 | |
| 269 bool SkSVGPaint::writeChangedAttributes(SkSVGParser& parser, | |
| 270 SkSVGPaint& current, bool* changed) { | |
| 271 SkSVGPaint& lastState = parser.fLastFlush; | |
| 272 for (int index = kInitial + 1; index < kTerminal; index++) { | |
| 273 if (changed[index] == false) | |
| 274 continue; | |
| 275 SkString* topAttr = current[index]; | |
| 276 size_t attrLength = topAttr->size(); | |
| 277 if (attrLength == 0) | |
| 278 continue; | |
| 279 const char* attrValue = topAttr->c_str(); | |
| 280 SkString* lastAttr = lastState[index]; | |
| 281 switch(index) { | |
| 282 case kClipPath: | |
| 283 case kClipRule: | |
| 284 case kEnableBackground: | |
| 285 break; | |
| 286 case kFill: | |
| 287 if (topAttr->equals("none") == false && lastAttr->equals("none")
== true) | |
| 288 parser._addAttribute("stroke", "false"); | |
| 289 goto fillStrokeAttrCommon; | |
| 290 case kFillRule: | |
| 291 case kFilter: | |
| 292 case kFontFamily: | |
| 293 break; | |
| 294 case kFontSize: | |
| 295 parser._addAttributeLen("textSize", attrValue, attrLength); | |
| 296 break; | |
| 297 case kLetterSpacing: | |
| 298 parser._addAttributeLen("textTracking", attrValue, attrLength); | |
| 299 break; | |
| 300 case kMask: | |
| 301 break; | |
| 302 case kOpacity: | |
| 303 break; | |
| 304 case kStopColor: | |
| 305 break; | |
| 306 case kStopOpacity: | |
| 307 break; | |
| 308 case kStroke: | |
| 309 if (topAttr->equals("none") == false && lastAttr->equals("none")
== true) | |
| 310 parser._addAttribute("stroke", "true"); | |
| 311 fillStrokeAttrCommon: | |
| 312 if (strncmp(attrValue, "url(", 4) == 0) { | |
| 313 SkASSERT(attrValue[4] == '#'); | |
| 314 const char* idStart = attrValue + 5; | |
| 315 const char* idEnd = strrchr(attrValue, ')'); | |
| 316 SkASSERT(idStart < idEnd); | |
| 317 SkString id(idStart, idEnd - idStart); | |
| 318 SkSVGElement* found; | |
| 319 if (strncmp(id.c_str(), "mask", 4) != 0) { | |
| 320 bool itsFound = parser.fIDs.find(id.c_str(), &found); | |
| 321 SkASSERT(itsFound); | |
| 322 SkASSERT(found->getType() == SkSVGType_LinearGradient || | |
| 323 found->getType() == SkSVGType_RadialGradient); | |
| 324 } | |
| 325 parser._addAttribute("shader", id.c_str()); | |
| 326 } | |
| 327 break; | |
| 328 case kStroke_Dasharray: | |
| 329 break; | |
| 330 case kStroke_Linecap: | |
| 331 parser._addAttributeLen("strokeCap", attrValue, attrLength); | |
| 332 break; | |
| 333 case kStroke_Linejoin: | |
| 334 parser._addAttributeLen("strokeJoin", attrValue, attrLength); | |
| 335 break; | |
| 336 case kStroke_Miterlimit: | |
| 337 parser._addAttributeLen("strokeMiter", attrValue, attrLength); | |
| 338 break; | |
| 339 case kStroke_Width: | |
| 340 parser._addAttributeLen("strokeWidth", attrValue, attrLength); | |
| 341 case kStyle: | |
| 342 case kTransform: | |
| 343 break; | |
| 344 default: | |
| 345 SkASSERT(0); | |
| 346 return false; | |
| 347 } | |
| 348 } | |
| 349 return true; | |
| 350 } | |
| 351 | |
| 352 bool SkSVGPaint::writeChangedElements(SkSVGParser& parser, | |
| 353 SkSVGPaint& current, bool* changed) { | |
| 354 SkSVGPaint& lastState = parser.fLastFlush; | |
| 355 for (int index = kInitial + 1; index < kTerminal; index++) { | |
| 356 SkString* topAttr = current[index]; | |
| 357 size_t attrLength = topAttr->size(); | |
| 358 if (attrLength == 0) | |
| 359 continue; | |
| 360 const char* attrValue = topAttr->c_str(); | |
| 361 SkString* lastAttr = lastState[index]; | |
| 362 switch(index) { | |
| 363 case kClipPath: | |
| 364 case kClipRule: | |
| 365 // !!! need to add this outside of paint | |
| 366 break; | |
| 367 case kEnableBackground: | |
| 368 // !!! don't know what to do with this | |
| 369 break; | |
| 370 case kFill: | |
| 371 goto addColor; | |
| 372 case kFillRule: | |
| 373 case kFilter: | |
| 374 break; | |
| 375 case kFontFamily: | |
| 376 parser._startElement("typeface"); | |
| 377 parser._addAttributeLen("fontName", attrValue, attrLength); | |
| 378 parser._endElement(); // typeface | |
| 379 break; | |
| 380 case kFontSize: | |
| 381 case kLetterSpacing: | |
| 382 break; | |
| 383 case kMask: | |
| 384 case kOpacity: | |
| 385 if (changed[kStroke] == false && changed[kFill] == false) { | |
| 386 parser._startElement("color"); | |
| 387 SkString& opacity = current.f_opacity; | |
| 388 parser._addAttributeLen("color", parser.fLastColor.c_str(),
parser.fLastColor.size()); | |
| 389 parser._addAttributeLen("alpha", opacity.c_str(), opacity.si
ze()); | |
| 390 parser._endElement(); // color | |
| 391 } | |
| 392 break; | |
| 393 case kStopColor: | |
| 394 break; | |
| 395 case kStopOpacity: | |
| 396 break; | |
| 397 case kStroke: | |
| 398 addColor: | |
| 399 if (strncmp(lastAttr->c_str(), "url(", 4) == 0 && strncmp(attrVa
lue, "url(", 4) != 0) { | |
| 400 parser._startElement("shader"); | |
| 401 parser._endElement(); | |
| 402 } | |
| 403 if (topAttr->equals(*lastAttr)) | |
| 404 continue; | |
| 405 { | |
| 406 bool urlRef = strncmp(attrValue, "url(", 4) == 0; | |
| 407 bool colorNone = strcmp(attrValue, "none") == 0; | |
| 408 bool lastEqual = parser.fLastColor.equals(attrValue, attrLen
gth); | |
| 409 bool newColor = urlRef == false && colorNone == false && las
tEqual == false; | |
| 410 if (newColor || changed[kOpacity]) { | |
| 411 parser._startElement("color"); | |
| 412 if (newColor || changed[kOpacity]) { | |
| 413 parser._addAttributeLen("color", attrValue, attrLeng
th); | |
| 414 parser.fLastColor.set(attrValue, attrLength); | |
| 415 } | |
| 416 if (changed[kOpacity]) { | |
| 417 SkString& opacity = current.f_opacity; | |
| 418 parser._addAttributeLen("alpha", opacity.c_str(), op
acity.size()); | |
| 419 } | |
| 420 parser._endElement(); // color | |
| 421 } | |
| 422 } | |
| 423 break; | |
| 424 case kStroke_Dasharray: | |
| 425 parser._startElement("dash"); | |
| 426 SkSVGParser::ConvertToArray(*topAttr); | |
| 427 parser._addAttribute("intervals", topAttr->c_str()); | |
| 428 parser._endElement(); // dash | |
| 429 break; | |
| 430 case kStroke_Linecap: | |
| 431 case kStroke_Linejoin: | |
| 432 case kStroke_Miterlimit: | |
| 433 case kStroke_Width: | |
| 434 case kStyle: | |
| 435 case kTransform: | |
| 436 break; | |
| 437 default: | |
| 438 SkASSERT(0); | |
| 439 return false; | |
| 440 } | |
| 441 } | |
| 442 return true; | |
| 443 } | |
| 444 | |
| 445 void SkSVGPaint::Push(SkSVGPaint** head, SkSVGPaint* newRecord) { | |
| 446 newRecord->fNext = *head; | |
| 447 *head = newRecord; | |
| 448 } | |
| 449 | |
| 450 void SkSVGPaint::Pop(SkSVGPaint** head) { | |
| 451 SkSVGPaint* next = (*head)->fNext; | |
| 452 *head = next; | |
| 453 } | |
| OLD | NEW |