From 2d961e086bb40b371b1a667536fa089794847368 Mon Sep 17 00:00:00 2001 From: fmalita Date: Thu, 11 Aug 2016 09:16:29 -0700 Subject: [SVGDom] Add more presentation attributes. Implement proper presentation attribute inheritance, and add support for * fill-opacity * stroke-linecap * stroke-linejoin * stroke-opacity * stroke-width R=robertphillips@google.com,stephana@google.com GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2234153002 Review-Url: https://codereview.chromium.org/2234153002 --- experimental/svg/model/SkSVGAttribute.cpp | 31 ++-- experimental/svg/model/SkSVGAttribute.h | 28 ++-- experimental/svg/model/SkSVGAttributeParser.cpp | 68 ++++++++ experimental/svg/model/SkSVGAttributeParser.h | 3 + experimental/svg/model/SkSVGDOM.cpp | 77 ++++++++-- experimental/svg/model/SkSVGNode.cpp | 37 ++++- experimental/svg/model/SkSVGRenderContext.cpp | 196 +++++++++++++++++++----- experimental/svg/model/SkSVGRenderContext.h | 37 ++--- experimental/svg/model/SkSVGShape.cpp | 4 +- experimental/svg/model/SkSVGTypes.h | 90 +++++++++++ experimental/svg/model/SkSVGValue.h | 9 ++ 11 files changed, 460 insertions(+), 120 deletions(-) (limited to 'experimental/svg') diff --git a/experimental/svg/model/SkSVGAttribute.cpp b/experimental/svg/model/SkSVGAttribute.cpp index 2f1cace773..9d6b193e04 100644 --- a/experimental/svg/model/SkSVGAttribute.cpp +++ b/experimental/svg/model/SkSVGAttribute.cpp @@ -6,29 +6,18 @@ */ #include "SkSVGAttribute.h" -#include "SkSVGRenderContext.h" -SkSVGPresentationAttributes::SkSVGPresentationAttributes() - : fFillIsSet(false) - , fStrokeIsSet(false) { } - -void SkSVGPresentationAttributes::setFill(const SkSVGColorType& c) { - fFill = c; - fFillIsSet = true; -} - -void SkSVGPresentationAttributes::setStroke(const SkSVGColorType& c) { - fStroke = c; - fStrokeIsSet = true; -} +SkSVGPresentationAttributes SkSVGPresentationAttributes::MakeInitial() { + SkSVGPresentationAttributes result; + result.fFill.set(SkSVGPaint(SkSVGColorType(SK_ColorBLACK))); + result.fFillOpacity.set(SkSVGNumberType(1)); -void SkSVGPresentationAttributes::applyTo(SkSVGRenderContext* ctx) const { - if (fFillIsSet) { - ctx->writablePresentationContext()->setFillColor(fFill); - } + result.fStroke.set(SkSVGPaint(SkSVGPaint::Type::kNone)); + result.fStrokeLineCap.set(SkSVGLineCap(SkSVGLineCap::Type::kButt)); + result.fStrokeLineJoin.set(SkSVGLineJoin(SkSVGLineJoin::Type::kMiter)); + result.fStrokeOpacity.set(SkSVGNumberType(1)); + result.fStrokeWidth.set(SkSVGLength(1)); - if (fStrokeIsSet) { - ctx->writablePresentationContext()->setStrokeColor(fStroke); - } + return result; } diff --git a/experimental/svg/model/SkSVGAttribute.h b/experimental/svg/model/SkSVGAttribute.h index 1d7c96eb22..4ac595d526 100644 --- a/experimental/svg/model/SkSVGAttribute.h +++ b/experimental/svg/model/SkSVGAttribute.h @@ -16,10 +16,15 @@ class SkSVGRenderContext; enum class SkSVGAttribute { kD, kFill, + kFillOpacity, kHeight, kRx, kRy, kStroke, + kStrokeOpacity, + kStrokeLineCap, + kStrokeLineJoin, + kStrokeWidth, kTransform, kViewBox, kWidth, @@ -29,22 +34,19 @@ enum class SkSVGAttribute { kUnknown, }; -class SkSVGPresentationAttributes { -public: - SkSVGPresentationAttributes(); +struct SkSVGPresentationAttributes { + static SkSVGPresentationAttributes MakeInitial(); - void setFill(const SkSVGColorType&); - void setStroke(const SkSVGColorType&); + // TODO: SkTLazy adds an extra ptr per attribute; refactor to reduce overhead. - void applyTo(SkSVGRenderContext*) const; + SkTLazy fFill; + SkTLazy fFillOpacity; -private: - // Color only for now. - SkSVGColorType fFill; - SkSVGColorType fStroke; - - unsigned fFillIsSet : 1; - unsigned fStrokeIsSet : 1; + SkTLazy fStroke; + SkTLazy fStrokeLineCap; + SkTLazy fStrokeLineJoin; + SkTLazy fStrokeOpacity; + SkTLazy fStrokeWidth; }; #endif // SkSVGAttribute_DEFINED diff --git a/experimental/svg/model/SkSVGAttributeParser.cpp b/experimental/svg/model/SkSVGAttributeParser.cpp index 7a2561cdf7..308eb62ba6 100644 --- a/experimental/svg/model/SkSVGAttributeParser.cpp +++ b/experimental/svg/model/SkSVGAttributeParser.cpp @@ -359,3 +359,71 @@ bool SkSVGAttributeParser::parseTransform(SkSVGTransformType* t) { *t = SkSVGTransformType(matrix); return true; } + +// https://www.w3.org/TR/SVG/painting.html#SpecifyingPaint +bool SkSVGAttributeParser::parsePaint(SkSVGPaint* paint) { + SkSVGColorType c; + bool parsedValue = false; + if (this->parseColor(&c)) { + *paint = SkSVGPaint(c); + parsedValue = true; + } else if (this->parseExpectedStringToken("none")) { + *paint = SkSVGPaint(SkSVGPaint::Type::kNone); + parsedValue = true; + } else if (this->parseExpectedStringToken("currentColor")) { + *paint = SkSVGPaint(SkSVGPaint::Type::kCurrentColor); + parsedValue = true; + } else if (this->parseExpectedStringToken("inherit")) { + *paint = SkSVGPaint(SkSVGPaint::Type::kInherit); + parsedValue = true; + } + return parsedValue && this->parseEOSToken(); +} + +// https://www.w3.org/TR/SVG/painting.html#StrokeLinecapProperty +bool SkSVGAttributeParser::parseLineCap(SkSVGLineCap* cap) { + static const struct { + SkSVGLineCap::Type fType; + const char* fName; + } gCapInfo[] = { + { SkSVGLineCap::Type::kButt , "butt" }, + { SkSVGLineCap::Type::kRound , "round" }, + { SkSVGLineCap::Type::kSquare , "square" }, + { SkSVGLineCap::Type::kInherit, "inherit" }, + }; + + bool parsedValue = false; + for (size_t i = 0; i < SK_ARRAY_COUNT(gCapInfo); ++i) { + if (this->parseExpectedStringToken(gCapInfo[i].fName)) { + *cap = SkSVGLineCap(gCapInfo[i].fType); + parsedValue = true; + break; + } + } + + return parsedValue && this->parseEOSToken(); +} + +// https://www.w3.org/TR/SVG/painting.html#StrokeLinejoinProperty +bool SkSVGAttributeParser::parseLineJoin(SkSVGLineJoin* join) { + static const struct { + SkSVGLineJoin::Type fType; + const char* fName; + } gJoinInfo[] = { + { SkSVGLineJoin::Type::kMiter , "miter" }, + { SkSVGLineJoin::Type::kRound , "round" }, + { SkSVGLineJoin::Type::kBevel , "bevel" }, + { SkSVGLineJoin::Type::kInherit, "inherit" }, + }; + + bool parsedValue = false; + for (size_t i = 0; i < SK_ARRAY_COUNT(gJoinInfo); ++i) { + if (this->parseExpectedStringToken(gJoinInfo[i].fName)) { + *join = SkSVGLineJoin(gJoinInfo[i].fType); + parsedValue = true; + break; + } + } + + return parsedValue && this->parseEOSToken(); +} diff --git a/experimental/svg/model/SkSVGAttributeParser.h b/experimental/svg/model/SkSVGAttributeParser.h index c616113d6b..637bf4a260 100644 --- a/experimental/svg/model/SkSVGAttributeParser.h +++ b/experimental/svg/model/SkSVGAttributeParser.h @@ -19,6 +19,9 @@ public: bool parseLength(SkSVGLength*); bool parseViewBox(SkSVGViewBoxType*); bool parseTransform(SkSVGTransformType*); + bool parsePaint(SkSVGPaint*); + bool parseLineCap(SkSVGLineCap*); + bool parseLineJoin(SkSVGLineJoin*); private: // Stack-only diff --git a/experimental/svg/model/SkSVGDOM.cpp b/experimental/svg/model/SkSVGDOM.cpp index 2d1972b92f..6e4bc49a9f 100644 --- a/experimental/svg/model/SkSVGDOM.cpp +++ b/experimental/svg/model/SkSVGDOM.cpp @@ -25,13 +25,17 @@ namespace { bool SetPaintAttribute(const sk_sp& node, SkSVGAttribute attr, const char* stringValue) { - SkSVGColorType color; + SkSVGPaint paint; SkSVGAttributeParser parser(stringValue); - if (!parser.parseColor(&color)) { - return false; + if (!parser.parsePaint(&paint)) { + // Until we have paint server support, failing here will cause default/all-black rendering. + // It's better to just not draw for now. + paint = SkSVGPaint(SkSVGPaint::Type::kNone); + + // return false; } - node->setAttribute(attr, SkSVGColorValue(color)); + node->setAttribute(attr, SkSVGPaintValue(paint)); return true; } @@ -70,6 +74,18 @@ bool SetLengthAttribute(const sk_sp& node, SkSVGAttribute attr, return true; } +bool SetNumberAttribute(const sk_sp& node, SkSVGAttribute attr, + const char* stringValue) { + SkSVGNumberType number; + SkSVGAttributeParser parser(stringValue); + if (!parser.parseNumber(&number)) { + return false; + } + + node->setAttribute(attr, SkSVGNumberValue(number)); + return true; +} + bool SetViewBoxAttribute(const sk_sp& node, SkSVGAttribute attr, const char* stringValue) { SkSVGViewBoxType viewBox; @@ -82,6 +98,30 @@ bool SetViewBoxAttribute(const sk_sp& node, SkSVGAttribute attr, return true; } +bool SetLineCapAttribute(const sk_sp& node, SkSVGAttribute attr, + const char* stringValue) { + SkSVGLineCap lineCap; + SkSVGAttributeParser parser(stringValue); + if (!parser.parseLineCap(&lineCap)) { + return false; + } + + node->setAttribute(attr, SkSVGLineCapValue(lineCap)); + return true; +} + +bool SetLineJoinAttribute(const sk_sp& node, SkSVGAttribute attr, + const char* stringValue) { + SkSVGLineJoin lineJoin; + SkSVGAttributeParser parser(stringValue); + if (!parser.parseLineJoin(&lineJoin)) { + return false; + } + + node->setAttribute(attr, SkSVGLineJoinValue(lineJoin)); + return true; +} + SkString TrimmedString(const char* first, const char* last) { SkASSERT(first); SkASSERT(last); @@ -160,18 +200,23 @@ struct AttrParseInfo { }; SortedDictionaryEntry gAttributeParseInfo[] = { - { "d" , { SkSVGAttribute::kD , SetPathDataAttribute }}, - { "fill" , { SkSVGAttribute::kFill , SetPaintAttribute }}, - { "height" , { SkSVGAttribute::kHeight , SetLengthAttribute }}, - { "rx" , { SkSVGAttribute::kRx , SetLengthAttribute }}, - { "ry" , { SkSVGAttribute::kRy , SetLengthAttribute }}, - { "stroke" , { SkSVGAttribute::kStroke , SetPaintAttribute }}, - { "style" , { SkSVGAttribute::kUnknown , SetStyleAttributes }}, - { "transform", { SkSVGAttribute::kTransform, SetTransformAttribute }}, - { "viewBox" , { SkSVGAttribute::kViewBox , SetViewBoxAttribute }}, - { "width" , { SkSVGAttribute::kWidth , SetLengthAttribute }}, - { "x" , { SkSVGAttribute::kX , SetLengthAttribute }}, - { "y" , { SkSVGAttribute::kY , SetLengthAttribute }}, + { "d" , { SkSVGAttribute::kD , SetPathDataAttribute }}, + { "fill" , { SkSVGAttribute::kFill , SetPaintAttribute }}, + { "fill-opacity" , { SkSVGAttribute::kFillOpacity , SetNumberAttribute }}, + { "height" , { SkSVGAttribute::kHeight , SetLengthAttribute }}, + { "rx" , { SkSVGAttribute::kRx , SetLengthAttribute }}, + { "ry" , { SkSVGAttribute::kRy , SetLengthAttribute }}, + { "stroke" , { SkSVGAttribute::kStroke , SetPaintAttribute }}, + { "stroke-linecap" , { SkSVGAttribute::kStrokeLineCap , SetLineCapAttribute }}, + { "stroke-linejoin", { SkSVGAttribute::kStrokeLineJoin, SetLineJoinAttribute }}, + { "stroke-opacity" , { SkSVGAttribute::kStrokeOpacity , SetNumberAttribute }}, + { "stroke-width" , { SkSVGAttribute::kStrokeWidth , SetLengthAttribute }}, + { "style" , { SkSVGAttribute::kUnknown , SetStyleAttributes }}, + { "transform" , { SkSVGAttribute::kTransform , SetTransformAttribute }}, + { "viewBox" , { SkSVGAttribute::kViewBox , SetViewBoxAttribute }}, + { "width" , { SkSVGAttribute::kWidth , SetLengthAttribute }}, + { "x" , { SkSVGAttribute::kX , SetLengthAttribute }}, + { "y" , { SkSVGAttribute::kY , SetLengthAttribute }}, }; SortedDictionaryEntry(*)()> gTagFactories[] = { diff --git a/experimental/svg/model/SkSVGNode.cpp b/experimental/svg/model/SkSVGNode.cpp index 34c6e17d61..d60c984cea 100644 --- a/experimental/svg/model/SkSVGNode.cpp +++ b/experimental/svg/model/SkSVGNode.cpp @@ -25,7 +25,7 @@ void SkSVGNode::render(const SkSVGRenderContext& ctx) const { } bool SkSVGNode::onPrepareToRender(SkSVGRenderContext* ctx) const { - fPresentationAttributes.applyTo(ctx); + ctx->applyPresentationAttributes(fPresentationAttributes); return true; } @@ -36,13 +36,40 @@ void SkSVGNode::setAttribute(SkSVGAttribute attr, const SkSVGValue& v) { void SkSVGNode::onSetAttribute(SkSVGAttribute attr, const SkSVGValue& v) { switch (attr) { case SkSVGAttribute::kFill: - if (const SkSVGColorValue* color = v.as()) { - fPresentationAttributes.setFill(*color); + if (const SkSVGPaintValue* paint = v.as()) { + fPresentationAttributes.fFill.set(*paint); + } + break; + case SkSVGAttribute::kFillOpacity: + if (const SkSVGNumberValue* opacity = v.as()) { + fPresentationAttributes.fFillOpacity.set( + SkSVGNumberType(SkTPin((*opacity)->value(), 0, 1))); } break; case SkSVGAttribute::kStroke: - if (const SkSVGColorValue* color = v.as()) { - fPresentationAttributes.setStroke(*color); + if (const SkSVGPaintValue* paint = v.as()) { + fPresentationAttributes.fStroke.set(*paint); + } + break; + case SkSVGAttribute::kStrokeOpacity: + if (const SkSVGNumberValue* opacity = v.as()) { + fPresentationAttributes.fStrokeOpacity.set( + SkSVGNumberType(SkTPin((*opacity)->value(), 0, 1))); + } + break; + case SkSVGAttribute::kStrokeLineCap: + if (const SkSVGLineCapValue* lineCap = v.as()) { + fPresentationAttributes.fStrokeLineCap.set(*lineCap); + } + break; + case SkSVGAttribute::kStrokeLineJoin: + if (const SkSVGLineJoinValue* lineJoin = v.as()) { + fPresentationAttributes.fStrokeLineJoin.set(*lineJoin); + } + break; + case SkSVGAttribute::kStrokeWidth: + if (const SkSVGLengthValue* strokeWidth = v.as()) { + fPresentationAttributes.fStrokeWidth.set(*strokeWidth); } break; default: diff --git a/experimental/svg/model/SkSVGRenderContext.cpp b/experimental/svg/model/SkSVGRenderContext.cpp index e902d4ecf3..ee92c47d54 100644 --- a/experimental/svg/model/SkSVGRenderContext.cpp +++ b/experimental/svg/model/SkSVGRenderContext.cpp @@ -6,6 +6,7 @@ */ #include "SkCanvas.h" +#include "SkSVGAttribute.h" #include "SkSVGRenderContext.h" #include "SkSVGTypes.h" @@ -30,17 +31,15 @@ SkScalar length_size_for_type(const SkSize& viewport, SkSVGLengthContext::Length SkScalar SkSVGLengthContext::resolve(const SkSVGLength& l, LengthType t) const { switch (l.unit()) { case SkSVGLength::Unit::kNumber: + // Fall through. + case SkSVGLength::Unit::kPX: return l.value(); - break; case SkSVGLength::Unit::kPercentage: return l.value() * length_size_for_type(fViewport, t) / 100; - break; default: SkDebugf("unsupported unit type: <%d>\n", l.unit()); - break; + return 0; } - - return 0; } SkRect SkSVGLengthContext::resolveRect(const SkSVGLength& x, const SkSVGLength& y, @@ -52,55 +51,135 @@ SkRect SkSVGLengthContext::resolveRect(const SkSVGLength& x, const SkSVGLength& this->resolve(h, SkSVGLengthContext::LengthType::kVertical)); } -SkSVGPresentationContext::SkSVGPresentationContext() {} +namespace { -SkSVGPresentationContext::SkSVGPresentationContext(const SkSVGPresentationContext& o) { - this->initFrom(o); +SkPaint::Cap toSkCap(const SkSVGLineCap& cap) { + switch (cap.type()) { + case SkSVGLineCap::Type::kButt: + return SkPaint::kButt_Cap; + case SkSVGLineCap::Type::kRound: + return SkPaint::kRound_Cap; + case SkSVGLineCap::Type::kSquare: + return SkPaint::kSquare_Cap; + default: + SkASSERT(false); + return SkPaint::kButt_Cap; + } } -SkSVGPresentationContext& SkSVGPresentationContext::operator=(const SkSVGPresentationContext& o) { - this->initFrom(o); - return *this; +SkPaint::Join toSkJoin(const SkSVGLineJoin& join) { + switch (join.type()) { + case SkSVGLineJoin::Type::kMiter: + return SkPaint::kMiter_Join; + case SkSVGLineJoin::Type::kRound: + return SkPaint::kRound_Join; + case SkSVGLineJoin::Type::kBevel: + return SkPaint::kBevel_Join; + default: + SkASSERT(false); + return SkPaint::kMiter_Join; + } } -void SkSVGPresentationContext::initFrom(const SkSVGPresentationContext& other) { - if (other.fFill.isValid()) { - fFill.set(*other.fFill.get()); - } else { - fFill.reset(); +void applySvgPaint(const SkSVGPaint& svgPaint, SkPaint* p) { + switch (svgPaint.type()) { + case SkSVGPaint::Type::kColor: + p->setColor(SkColorSetA(svgPaint.color(), p->getAlpha())); + break; + case SkSVGPaint::Type::kCurrentColor: + SkDebugf("unimplemented 'currentColor' paint type"); + // Fall through. + case SkSVGPaint::Type::kNone: + // Fall through. + case SkSVGPaint::Type::kInherit: + break; } +} - if (other.fStroke.isValid()) { - fStroke.set(*other.fStroke.get()); - } else { - fStroke.reset(); - } +// Commit the selected attribute to the paint cache. +template +void commitToPaint(const SkSVGPresentationAttributes&, + const SkSVGLengthContext&, + SkSVGPresentationContext*); + +template <> +void commitToPaint(const SkSVGPresentationAttributes& attrs, + const SkSVGLengthContext&, + SkSVGPresentationContext* pctx) { + applySvgPaint(*attrs.fFill.get(), &pctx->fFillPaint); } -SkPaint& SkSVGPresentationContext::ensureFill() { - if (!fFill.isValid()) { - fFill.init(); - fFill.get()->setStyle(SkPaint::kFill_Style); - fFill.get()->setAntiAlias(true); +template <> +void commitToPaint(const SkSVGPresentationAttributes& attrs, + const SkSVGLengthContext&, + SkSVGPresentationContext* pctx) { + applySvgPaint(*attrs.fStroke.get(), &pctx->fStrokePaint); +} + +template <> +void commitToPaint(const SkSVGPresentationAttributes& attrs, + const SkSVGLengthContext&, + SkSVGPresentationContext* pctx) { + pctx->fFillPaint.setAlpha(static_cast(*attrs.fFillOpacity.get() * 255)); +} + +template <> +void commitToPaint(const SkSVGPresentationAttributes& attrs, + const SkSVGLengthContext&, + SkSVGPresentationContext* pctx) { + const auto& cap = *attrs.fStrokeLineCap.get(); + if (cap.type() != SkSVGLineCap::Type::kInherit) { + pctx->fStrokePaint.setStrokeCap(toSkCap(cap)); } - return *fFill.get(); } -SkPaint& SkSVGPresentationContext::ensureStroke() { - if (!fStroke.isValid()) { - fStroke.init(); - fStroke.get()->setStyle(SkPaint::kStroke_Style); - fStroke.get()->setAntiAlias(true); +template <> +void commitToPaint(const SkSVGPresentationAttributes& attrs, + const SkSVGLengthContext&, + SkSVGPresentationContext* pctx) { + const auto& join = *attrs.fStrokeLineJoin.get(); + if (join.type() != SkSVGLineJoin::Type::kInherit) { + pctx->fStrokePaint.setStrokeJoin(toSkJoin(join)); } - return *fStroke.get(); } -void SkSVGPresentationContext::setFillColor(SkColor color) { - this->ensureFill().setColor(color); +template <> +void commitToPaint(const SkSVGPresentationAttributes& attrs, + const SkSVGLengthContext&, + SkSVGPresentationContext* pctx) { + pctx->fStrokePaint.setAlpha(static_cast(*attrs.fStrokeOpacity.get() * 255)); } -void SkSVGPresentationContext::setStrokeColor(SkColor color) { - this->ensureStroke().setColor(color); +template <> +void commitToPaint(const SkSVGPresentationAttributes& attrs, + const SkSVGLengthContext& lctx, + SkSVGPresentationContext* pctx) { + auto strokeWidth = lctx.resolve(*attrs.fStrokeWidth.get(), + SkSVGLengthContext::LengthType::kOther); + pctx->fStrokePaint.setStrokeWidth(strokeWidth); +} + +} // anonymous ns + +SkSVGPresentationContext::SkSVGPresentationContext() + : fInherited(SkSVGPresentationAttributes::MakeInitial()) { + + fFillPaint.setStyle(SkPaint::kFill_Style); + fStrokePaint.setStyle(SkPaint::kStroke_Style); + + // TODO: drive AA off presentation attrs also (shape-rendering?) + fFillPaint.setAntiAlias(true); + fStrokePaint.setAntiAlias(true); + + // Commit initial values to the paint cache. + SkSVGLengthContext dummy(SkSize::Make(0, 0)); + commitToPaint(fInherited, dummy, this); + commitToPaint(fInherited, dummy, this); + commitToPaint(fInherited, dummy, this); + commitToPaint(fInherited, dummy, this); + commitToPaint(fInherited, dummy, this); + commitToPaint(fInherited, dummy, this); + commitToPaint(fInherited, dummy, this); } SkSVGRenderContext::SkSVGRenderContext(SkCanvas* canvas, @@ -112,10 +191,47 @@ SkSVGRenderContext::SkSVGRenderContext(SkCanvas* canvas, , fCanvasSaveCount(canvas->getSaveCount()) {} SkSVGRenderContext::SkSVGRenderContext(const SkSVGRenderContext& other) - : SkSVGRenderContext(other.canvas(), - other.lengthContext(), - other.presentationContext()) {} + : SkSVGRenderContext(other.fCanvas, + *other.fLengthContext, + *other.fPresentationContext) {} SkSVGRenderContext::~SkSVGRenderContext() { fCanvas->restoreToCount(fCanvasSaveCount); } + +void SkSVGRenderContext::applyPresentationAttributes(const SkSVGPresentationAttributes& attrs) { + +#define ApplyLazyInheritedAttribute(ATTR) \ + do { \ + /* All attributes should be defined on the inherited context. */ \ + SkASSERT(fPresentationContext->fInherited.f ## ATTR.isValid()); \ + const auto* value = attrs.f ## ATTR.getMaybeNull(); \ + if (value && *value != *fPresentationContext->fInherited.f ## ATTR.get()) { \ + /* Update the local attribute value */ \ + fPresentationContext.writable()->fInherited.f ## ATTR.set(*value); \ + /* Update the cached paints */ \ + commitToPaint(attrs, *fLengthContext, \ + fPresentationContext.writable()); \ + } \ + } while (false) + + ApplyLazyInheritedAttribute(Fill); + ApplyLazyInheritedAttribute(FillOpacity); + ApplyLazyInheritedAttribute(Stroke); + ApplyLazyInheritedAttribute(StrokeLineCap); + ApplyLazyInheritedAttribute(StrokeLineJoin); + ApplyLazyInheritedAttribute(StrokeOpacity); + ApplyLazyInheritedAttribute(StrokeWidth); + +#undef ApplyLazyInheritedAttribute +} + +const SkPaint* SkSVGRenderContext::fillPaint() const { + const SkSVGPaint::Type paintType = fPresentationContext->fInherited.fFill.get()->type(); + return paintType != SkSVGPaint::Type::kNone ? &fPresentationContext->fFillPaint : nullptr; +} + +const SkPaint* SkSVGRenderContext::strokePaint() const { + const SkSVGPaint::Type paintType = fPresentationContext->fInherited.fStroke.get()->type(); + return paintType != SkSVGPaint::Type::kNone ? &fPresentationContext->fStrokePaint : nullptr; +} diff --git a/experimental/svg/model/SkSVGRenderContext.h b/experimental/svg/model/SkSVGRenderContext.h index 47886d7cb0..e6df725a5c 100644 --- a/experimental/svg/model/SkSVGRenderContext.h +++ b/experimental/svg/model/SkSVGRenderContext.h @@ -11,6 +11,7 @@ #include "SkPaint.h" #include "SkRect.h" #include "SkSize.h" +#include "SkSVGAttribute.h" #include "SkTLazy.h" #include "SkTypes.h" @@ -38,27 +39,17 @@ private: SkSize fViewport; }; -class SkSVGPresentationContext { -public: +struct SkSVGPresentationContext { SkSVGPresentationContext(); - SkSVGPresentationContext(const SkSVGPresentationContext&); - SkSVGPresentationContext& operator=(const SkSVGPresentationContext&); - - const SkPaint* fillPaint() const { return fFill.getMaybeNull(); } - const SkPaint* strokePaint() const { return fStroke.getMaybeNull(); } - - void setFillColor(SkColor); - void setStrokeColor(SkColor); - -private: - void initFrom(const SkSVGPresentationContext&); + SkSVGPresentationContext(const SkSVGPresentationContext&) = default; + SkSVGPresentationContext& operator=(const SkSVGPresentationContext&) = default; - SkPaint& ensureFill(); - SkPaint& ensureStroke(); + // Inherited presentation attributes, computed for the current node. + SkSVGPresentationAttributes fInherited; - // TODO: convert to regular SkPaints and track explicit attribute values instead. - SkTLazy fFill; - SkTLazy fStroke; + // Cached paints, reflecting the current presentation attributes. + SkPaint fFillPaint; + SkPaint fStrokePaint; }; class SkSVGRenderContext { @@ -70,13 +61,13 @@ public: const SkSVGLengthContext& lengthContext() const { return *fLengthContext; } SkSVGLengthContext* writableLengthContext() { return fLengthContext.writable(); } - const SkSVGPresentationContext& presentationContext() const { return *fPresentationContext; } - SkSVGPresentationContext* writablePresentationContext() { - return fPresentationContext.writable(); - } - SkCanvas* canvas() const { return fCanvas; } + void applyPresentationAttributes(const SkSVGPresentationAttributes&); + + const SkPaint* fillPaint() const; + const SkPaint* strokePaint() const; + private: // Stack-only void* operator new(size_t) = delete; diff --git a/experimental/svg/model/SkSVGShape.cpp b/experimental/svg/model/SkSVGShape.cpp index 516d4a2925..38af4c9303 100644 --- a/experimental/svg/model/SkSVGShape.cpp +++ b/experimental/svg/model/SkSVGShape.cpp @@ -12,11 +12,11 @@ SkSVGShape::SkSVGShape(SkSVGTag t) : INHERITED(t) {} void SkSVGShape::onRender(const SkSVGRenderContext& ctx) const { // TODO: this approach forces duplicate geometry resolution in onDraw(); refactor to avoid. - if (const SkPaint* fillPaint = ctx.presentationContext().fillPaint()) { + if (const SkPaint* fillPaint = ctx.fillPaint()) { this->onDraw(ctx.canvas(), ctx.lengthContext(), *fillPaint); } - if (const SkPaint* strokePaint = ctx.presentationContext().strokePaint()) { + if (const SkPaint* strokePaint = ctx.strokePaint()) { this->onDraw(ctx.canvas(), ctx.lengthContext(), *strokePaint); } } diff --git a/experimental/svg/model/SkSVGTypes.h b/experimental/svg/model/SkSVGTypes.h index b2e2db1076..c8330e1088 100644 --- a/experimental/svg/model/SkSVGTypes.h +++ b/experimental/svg/model/SkSVGTypes.h @@ -23,6 +23,13 @@ public: SkSVGPrimitiveTypeWrapper(const SkSVGPrimitiveTypeWrapper&) = default; SkSVGPrimitiveTypeWrapper& operator=(const SkSVGPrimitiveTypeWrapper&) = default; + bool operator==(const SkSVGPrimitiveTypeWrapper& other) const { + return fValue == other.fValue; + } + bool operator!=(const SkSVGPrimitiveTypeWrapper& other) const { + return !(*this == other); + } + const T& value() const { return fValue; } operator const T&() const { return fValue; } @@ -57,6 +64,11 @@ public: SkSVGLength(const SkSVGLength&) = default; SkSVGLength& operator=(const SkSVGLength&) = default; + bool operator==(const SkSVGLength& other) const { + return fUnit == other.fUnit && fValue == other.fValue; + } + bool operator!=(const SkSVGLength& other) const { return !(*this == other); } + const SkScalar& value() const { return fValue; } const Unit& unit() const { return fUnit; } @@ -65,4 +77,82 @@ private: Unit fUnit; }; +class SkSVGPaint { +public: + enum class Type { + kNone, + kCurrentColor, + kColor, + kInherit, + }; + + constexpr SkSVGPaint() : fType(Type::kInherit), fColor(SK_ColorBLACK) {} + explicit constexpr SkSVGPaint(Type t) : fType(t), fColor(SK_ColorBLACK) {} + explicit constexpr SkSVGPaint(const SkSVGColorType& c) : fType(Type::kColor), fColor(c) {} + + SkSVGPaint(const SkSVGPaint&) = default; + SkSVGPaint& operator=(const SkSVGPaint&) = default; + + bool operator==(const SkSVGPaint& other) const { + return fType == other.fType && fColor == other.fColor; + } + bool operator!=(const SkSVGPaint& other) const { return !(*this == other); } + + Type type() const { return fType; } + const SkSVGColorType& color() const { SkASSERT(fType == Type::kColor); return fColor; } + +private: + Type fType; + + SkSVGColorType fColor; +}; + +class SkSVGLineCap { +public: + enum class Type { + kButt, + kRound, + kSquare, + kInherit, + }; + + constexpr SkSVGLineCap() : fType(Type::kInherit) {} + constexpr explicit SkSVGLineCap(Type t) : fType(t) {} + + SkSVGLineCap(const SkSVGLineCap&) = default; + SkSVGLineCap& operator=(const SkSVGLineCap&) = default; + + bool operator==(const SkSVGLineCap& other) const { return fType == other.fType; } + bool operator!=(const SkSVGLineCap& other) const { return !(*this == other); } + + Type type() const { return fType; } + +private: + Type fType; +}; + +class SkSVGLineJoin { +public: + enum class Type { + kMiter, + kRound, + kBevel, + kInherit, + }; + + constexpr SkSVGLineJoin() : fType(Type::kInherit) {} + constexpr explicit SkSVGLineJoin(Type t) : fType(t) {} + + SkSVGLineJoin(const SkSVGLineJoin&) = default; + SkSVGLineJoin& operator=(const SkSVGLineJoin&) = default; + + bool operator==(const SkSVGLineJoin& other) const { return fType == other.fType; } + bool operator!=(const SkSVGLineJoin& other) const { return !(*this == other); } + + Type type() const { return fType; } + +private: + Type fType; +}; + #endif // SkSVGTypes_DEFINED diff --git a/experimental/svg/model/SkSVGValue.h b/experimental/svg/model/SkSVGValue.h index 00715e2a5b..583e60291d 100644 --- a/experimental/svg/model/SkSVGValue.h +++ b/experimental/svg/model/SkSVGValue.h @@ -19,6 +19,10 @@ public: enum class Type { kColor, kLength, + kLineCap, + kLineJoin, + kNumber, + kPaint, kPath, kTransform, kViewBox, @@ -50,6 +54,7 @@ public: , fWrappedValue(v) { } operator const T&() const { return fWrappedValue; } + const T* operator->() const { return &fWrappedValue; } private: // Stack-only @@ -66,5 +71,9 @@ using SkSVGLengthValue = SkSVGWrapperValue; using SkSVGTransformValue = SkSVGWrapperValue; using SkSVGViewBoxValue = SkSVGWrapperValue; +using SkSVGPaintValue = SkSVGWrapperValue; +using SkSVGLineCapValue = SkSVGWrapperValue; +using SkSVGLineJoinValue = SkSVGWrapperValue; +using SkSVGNumberValue = SkSVGWrapperValue; #endif // SkSVGValue_DEFINED -- cgit v1.2.3