1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/skia/trunk/src/svg/SkSVGPaintState.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,454 @@ 1.4 + 1.5 +/* 1.6 + * Copyright 2006 The Android Open Source Project 1.7 + * 1.8 + * Use of this source code is governed by a BSD-style license that can be 1.9 + * found in the LICENSE file. 1.10 + */ 1.11 + 1.12 + 1.13 +#include "SkSVGPaintState.h" 1.14 +#include "SkSVGElements.h" 1.15 +#include "SkSVGParser.h" 1.16 +#include "SkParse.h" 1.17 + 1.18 +SkSVGAttribute SkSVGPaint::gAttributes[] = { 1.19 + SVG_LITERAL_ATTRIBUTE(clip-path, f_clipPath), 1.20 + SVG_LITERAL_ATTRIBUTE(clip-rule, f_clipRule), 1.21 + SVG_LITERAL_ATTRIBUTE(enable-background, f_enableBackground), 1.22 + SVG_ATTRIBUTE(fill), 1.23 + SVG_LITERAL_ATTRIBUTE(fill-rule, f_fillRule), 1.24 + SVG_ATTRIBUTE(filter), 1.25 + SVG_LITERAL_ATTRIBUTE(font-family, f_fontFamily), 1.26 + SVG_LITERAL_ATTRIBUTE(font-size, f_fontSize), 1.27 + SVG_LITERAL_ATTRIBUTE(letter-spacing, f_letterSpacing), 1.28 + SVG_ATTRIBUTE(mask), 1.29 + SVG_ATTRIBUTE(opacity), 1.30 + SVG_LITERAL_ATTRIBUTE(stop-color, f_stopColor), 1.31 + SVG_LITERAL_ATTRIBUTE(stop-opacity, f_stopOpacity), 1.32 + SVG_ATTRIBUTE(stroke), 1.33 + SVG_LITERAL_ATTRIBUTE(stroke-dasharray, f_strokeDasharray), 1.34 + SVG_LITERAL_ATTRIBUTE(stroke-linecap, f_strokeLinecap), 1.35 + SVG_LITERAL_ATTRIBUTE(stroke-linejoin, f_strokeLinejoin), 1.36 + SVG_LITERAL_ATTRIBUTE(stroke-miterlimit, f_strokeMiterlimit), 1.37 + SVG_LITERAL_ATTRIBUTE(stroke-width, f_strokeWidth), 1.38 + SVG_ATTRIBUTE(style), 1.39 + SVG_ATTRIBUTE(transform) 1.40 +}; 1.41 + 1.42 +const int SkSVGPaint::kAttributesSize = SK_ARRAY_COUNT(SkSVGPaint::gAttributes); 1.43 + 1.44 +SkSVGPaint::SkSVGPaint() : fNext(NULL) { 1.45 +} 1.46 + 1.47 +SkString* SkSVGPaint::operator[](int index) { 1.48 + SkASSERT(index >= 0); 1.49 + SkASSERT(index < &fTerminal - &fInitial); 1.50 + SkASSERT(&fTerminal - &fInitial == kTerminal - kInitial); 1.51 + SkString* result = &fInitial + index + 1; 1.52 + return result; 1.53 +} 1.54 + 1.55 +void SkSVGPaint::addAttribute(SkSVGParser& parser, int attrIndex, 1.56 + const char* attrValue, size_t attrLength) { 1.57 + SkString* attr = (*this)[attrIndex]; 1.58 + switch(attrIndex) { 1.59 + case kClipPath: 1.60 + case kClipRule: 1.61 + case kEnableBackground: 1.62 + case kFill: 1.63 + case kFillRule: 1.64 + case kFilter: 1.65 + case kFontFamily: 1.66 + case kFontSize: 1.67 + case kLetterSpacing: 1.68 + case kMask: 1.69 + case kOpacity: 1.70 + case kStopColor: 1.71 + case kStopOpacity: 1.72 + case kStroke: 1.73 + case kStroke_Dasharray: 1.74 + case kStroke_Linecap: 1.75 + case kStroke_Linejoin: 1.76 + case kStroke_Miterlimit: 1.77 + case kStroke_Width: 1.78 + case kTransform: 1.79 + attr->set(attrValue, attrLength); 1.80 + return; 1.81 + case kStyle: { 1.82 + // iterate through colon / semi-colon delimited pairs 1.83 + int pairs = SkParse::Count(attrValue, ';'); 1.84 + const char* attrEnd = attrValue + attrLength; 1.85 + do { 1.86 + const char* end = strchr(attrValue, ';'); 1.87 + if (end == NULL) 1.88 + end = attrEnd; 1.89 + const char* delimiter = strchr(attrValue, ':'); 1.90 + SkASSERT(delimiter != 0 && delimiter < end); 1.91 + int index = parser.findAttribute(this, attrValue, (int) (delimiter - attrValue), true); 1.92 + SkASSERT(index >= 0); 1.93 + delimiter++; 1.94 + addAttribute(parser, index, delimiter, (int) (end - delimiter)); 1.95 + attrValue = end + 1; 1.96 + } while (--pairs); 1.97 + return; 1.98 + } 1.99 + default: 1.100 + SkASSERT(0); 1.101 + } 1.102 +} 1.103 + 1.104 +bool SkSVGPaint::flush(SkSVGParser& parser, bool isFlushable, bool isDef) { 1.105 + SkSVGPaint current; 1.106 + SkSVGPaint* walking = parser.fHead; 1.107 + int index; 1.108 + while (walking != NULL) { 1.109 + for (index = kInitial + 1; index < kTerminal; index++) { 1.110 + SkString* lastAttr = (*walking)[index]; 1.111 + if (lastAttr->size() == 0) 1.112 + continue; 1.113 + if (current[index]->size() > 0) 1.114 + continue; 1.115 + current[index]->set(*lastAttr); 1.116 + } 1.117 + walking = walking->fNext; 1.118 + } 1.119 + bool paintChanged = false; 1.120 + SkSVGPaint& lastState = parser.fLastFlush; 1.121 + if (isFlushable == false) { 1.122 + if (isDef == true) { 1.123 + if (current.f_mask.size() > 0 && current.f_mask.equals(lastState.f_mask) == false) { 1.124 + SkSVGElement* found; 1.125 + const char* idStart = strchr(current.f_mask.c_str(), '#'); 1.126 + SkASSERT(idStart); 1.127 + SkString id(idStart + 1, strlen(idStart) - 2); 1.128 + bool itsFound = parser.fIDs.find(id.c_str(), &found); 1.129 + SkASSERT(itsFound); 1.130 + SkSVGElement* gradient = found->getGradient(); 1.131 + if (gradient) { 1.132 + gradient->write(parser, current.f_fill); 1.133 + gradient->write(parser, current.f_stroke); 1.134 + } 1.135 + } 1.136 + } 1.137 + goto setLast; 1.138 + } 1.139 + { 1.140 + bool changed[kTerminal]; 1.141 + memset(changed, 0, sizeof(changed)); 1.142 + for (index = kInitial + 1; index < kTerminal; index++) { 1.143 + if (index == kTransform || index == kClipPath || index == kStopColor || index == kStopOpacity || 1.144 + index == kClipRule || index == kFillRule) 1.145 + continue; 1.146 + SkString* lastAttr = lastState[index]; 1.147 + SkString* currentAttr = current[index]; 1.148 + paintChanged |= changed[index] = lastAttr->equals(*currentAttr) == false; 1.149 + } 1.150 + if (paintChanged) { 1.151 + if (current.f_mask.size() > 0) { 1.152 + if (current.f_fill.equals("none") == false && strncmp(current.f_fill.c_str(), "url(#", 5) != 0) { 1.153 + SkASSERT(current.f_fill.c_str()[0] == '#'); 1.154 + SkString replacement("url(#mask"); 1.155 + replacement.append(current.f_fill.c_str() + 1); 1.156 + replacement.appendUnichar(')'); 1.157 + current.f_fill.set(replacement); 1.158 + } 1.159 + if (current.f_stroke.equals("none") == false && strncmp(current.f_stroke.c_str(), "url(#", 5) != 0) { 1.160 + SkASSERT(current.f_stroke.c_str()[0] == '#'); 1.161 + SkString replacement("url(#mask"); 1.162 + replacement.append(current.f_stroke.c_str() + 1); 1.163 + replacement.appendUnichar(')'); 1.164 + current.f_stroke.set(replacement); 1.165 + } 1.166 + } 1.167 + if (current.f_fill.equals("none") && current.f_stroke.equals("none")) 1.168 + current.f_opacity.set("0"); 1.169 + if (parser.fSuppressPaint == false) { 1.170 + parser._startElement("paint"); 1.171 + bool success = writeChangedAttributes(parser, current, changed); 1.172 + if (success == false) 1.173 + return paintChanged; 1.174 + success = writeChangedElements(parser, current, changed); 1.175 + if (success == false) 1.176 + return paintChanged; 1.177 + parser._endElement(); // paint 1.178 + } 1.179 + } 1.180 + } 1.181 +setLast: 1.182 + for (index = kInitial + 1; index < kTerminal; index++) { 1.183 + SkString* lastAttr = lastState[index]; 1.184 + SkString* currentAttr = current[index]; 1.185 + lastAttr->set(*currentAttr); 1.186 + } 1.187 + return paintChanged; 1.188 +} 1.189 + 1.190 +int SkSVGPaint::getAttributes(const SkSVGAttribute** attrPtr) { 1.191 + *attrPtr = gAttributes; 1.192 + return kAttributesSize; 1.193 +} 1.194 + 1.195 +void SkSVGPaint::setSave(SkSVGParser& parser) { 1.196 + SkTDArray<SkString*> clips; 1.197 + SkSVGPaint* walking = parser.fHead; 1.198 + int index; 1.199 + SkMatrix sum; 1.200 + sum.reset(); 1.201 + while (walking != NULL) { 1.202 + for (index = kInitial + 1; index < kTerminal; index++) { 1.203 + SkString* lastAttr = (*walking)[index]; 1.204 + if (lastAttr->size() == 0) 1.205 + continue; 1.206 + if (index == kTransform) { 1.207 + const char* str = lastAttr->c_str(); 1.208 + SkASSERT(strncmp(str, "matrix(", 7) == 0); 1.209 + str += 6; 1.210 + const char* strEnd = strrchr(str, ')'); 1.211 + SkASSERT(strEnd != NULL); 1.212 + SkString mat(str, strEnd - str); 1.213 + SkSVGParser::ConvertToArray(mat); 1.214 + SkScalar values[6]; 1.215 + SkParse::FindScalars(mat.c_str() + 1, values, 6); 1.216 + SkMatrix matrix; 1.217 + matrix.reset(); 1.218 + matrix.setScaleX(values[0]); 1.219 + matrix.setSkewY(values[1]); 1.220 + matrix.setSkewX(values[2]); 1.221 + matrix.setScaleY(values[3]); 1.222 + matrix.setTranslateX(values[4]); 1.223 + matrix.setTranslateY(values[5]); 1.224 + sum.setConcat(matrix, sum); 1.225 + continue; 1.226 + } 1.227 + if ( index == kClipPath) 1.228 + *clips.insert(0) = lastAttr; 1.229 + } 1.230 + walking = walking->fNext; 1.231 + } 1.232 + if ((sum == parser.fLastTransform) == false) { 1.233 + SkMatrix inverse; 1.234 + bool success = parser.fLastTransform.invert(&inverse); 1.235 + SkASSERT(success == true); 1.236 + SkMatrix output; 1.237 + output.setConcat(inverse, sum); 1.238 + parser.fLastTransform = sum; 1.239 + SkString outputStr; 1.240 + outputStr.appendUnichar('['); 1.241 + outputStr.appendScalar(output.getScaleX()); 1.242 + outputStr.appendUnichar(','); 1.243 + outputStr.appendScalar(output.getSkewX()); 1.244 + outputStr.appendUnichar(','); 1.245 + outputStr.appendScalar(output.getTranslateX()); 1.246 + outputStr.appendUnichar(','); 1.247 + outputStr.appendScalar(output.getSkewY()); 1.248 + outputStr.appendUnichar(','); 1.249 + outputStr.appendScalar(output.getScaleY()); 1.250 + outputStr.appendUnichar(','); 1.251 + outputStr.appendScalar(output.getTranslateY()); 1.252 + outputStr.appendUnichar(','); 1.253 + outputStr.appendScalar(output.getPerspX()); 1.254 + outputStr.appendUnichar(','); 1.255 + outputStr.appendScalar(output.getPerspY()); 1.256 + outputStr.append(",1]"); 1.257 + parser._startElement("matrix"); 1.258 + parser._addAttributeLen("matrix", outputStr.c_str(), outputStr.size()); 1.259 + parser._endElement(); 1.260 + } 1.261 +#if 0 // incomplete 1.262 + if (parser.fTransformClips.size() > 0) { 1.263 + // need to reset the clip when the 'g' scope is ended 1.264 + parser._startElement("add"); 1.265 + const char* start = strchr(current->f_clipPath.c_str(), '#') + 1; 1.266 + SkASSERT(start); 1.267 + parser._addAttributeLen("use", start, strlen(start) - 1); 1.268 + parser._endElement(); // clip 1.269 + } 1.270 +#endif 1.271 +} 1.272 + 1.273 +bool SkSVGPaint::writeChangedAttributes(SkSVGParser& parser, 1.274 + SkSVGPaint& current, bool* changed) { 1.275 + SkSVGPaint& lastState = parser.fLastFlush; 1.276 + for (int index = kInitial + 1; index < kTerminal; index++) { 1.277 + if (changed[index] == false) 1.278 + continue; 1.279 + SkString* topAttr = current[index]; 1.280 + size_t attrLength = topAttr->size(); 1.281 + if (attrLength == 0) 1.282 + continue; 1.283 + const char* attrValue = topAttr->c_str(); 1.284 + SkString* lastAttr = lastState[index]; 1.285 + switch(index) { 1.286 + case kClipPath: 1.287 + case kClipRule: 1.288 + case kEnableBackground: 1.289 + break; 1.290 + case kFill: 1.291 + if (topAttr->equals("none") == false && lastAttr->equals("none") == true) 1.292 + parser._addAttribute("stroke", "false"); 1.293 + goto fillStrokeAttrCommon; 1.294 + case kFillRule: 1.295 + case kFilter: 1.296 + case kFontFamily: 1.297 + break; 1.298 + case kFontSize: 1.299 + parser._addAttributeLen("textSize", attrValue, attrLength); 1.300 + break; 1.301 + case kLetterSpacing: 1.302 + parser._addAttributeLen("textTracking", attrValue, attrLength); 1.303 + break; 1.304 + case kMask: 1.305 + break; 1.306 + case kOpacity: 1.307 + break; 1.308 + case kStopColor: 1.309 + break; 1.310 + case kStopOpacity: 1.311 + break; 1.312 + case kStroke: 1.313 + if (topAttr->equals("none") == false && lastAttr->equals("none") == true) 1.314 + parser._addAttribute("stroke", "true"); 1.315 +fillStrokeAttrCommon: 1.316 + if (strncmp(attrValue, "url(", 4) == 0) { 1.317 + SkASSERT(attrValue[4] == '#'); 1.318 + const char* idStart = attrValue + 5; 1.319 + const char* idEnd = strrchr(attrValue, ')'); 1.320 + SkASSERT(idStart < idEnd); 1.321 + SkString id(idStart, idEnd - idStart); 1.322 + SkSVGElement* found; 1.323 + if (strncmp(id.c_str(), "mask", 4) != 0) { 1.324 + bool itsFound = parser.fIDs.find(id.c_str(), &found); 1.325 + SkASSERT(itsFound); 1.326 + SkASSERT(found->getType() == SkSVGType_LinearGradient || 1.327 + found->getType() == SkSVGType_RadialGradient); 1.328 + } 1.329 + parser._addAttribute("shader", id.c_str()); 1.330 + } 1.331 + break; 1.332 + case kStroke_Dasharray: 1.333 + break; 1.334 + case kStroke_Linecap: 1.335 + parser._addAttributeLen("strokeCap", attrValue, attrLength); 1.336 + break; 1.337 + case kStroke_Linejoin: 1.338 + parser._addAttributeLen("strokeJoin", attrValue, attrLength); 1.339 + break; 1.340 + case kStroke_Miterlimit: 1.341 + parser._addAttributeLen("strokeMiter", attrValue, attrLength); 1.342 + break; 1.343 + case kStroke_Width: 1.344 + parser._addAttributeLen("strokeWidth", attrValue, attrLength); 1.345 + case kStyle: 1.346 + case kTransform: 1.347 + break; 1.348 + default: 1.349 + SkASSERT(0); 1.350 + return false; 1.351 + } 1.352 + } 1.353 + return true; 1.354 +} 1.355 + 1.356 +bool SkSVGPaint::writeChangedElements(SkSVGParser& parser, 1.357 + SkSVGPaint& current, bool* changed) { 1.358 + SkSVGPaint& lastState = parser.fLastFlush; 1.359 + for (int index = kInitial + 1; index < kTerminal; index++) { 1.360 + SkString* topAttr = current[index]; 1.361 + size_t attrLength = topAttr->size(); 1.362 + if (attrLength == 0) 1.363 + continue; 1.364 + const char* attrValue = topAttr->c_str(); 1.365 + SkString* lastAttr = lastState[index]; 1.366 + switch(index) { 1.367 + case kClipPath: 1.368 + case kClipRule: 1.369 + // !!! need to add this outside of paint 1.370 + break; 1.371 + case kEnableBackground: 1.372 + // !!! don't know what to do with this 1.373 + break; 1.374 + case kFill: 1.375 + goto addColor; 1.376 + case kFillRule: 1.377 + case kFilter: 1.378 + break; 1.379 + case kFontFamily: 1.380 + parser._startElement("typeface"); 1.381 + parser._addAttributeLen("fontName", attrValue, attrLength); 1.382 + parser._endElement(); // typeface 1.383 + break; 1.384 + case kFontSize: 1.385 + case kLetterSpacing: 1.386 + break; 1.387 + case kMask: 1.388 + case kOpacity: 1.389 + if (changed[kStroke] == false && changed[kFill] == false) { 1.390 + parser._startElement("color"); 1.391 + SkString& opacity = current.f_opacity; 1.392 + parser._addAttributeLen("color", parser.fLastColor.c_str(), parser.fLastColor.size()); 1.393 + parser._addAttributeLen("alpha", opacity.c_str(), opacity.size()); 1.394 + parser._endElement(); // color 1.395 + } 1.396 + break; 1.397 + case kStopColor: 1.398 + break; 1.399 + case kStopOpacity: 1.400 + break; 1.401 + case kStroke: 1.402 +addColor: 1.403 + if (strncmp(lastAttr->c_str(), "url(", 4) == 0 && strncmp(attrValue, "url(", 4) != 0) { 1.404 + parser._startElement("shader"); 1.405 + parser._endElement(); 1.406 + } 1.407 + if (topAttr->equals(*lastAttr)) 1.408 + continue; 1.409 + { 1.410 + bool urlRef = strncmp(attrValue, "url(", 4) == 0; 1.411 + bool colorNone = strcmp(attrValue, "none") == 0; 1.412 + bool lastEqual = parser.fLastColor.equals(attrValue, attrLength); 1.413 + bool newColor = urlRef == false && colorNone == false && lastEqual == false; 1.414 + if (newColor || changed[kOpacity]) { 1.415 + parser._startElement("color"); 1.416 + if (newColor || changed[kOpacity]) { 1.417 + parser._addAttributeLen("color", attrValue, attrLength); 1.418 + parser.fLastColor.set(attrValue, attrLength); 1.419 + } 1.420 + if (changed[kOpacity]) { 1.421 + SkString& opacity = current.f_opacity; 1.422 + parser._addAttributeLen("alpha", opacity.c_str(), opacity.size()); 1.423 + } 1.424 + parser._endElement(); // color 1.425 + } 1.426 + } 1.427 + break; 1.428 + case kStroke_Dasharray: 1.429 + parser._startElement("dash"); 1.430 + SkSVGParser::ConvertToArray(*topAttr); 1.431 + parser._addAttribute("intervals", topAttr->c_str()); 1.432 + parser._endElement(); // dash 1.433 + break; 1.434 + case kStroke_Linecap: 1.435 + case kStroke_Linejoin: 1.436 + case kStroke_Miterlimit: 1.437 + case kStroke_Width: 1.438 + case kStyle: 1.439 + case kTransform: 1.440 + break; 1.441 + default: 1.442 + SkASSERT(0); 1.443 + return false; 1.444 + } 1.445 + } 1.446 + return true; 1.447 +} 1.448 + 1.449 +void SkSVGPaint::Push(SkSVGPaint** head, SkSVGPaint* newRecord) { 1.450 + newRecord->fNext = *head; 1.451 + *head = newRecord; 1.452 +} 1.453 + 1.454 +void SkSVGPaint::Pop(SkSVGPaint** head) { 1.455 + SkSVGPaint* next = (*head)->fNext; 1.456 + *head = next; 1.457 +}