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