diff options
Diffstat (limited to 'src/svg/SkSVGPaintState.cpp')
-rw-r--r-- | src/svg/SkSVGPaintState.cpp | 463 |
1 files changed, 463 insertions, 0 deletions
diff --git a/src/svg/SkSVGPaintState.cpp b/src/svg/SkSVGPaintState.cpp new file mode 100644 index 0000000..7fc90c7 --- /dev/null +++ b/src/svg/SkSVGPaintState.cpp @@ -0,0 +1,463 @@ +/* libs/graphics/svg/SkSVGPaintState.cpp +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkSVGPaintState.h" +#include "SkSVGElements.h" +#include "SkSVGParser.h" +#include "SkParse.h" + +SkSVGAttribute SkSVGPaint::gAttributes[] = { + SVG_LITERAL_ATTRIBUTE(clip-path, f_clipPath), + SVG_LITERAL_ATTRIBUTE(clip-rule, f_clipRule), + SVG_LITERAL_ATTRIBUTE(enable-background, f_enableBackground), + SVG_ATTRIBUTE(fill), + SVG_LITERAL_ATTRIBUTE(fill-rule, f_fillRule), + SVG_ATTRIBUTE(filter), + SVG_LITERAL_ATTRIBUTE(font-family, f_fontFamily), + SVG_LITERAL_ATTRIBUTE(font-size, f_fontSize), + SVG_LITERAL_ATTRIBUTE(letter-spacing, f_letterSpacing), + SVG_ATTRIBUTE(mask), + SVG_ATTRIBUTE(opacity), + SVG_LITERAL_ATTRIBUTE(stop-color, f_stopColor), + SVG_LITERAL_ATTRIBUTE(stop-opacity, f_stopOpacity), + SVG_ATTRIBUTE(stroke), + SVG_LITERAL_ATTRIBUTE(stroke-dasharray, f_strokeDasharray), + SVG_LITERAL_ATTRIBUTE(stroke-linecap, f_strokeLinecap), + SVG_LITERAL_ATTRIBUTE(stroke-linejoin, f_strokeLinejoin), + SVG_LITERAL_ATTRIBUTE(stroke-miterlimit, f_strokeMiterlimit), + SVG_LITERAL_ATTRIBUTE(stroke-width, f_strokeWidth), + SVG_ATTRIBUTE(style), + SVG_ATTRIBUTE(transform) +}; + +const int SkSVGPaint::kAttributesSize = SK_ARRAY_COUNT(SkSVGPaint::gAttributes); + +SkSVGPaint::SkSVGPaint() : fNext(NULL) { +} + +SkString* SkSVGPaint::operator[](int index) { + SkASSERT(index >= 0); + SkASSERT(index < &fTerminal - &fInitial); + SkASSERT(&fTerminal - &fInitial == kTerminal - kInitial); + SkString* result = &fInitial + index + 1; + return result; +} + +void SkSVGPaint::addAttribute(SkSVGParser& parser, int attrIndex, + const char* attrValue, size_t attrLength) { + SkString* attr = (*this)[attrIndex]; + switch(attrIndex) { + case kClipPath: + case kClipRule: + case kEnableBackground: + case kFill: + case kFillRule: + case kFilter: + case kFontFamily: + case kFontSize: + case kLetterSpacing: + case kMask: + case kOpacity: + case kStopColor: + case kStopOpacity: + case kStroke: + case kStroke_Dasharray: + case kStroke_Linecap: + case kStroke_Linejoin: + case kStroke_Miterlimit: + case kStroke_Width: + case kTransform: + attr->set(attrValue, attrLength); + return; + case kStyle: { + // iterate through colon / semi-colon delimited pairs + int pairs = SkParse::Count(attrValue, ';'); + const char* attrEnd = attrValue + attrLength; + do { + const char* end = strchr(attrValue, ';'); + if (end == NULL) + end = attrEnd; + const char* delimiter = strchr(attrValue, ':'); + SkASSERT(delimiter != 0 && delimiter < end); + int index = parser.findAttribute(this, attrValue, (int) (delimiter - attrValue), true); + SkASSERT(index >= 0); + delimiter++; + addAttribute(parser, index, delimiter, (int) (end - delimiter)); + attrValue = end + 1; + } while (--pairs); + return; + } + default: + SkASSERT(0); + } +} + +bool SkSVGPaint::flush(SkSVGParser& parser, bool isFlushable, bool isDef) { + SkSVGPaint current; + SkSVGPaint* walking = parser.fHead; + int index; + while (walking != NULL) { + for (index = kInitial + 1; index < kTerminal; index++) { + SkString* lastAttr = (*walking)[index]; + if (lastAttr->size() == 0) + continue; + if (current[index]->size() > 0) + continue; + current[index]->set(*lastAttr); + } + walking = walking->fNext; + } + bool paintChanged = false; + SkSVGPaint& lastState = parser.fLastFlush; + if (isFlushable == false) { + if (isDef == true) { + if (current.f_mask.size() > 0 && current.f_mask.equals(lastState.f_mask) == false) { + SkSVGElement* found; + const char* idStart = strchr(current.f_mask.c_str(), '#'); + SkASSERT(idStart); + SkString id(idStart + 1, strlen(idStart) - 2); + bool itsFound = parser.fIDs.find(id.c_str(), &found); + SkASSERT(itsFound); + SkSVGElement* gradient = found->getGradient(); + if (gradient) { + gradient->write(parser, current.f_fill); + gradient->write(parser, current.f_stroke); + } + } + } + goto setLast; + } + { + bool changed[kTerminal]; + memset(changed, 0, sizeof(changed)); + for (index = kInitial + 1; index < kTerminal; index++) { + if (index == kTransform || index == kClipPath || index == kStopColor || index == kStopOpacity || + index == kClipRule || index == kFillRule) + continue; + SkString* lastAttr = lastState[index]; + SkString* currentAttr = current[index]; + paintChanged |= changed[index] = lastAttr->equals(*currentAttr) == false; + } + if (paintChanged) { + if (current.f_mask.size() > 0) { + if (current.f_fill.equals("none") == false && strncmp(current.f_fill.c_str(), "url(#", 5) != 0) { + SkASSERT(current.f_fill.c_str()[0] == '#'); + SkString replacement("url(#mask"); + replacement.append(current.f_fill.c_str() + 1); + replacement.appendUnichar(')'); + current.f_fill.set(replacement); + } + if (current.f_stroke.equals("none") == false && strncmp(current.f_stroke.c_str(), "url(#", 5) != 0) { + SkASSERT(current.f_stroke.c_str()[0] == '#'); + SkString replacement("url(#mask"); + replacement.append(current.f_stroke.c_str() + 1); + replacement.appendUnichar(')'); + current.f_stroke.set(replacement); + } + } + if (current.f_fill.equals("none") && current.f_stroke.equals("none")) + current.f_opacity.set("0"); + if (parser.fSuppressPaint == false) { + parser._startElement("paint"); + bool success = writeChangedAttributes(parser, current, changed); + if (success == false) + return paintChanged; + success = writeChangedElements(parser, current, changed); + if (success == false) + return paintChanged; + parser._endElement(); // paint + } + } + } +setLast: + for (index = kInitial + 1; index < kTerminal; index++) { + SkString* lastAttr = lastState[index]; + SkString* currentAttr = current[index]; + lastAttr->set(*currentAttr); + } + return paintChanged; +} + +int SkSVGPaint::getAttributes(const SkSVGAttribute** attrPtr) { + *attrPtr = gAttributes; + return kAttributesSize; +} + +void SkSVGPaint::setSave(SkSVGParser& parser) { + SkTDArray<SkString*> clips; + SkSVGPaint* walking = parser.fHead; + int index; + SkMatrix sum; + sum.reset(); + while (walking != NULL) { + for (index = kInitial + 1; index < kTerminal; index++) { + SkString* lastAttr = (*walking)[index]; + if (lastAttr->size() == 0) + continue; + if (index == kTransform) { + const char* str = lastAttr->c_str(); + SkASSERT(strncmp(str, "matrix(", 7) == 0); + str += 6; + const char* strEnd = strrchr(str, ')'); + SkASSERT(strEnd != NULL); + SkString mat(str, strEnd - str); + SkSVGParser::ConvertToArray(mat); + SkScalar values[6]; + SkParse::FindScalars(mat.c_str() + 1, values, 6); + SkMatrix matrix; + matrix.reset(); + matrix.setScaleX(values[0]); + matrix.setSkewY(values[1]); + matrix.setSkewX(values[2]); + matrix.setScaleY(values[3]); + matrix.setTranslateX(values[4]); + matrix.setTranslateY(values[5]); + sum.setConcat(matrix, sum); + continue; + } + if ( index == kClipPath) + *clips.insert(0) = lastAttr; + } + walking = walking->fNext; + } + if ((sum == parser.fLastTransform) == false) { + SkMatrix inverse; + bool success = parser.fLastTransform.invert(&inverse); + SkASSERT(success == true); + SkMatrix output; + output.setConcat(inverse, sum); + parser.fLastTransform = sum; + SkString outputStr; + outputStr.appendUnichar('['); + outputStr.appendScalar(output.getScaleX()); + outputStr.appendUnichar(','); + outputStr.appendScalar(output.getSkewX()); + outputStr.appendUnichar(','); + outputStr.appendScalar(output.getTranslateX()); + outputStr.appendUnichar(','); + outputStr.appendScalar(output.getSkewY()); + outputStr.appendUnichar(','); + outputStr.appendScalar(output.getScaleY()); + outputStr.appendUnichar(','); + outputStr.appendScalar(output.getTranslateY()); + outputStr.appendUnichar(','); + outputStr.appendScalar(output.getPerspX()); + outputStr.appendUnichar(','); + outputStr.appendScalar(output.getPerspY()); + outputStr.append(",1]"); + parser._startElement("matrix"); + parser._addAttributeLen("matrix", outputStr.c_str(), outputStr.size()); + parser._endElement(); + } +#if 0 // incomplete + if (parser.fTransformClips.size() > 0) { + // need to reset the clip when the 'g' scope is ended + parser._startElement("add"); + const char* start = strchr(current->f_clipPath.c_str(), '#') + 1; + SkASSERT(start); + parser._addAttributeLen("use", start, strlen(start) - 1); + parser._endElement(); // clip + } +#endif +} + +bool SkSVGPaint::writeChangedAttributes(SkSVGParser& parser, + SkSVGPaint& current, bool* changed) { + SkSVGPaint& lastState = parser.fLastFlush; + for (int index = kInitial + 1; index < kTerminal; index++) { + if (changed[index] == false) + continue; + SkString* topAttr = current[index]; + size_t attrLength = topAttr->size(); + if (attrLength == 0) + continue; + const char* attrValue = topAttr->c_str(); + SkString* lastAttr = lastState[index]; + switch(index) { + case kClipPath: + case kClipRule: + case kEnableBackground: + break; + case kFill: + if (topAttr->equals("none") == false && lastAttr->equals("none") == true) + parser._addAttribute("stroke", "false"); + goto fillStrokeAttrCommon; + case kFillRule: + case kFilter: + case kFontFamily: + break; + case kFontSize: + parser._addAttributeLen("textSize", attrValue, attrLength); + break; + case kLetterSpacing: + parser._addAttributeLen("textTracking", attrValue, attrLength); + break; + case kMask: + break; + case kOpacity: + break; + case kStopColor: + break; + case kStopOpacity: + break; + case kStroke: + if (topAttr->equals("none") == false && lastAttr->equals("none") == true) + parser._addAttribute("stroke", "true"); +fillStrokeAttrCommon: + if (strncmp(attrValue, "url(", 4) == 0) { + SkASSERT(attrValue[4] == '#'); + const char* idStart = attrValue + 5; + char* idEnd = strrchr(attrValue, ')'); + SkASSERT(idStart < idEnd); + SkString id(idStart, idEnd - idStart); + SkSVGElement* found; + if (strncmp(id.c_str(), "mask", 4) != 0) { + bool itsFound = parser.fIDs.find(id.c_str(), &found); + SkASSERT(itsFound); + SkASSERT(found->getType() == SkSVGType_LinearGradient || + found->getType() == SkSVGType_RadialGradient); + } + parser._addAttribute("shader", id.c_str()); + } + break; + case kStroke_Dasharray: + break; + case kStroke_Linecap: + parser._addAttributeLen("strokeCap", attrValue, attrLength); + break; + case kStroke_Linejoin: + parser._addAttributeLen("strokeJoin", attrValue, attrLength); + break; + case kStroke_Miterlimit: + parser._addAttributeLen("strokeMiter", attrValue, attrLength); + break; + case kStroke_Width: + parser._addAttributeLen("strokeWidth", attrValue, attrLength); + case kStyle: + case kTransform: + break; + default: + SkASSERT(0); + return false; + } + } + return true; +} + +bool SkSVGPaint::writeChangedElements(SkSVGParser& parser, + SkSVGPaint& current, bool* changed) { + SkSVGPaint& lastState = parser.fLastFlush; + for (int index = kInitial + 1; index < kTerminal; index++) { + SkString* topAttr = current[index]; + size_t attrLength = topAttr->size(); + if (attrLength == 0) + continue; + const char* attrValue = topAttr->c_str(); + SkString* lastAttr = lastState[index]; + switch(index) { + case kClipPath: + case kClipRule: + // !!! need to add this outside of paint + break; + case kEnableBackground: + // !!! don't know what to do with this + break; + case kFill: + goto addColor; + case kFillRule: + case kFilter: + break; + case kFontFamily: + parser._startElement("typeface"); + parser._addAttributeLen("fontName", attrValue, attrLength); + parser._endElement(); // typeface + break; + case kFontSize: + case kLetterSpacing: + break; + case kMask: + case kOpacity: + if (changed[kStroke] == false && changed[kFill] == false) { + parser._startElement("color"); + SkString& opacity = current.f_opacity; + parser._addAttributeLen("color", parser.fLastColor.c_str(), parser.fLastColor.size()); + parser._addAttributeLen("alpha", opacity.c_str(), opacity.size()); + parser._endElement(); // color + } + break; + case kStopColor: + break; + case kStopOpacity: + break; + case kStroke: +addColor: + if (strncmp(lastAttr->c_str(), "url(", 4) == 0 && strncmp(attrValue, "url(", 4) != 0) { + parser._startElement("shader"); + parser._endElement(); + } + if (topAttr->equals(*lastAttr)) + continue; + { + bool urlRef = strncmp(attrValue, "url(", 4) == 0; + bool colorNone = strcmp(attrValue, "none") == 0; + bool lastEqual = parser.fLastColor.equals(attrValue, attrLength); + bool newColor = urlRef == false && colorNone == false && lastEqual == false; + if (newColor || changed[kOpacity]) { + parser._startElement("color"); + if (newColor || changed[kOpacity]) { + parser._addAttributeLen("color", attrValue, attrLength); + parser.fLastColor.set(attrValue, attrLength); + } + if (changed[kOpacity]) { + SkString& opacity = current.f_opacity; + parser._addAttributeLen("alpha", opacity.c_str(), opacity.size()); + } + parser._endElement(); // color + } + } + break; + case kStroke_Dasharray: + parser._startElement("dash"); + SkSVGParser::ConvertToArray(*topAttr); + parser._addAttribute("intervals", topAttr->c_str()); + parser._endElement(); // dash + break; + case kStroke_Linecap: + case kStroke_Linejoin: + case kStroke_Miterlimit: + case kStroke_Width: + case kStyle: + case kTransform: + break; + default: + SkASSERT(0); + return false; + } + } + return true; +} + +void SkSVGPaint::Push(SkSVGPaint** head, SkSVGPaint* newRecord) { + newRecord->fNext = *head; + *head = newRecord; +} + +void SkSVGPaint::Pop(SkSVGPaint** head) { + SkSVGPaint* next = (*head)->fNext; + *head = next; +} + |