gfx/skia/trunk/src/svg/SkSVGPaintState.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial