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