From 28d5b72d86fdfae20dc47ba548748f119c7273e3 Mon Sep 17 00:00:00 2001 From: fmalita Date: Mon, 12 Sep 2016 17:06:47 -0700 Subject: [SVGDom] Initial linear gradient support Kind of a big change, to connect several new bits into something useful: * ID tracking & lookup * new asPaint() node virtual to support shader (and in the future filter) based paint servers * , and element support * 'href', 'offset', 'stop-color', 'stop-opacity' attribute support * IRI/FuncIRI and rgb(...) parsing BUG=skia: GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2327233003 Review-Url: https://codereview.chromium.org/2327233003 --- experimental/svg/model/SkSVGAttribute.h | 4 + experimental/svg/model/SkSVGAttributeParser.cpp | 67 ++++++++++++- experimental/svg/model/SkSVGAttributeParser.h | 4 + experimental/svg/model/SkSVGContainer.h | 5 +- experimental/svg/model/SkSVGDOM.cpp | 75 +++++++++++---- experimental/svg/model/SkSVGDOM.h | 2 + experimental/svg/model/SkSVGDefs.h | 24 +++++ experimental/svg/model/SkSVGHiddenContainer.h | 26 ++++++ experimental/svg/model/SkSVGIDMapper.h | 19 ++++ experimental/svg/model/SkSVGLinearGradient.cpp | 119 ++++++++++++++++++++++++ experimental/svg/model/SkSVGLinearGradient.h | 49 ++++++++++ experimental/svg/model/SkSVGNode.cpp | 6 ++ experimental/svg/model/SkSVGNode.h | 7 ++ experimental/svg/model/SkSVGRenderContext.cpp | 51 ++++++---- experimental/svg/model/SkSVGRenderContext.h | 7 +- experimental/svg/model/SkSVGStop.cpp | 46 +++++++++ experimental/svg/model/SkSVGStop.h | 44 +++++++++ experimental/svg/model/SkSVGTypes.h | 16 +++- experimental/svg/model/SkSVGValue.h | 2 + 19 files changed, 529 insertions(+), 44 deletions(-) create mode 100644 experimental/svg/model/SkSVGDefs.h create mode 100644 experimental/svg/model/SkSVGHiddenContainer.h create mode 100644 experimental/svg/model/SkSVGIDMapper.h create mode 100644 experimental/svg/model/SkSVGLinearGradient.cpp create mode 100644 experimental/svg/model/SkSVGLinearGradient.h create mode 100644 experimental/svg/model/SkSVGStop.cpp create mode 100644 experimental/svg/model/SkSVGStop.h (limited to 'experimental/svg') diff --git a/experimental/svg/model/SkSVGAttribute.h b/experimental/svg/model/SkSVGAttribute.h index 196575de0b..ce06b1e8a6 100644 --- a/experimental/svg/model/SkSVGAttribute.h +++ b/experimental/svg/model/SkSVGAttribute.h @@ -20,11 +20,15 @@ enum class SkSVGAttribute { kFill, kFillOpacity, kHeight, + kHref, + kOffset, kOpacity, kPoints, kR, // : radius kRx, // ,: horizontal (corner) radius kRy, // ,: vertical (corner) radius + kStopColor, + kStopOpacity, kStroke, kStrokeOpacity, kStrokeLineCap, diff --git a/experimental/svg/model/SkSVGAttributeParser.cpp b/experimental/svg/model/SkSVGAttributeParser.cpp index f8bfa2033f..9d2d6b8136 100644 --- a/experimental/svg/model/SkSVGAttributeParser.cpp +++ b/experimental/svg/model/SkSVGAttributeParser.cpp @@ -113,6 +113,7 @@ bool SkSVGAttributeParser::parseLengthUnitToken(SkSVGLength::Unit* unit) { return false; } +// https://www.w3.org/TR/SVG/types.html#DataTypeColor bool SkSVGAttributeParser::parseNamedColorToken(SkColor* c) { if (const char* next = SkParse::FindNamedColor(fCurPos, strlen(fCurPos), c)) { fCurPos = next; @@ -148,7 +149,38 @@ bool SkSVGAttributeParser::parseHexColorToken(SkColor* c) { return true; } -// https://www.w3.org/TR/SVG/types.html#DataTypeColor +bool SkSVGAttributeParser::parseColorComponentToken(int32_t* c) { + fCurPos = SkParse::FindS32(fCurPos, c); + if (!fCurPos) { + return false; + } + + if (*fCurPos == '%') { + *c = SkScalarRoundToInt(*c * 255.0f / 100); + fCurPos++; + } + + return true; +} + +bool SkSVGAttributeParser::parseRGBColorToken(SkColor* c) { + return this->parseParenthesized("rgb", [this](SkColor* c) -> bool { + int32_t r, g, b; + if (this->parseColorComponentToken(&r) && + this->parseSepToken() && + this->parseColorComponentToken(&g) && + this->parseSepToken() && + this->parseColorComponentToken(&b)) { + + *c = SkColorSetRGB(static_cast(r), + static_cast(g), + static_cast(b)); + return true; + } + return false; + }, c); +} + bool SkSVGAttributeParser::parseColor(SkSVGColorType* color) { SkColor c; @@ -157,7 +189,9 @@ bool SkSVGAttributeParser::parseColor(SkSVGColorType* color) { // TODO: rgb(...) bool parsedValue = false; - if (this->parseHexColorToken(&c) || this->parseNamedColorToken(&c)) { + if (this->parseHexColorToken(&c) + || this->parseNamedColorToken(&c) + || this->parseRGBColorToken(&c)) { *color = SkSVGColorType(c); parsedValue = true; @@ -168,6 +202,31 @@ bool SkSVGAttributeParser::parseColor(SkSVGColorType* color) { return parsedValue && this->parseEOSToken(); } +// https://www.w3.org/TR/SVG/linking.html#IRIReference +bool SkSVGAttributeParser::parseIRI(SkSVGStringType* iri) { + // consume preceding whitespace + this->parseWSToken(); + + // we only support local fragments + if (!this->parseExpectedStringToken("#")) { + return false; + } + const auto* start = fCurPos; + this->advanceWhile([](char c) -> bool { return !is_eos(c) && c != ')'; }); + if (start == fCurPos) { + return false; + } + *iri = SkString(start, fCurPos - start); + return true; +} + +// https://www.w3.org/TR/SVG/types.html#DataTypeFuncIRI +bool SkSVGAttributeParser::parseFuncIRI(SkSVGStringType* iri) { + return this->parseParenthesized("url", [this](SkSVGStringType* iri) -> bool { + return this->parseIRI(iri); + }, iri); +} + // https://www.w3.org/TR/SVG/types.html#DataTypeNumber bool SkSVGAttributeParser::parseNumber(SkSVGNumberType* number) { // consume WS @@ -363,6 +422,7 @@ bool SkSVGAttributeParser::parseTransform(SkSVGTransformType* t) { // https://www.w3.org/TR/SVG/painting.html#SpecifyingPaint bool SkSVGAttributeParser::parsePaint(SkSVGPaint* paint) { SkSVGColorType c; + SkSVGStringType iri; bool parsedValue = false; if (this->parseColor(&c)) { *paint = SkSVGPaint(c); @@ -376,6 +436,9 @@ bool SkSVGAttributeParser::parsePaint(SkSVGPaint* paint) { } else if (this->parseExpectedStringToken("inherit")) { *paint = SkSVGPaint(SkSVGPaint::Type::kInherit); parsedValue = true; + } else if (this->parseFuncIRI(&iri)) { + *paint = SkSVGPaint(iri.value()); + parsedValue = true; } return parsedValue && this->parseEOSToken(); } diff --git a/experimental/svg/model/SkSVGAttributeParser.h b/experimental/svg/model/SkSVGAttributeParser.h index d1ead39223..c1700a8e77 100644 --- a/experimental/svg/model/SkSVGAttributeParser.h +++ b/experimental/svg/model/SkSVGAttributeParser.h @@ -23,6 +23,7 @@ public: bool parseLineCap(SkSVGLineCap*); bool parseLineJoin(SkSVGLineJoin*); bool parsePoints(SkSVGPointsType*); + bool parseIRI(SkSVGStringType*); private: // Stack-only @@ -41,6 +42,9 @@ private: bool parseLengthUnitToken(SkSVGLength::Unit*); bool parseNamedColorToken(SkColor*); bool parseHexColorToken(SkColor*); + bool parseColorComponentToken(int32_t*); + bool parseRGBColorToken(SkColor*); + bool parseFuncIRI(SkSVGStringType*); // Transform helpers bool parseMatrixToken(SkMatrix*); diff --git a/experimental/svg/model/SkSVGContainer.h b/experimental/svg/model/SkSVGContainer.h index 9a3fd621ab..73c7c1803b 100644 --- a/experimental/svg/model/SkSVGContainer.h +++ b/experimental/svg/model/SkSVGContainer.h @@ -18,13 +18,14 @@ public: void appendChild(sk_sp) override; protected: - SkSVGContainer(SkSVGTag); + explicit SkSVGContainer(SkSVGTag); void onRender(const SkSVGRenderContext&) const override; -private: + // TODO: add some sort of child iterator, and hide the container. SkSTArray<1, sk_sp, true> fChildren; +private: typedef SkSVGTransformableNode INHERITED; }; diff --git a/experimental/svg/model/SkSVGDOM.cpp b/experimental/svg/model/SkSVGDOM.cpp index c123bc6bfb..d6e4f4c9bd 100644 --- a/experimental/svg/model/SkSVGDOM.cpp +++ b/experimental/svg/model/SkSVGDOM.cpp @@ -11,15 +11,18 @@ #include "SkString.h" #include "SkSVGAttributeParser.h" #include "SkSVGCircle.h" +#include "SkSVGDefs.h" #include "SkSVGDOM.h" #include "SkSVGEllipse.h" #include "SkSVGG.h" #include "SkSVGLine.h" +#include "SkSVGLinearGradient.h" #include "SkSVGNode.h" #include "SkSVGPath.h" #include "SkSVGPoly.h" #include "SkSVGRect.h" #include "SkSVGRenderContext.h" +#include "SkSVGStop.h" #include "SkSVGSVG.h" #include "SkSVGTypes.h" #include "SkSVGValue.h" @@ -32,17 +35,37 @@ bool SetPaintAttribute(const sk_sp& node, SkSVGAttribute attr, SkSVGPaint paint; SkSVGAttributeParser parser(stringValue); 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; + return false; } node->setAttribute(attr, SkSVGPaintValue(paint)); return true; } +bool SetColorAttribute(const sk_sp& node, SkSVGAttribute attr, + const char* stringValue) { + SkSVGColorType color; + SkSVGAttributeParser parser(stringValue); + if (!parser.parseColor(&color)) { + return false; + } + + node->setAttribute(attr, SkSVGColorValue(color)); + return true; +} + +bool SetIRIAttribute(const sk_sp& node, SkSVGAttribute attr, + const char* stringValue) { + SkSVGStringType iri; + SkSVGAttributeParser parser(stringValue); + if (!parser.parseIRI(&iri)) { + return false; + } + + node->setAttribute(attr, SkSVGStringValue(iri)); + return true; +} + bool SetPathDataAttribute(const sk_sp& node, SkSVGAttribute attr, const char* stringValue) { SkPath path; @@ -222,11 +245,14 @@ SortedDictionaryEntry gAttributeParseInfo[] = { { "fill" , { SkSVGAttribute::kFill , SetPaintAttribute }}, { "fill-opacity" , { SkSVGAttribute::kFillOpacity , SetNumberAttribute }}, { "height" , { SkSVGAttribute::kHeight , SetLengthAttribute }}, + { "offset" , { SkSVGAttribute::kOffset , SetLengthAttribute }}, { "opacity" , { SkSVGAttribute::kOpacity , SetNumberAttribute }}, { "points" , { SkSVGAttribute::kPoints , SetPointsAttribute }}, { "r" , { SkSVGAttribute::kR , SetLengthAttribute }}, { "rx" , { SkSVGAttribute::kRx , SetLengthAttribute }}, { "ry" , { SkSVGAttribute::kRy , SetLengthAttribute }}, + { "stop-color" , { SkSVGAttribute::kStopColor , SetColorAttribute }}, + { "stop-opacity" , { SkSVGAttribute::kStopOpacity , SetNumberAttribute }}, { "stroke" , { SkSVGAttribute::kStroke , SetPaintAttribute }}, { "stroke-linecap" , { SkSVGAttribute::kStrokeLineCap , SetLineCapAttribute }}, { "stroke-linejoin", { SkSVGAttribute::kStrokeLineJoin, SetLineJoinAttribute }}, @@ -239,29 +265,34 @@ SortedDictionaryEntry gAttributeParseInfo[] = { { "x" , { SkSVGAttribute::kX , SetLengthAttribute }}, { "x1" , { SkSVGAttribute::kX1 , SetLengthAttribute }}, { "x2" , { SkSVGAttribute::kX2 , SetLengthAttribute }}, + { "xlink:href" , { SkSVGAttribute::kHref , SetIRIAttribute }}, { "y" , { SkSVGAttribute::kY , SetLengthAttribute }}, { "y1" , { SkSVGAttribute::kY1 , SetLengthAttribute }}, { "y2" , { SkSVGAttribute::kY2 , SetLengthAttribute }}, }; SortedDictionaryEntry(*)()> gTagFactories[] = { - { "circle" , []() -> sk_sp { return SkSVGCircle::Make(); }}, - { "ellipse" , []() -> sk_sp { return SkSVGEllipse::Make(); }}, - { "g" , []() -> sk_sp { return SkSVGG::Make(); }}, - { "line" , []() -> sk_sp { return SkSVGLine::Make(); }}, - { "path" , []() -> sk_sp { return SkSVGPath::Make(); }}, - { "polygon" , []() -> sk_sp { return SkSVGPoly::MakePolygon(); }}, - { "polyline", []() -> sk_sp { return SkSVGPoly::MakePolyline(); }}, - { "rect" , []() -> sk_sp { return SkSVGRect::Make(); }}, - { "svg" , []() -> sk_sp { return SkSVGSVG::Make(); }}, + { "circle" , []() -> sk_sp { return SkSVGCircle::Make(); }}, + { "defs" , []() -> sk_sp { return SkSVGDefs::Make(); }}, + { "ellipse" , []() -> sk_sp { return SkSVGEllipse::Make(); }}, + { "g" , []() -> sk_sp { return SkSVGG::Make(); }}, + { "line" , []() -> sk_sp { return SkSVGLine::Make(); }}, + { "linearGradient", []() -> sk_sp { return SkSVGLinearGradient::Make(); }}, + { "path" , []() -> sk_sp { return SkSVGPath::Make(); }}, + { "polygon" , []() -> sk_sp { return SkSVGPoly::MakePolygon(); }}, + { "polyline" , []() -> sk_sp { return SkSVGPoly::MakePolyline(); }}, + { "rect" , []() -> sk_sp { return SkSVGRect::Make(); }}, + { "stop" , []() -> sk_sp { return SkSVGStop::Make(); }}, + { "svg" , []() -> sk_sp { return SkSVGSVG::Make(); }}, }; struct ConstructionContext { - ConstructionContext() : fParent(nullptr) { } + ConstructionContext(SkSVGIDMapper* mapper) : fParent(nullptr), fIDMapper(mapper) {} ConstructionContext(const ConstructionContext& other, const sk_sp& newParent) - : fParent(newParent.get()) { } + : fParent(newParent.get()), fIDMapper(other.fIDMapper) {} const SkSVGNode* fParent; + SkSVGIDMapper* fIDMapper; }; void set_string_attribute(const sk_sp& node, const char* name, const char* value) { @@ -285,10 +316,15 @@ void set_string_attribute(const sk_sp& node, const char* name, const } void parse_node_attributes(const SkDOM& xmlDom, const SkDOM::Node* xmlNode, - const sk_sp& svgNode) { + const sk_sp& svgNode, SkSVGIDMapper* mapper) { const char* name, *value; SkDOM::AttrIter attrIter(xmlDom, xmlNode); while ((name = attrIter.next(&value))) { + // We're handling id attributes out of band for now. + if (!strcmp(name, "id")) { + mapper->set(SkString(value), svgNode); + continue; + } set_string_attribute(svgNode, name, value); } } @@ -318,7 +354,7 @@ sk_sp construct_svg_node(const SkDOM& dom, const ConstructionContext& SkASSERT(SkTo(tagIndex) < SK_ARRAY_COUNT(gTagFactories)); sk_sp node = gTagFactories[tagIndex].fValue(); - parse_node_attributes(dom, xmlNode, node); + parse_node_attributes(dom, xmlNode, node, ctx.fIDMapper); ConstructionContext localCtx(ctx, node); for (auto* child = dom.getFirstChild(xmlNode, nullptr); child; @@ -341,7 +377,7 @@ SkSVGDOM::SkSVGDOM(const SkSize& containerSize) sk_sp SkSVGDOM::MakeFromDOM(const SkDOM& xmlDom, const SkSize& containerSize) { sk_sp dom = sk_make_sp(containerSize); - ConstructionContext ctx; + ConstructionContext ctx(&dom->fIDMapper); dom->fRoot = construct_svg_node(xmlDom, ctx, xmlDom.getRootNode()); return dom; @@ -359,6 +395,7 @@ sk_sp SkSVGDOM::MakeFromStream(SkStream& svgStream, const SkSize& cont void SkSVGDOM::render(SkCanvas* canvas) const { if (fRoot) { SkSVGRenderContext ctx(canvas, + fIDMapper, SkSVGLengthContext(fContainerSize), SkSVGPresentationContext()); fRoot->render(ctx); diff --git a/experimental/svg/model/SkSVGDOM.h b/experimental/svg/model/SkSVGDOM.h index 98f846246c..9c59b7c561 100644 --- a/experimental/svg/model/SkSVGDOM.h +++ b/experimental/svg/model/SkSVGDOM.h @@ -10,6 +10,7 @@ #include "SkRefCnt.h" #include "SkSize.h" +#include "SkSVGIDMapper.h" #include "SkTemplates.h" class SkCanvas; @@ -33,6 +34,7 @@ public: private: SkSize fContainerSize; sk_sp fRoot; + SkSVGIDMapper fIDMapper; typedef SkRefCnt INHERITED; }; diff --git a/experimental/svg/model/SkSVGDefs.h b/experimental/svg/model/SkSVGDefs.h new file mode 100644 index 0000000000..3064fe2197 --- /dev/null +++ b/experimental/svg/model/SkSVGDefs.h @@ -0,0 +1,24 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkSVGDefs_DEFINED +#define SkSVGDefs_DEFINED + +#include "SkSVGHiddenContainer.h" + +class SkSVGDefs : public SkSVGHiddenContainer { +public: + virtual ~SkSVGDefs() = default; + static sk_sp Make() { return sk_sp(new SkSVGDefs()); } + +private: + SkSVGDefs() : INHERITED(SkSVGTag::kDefs) {} + + typedef SkSVGHiddenContainer INHERITED; +}; + +#endif // SkSVGDefs_DEFINED diff --git a/experimental/svg/model/SkSVGHiddenContainer.h b/experimental/svg/model/SkSVGHiddenContainer.h new file mode 100644 index 0000000000..e224e55038 --- /dev/null +++ b/experimental/svg/model/SkSVGHiddenContainer.h @@ -0,0 +1,26 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkSVGHiddenContainer_DEFINED +#define SkSVGHiddenContainer_DEFINED + +#include "SkSVGContainer.h" + +class SkSVGHiddenContainer : public SkSVGContainer { +public: + virtual ~SkSVGHiddenContainer() = default; + +protected: + explicit SkSVGHiddenContainer(SkSVGTag t) : INHERITED(t) {} + + void onRender(const SkSVGRenderContext&) const final {} + +private: + typedef SkSVGContainer INHERITED; +}; + +#endif // SkSVGHiddenContainer_DEFINED diff --git a/experimental/svg/model/SkSVGIDMapper.h b/experimental/svg/model/SkSVGIDMapper.h new file mode 100644 index 0000000000..d88c1f12b1 --- /dev/null +++ b/experimental/svg/model/SkSVGIDMapper.h @@ -0,0 +1,19 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkSVGIDMapper_DEFINED +#define SkSVGIDMapper_DEFINED + +#include "SkRefCnt.h" +#include "SkTHash.h" + +class SkString; +class SkSVGNode; + +using SkSVGIDMapper = SkTHashMap>; + +#endif // SkSVGIDMapper_DEFINED diff --git a/experimental/svg/model/SkSVGLinearGradient.cpp b/experimental/svg/model/SkSVGLinearGradient.cpp new file mode 100644 index 0000000000..20c27f540c --- /dev/null +++ b/experimental/svg/model/SkSVGLinearGradient.cpp @@ -0,0 +1,119 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkGradientShader.h" +#include "SkSVGLinearGradient.h" +#include "SkSVGRenderContext.h" +#include "SkSVGStop.h" +#include "SkSVGValue.h" + +SkSVGLinearGradient::SkSVGLinearGradient() : INHERITED(SkSVGTag::kLinearGradient) {} + +void SkSVGLinearGradient::setHref(const SkSVGStringType& href) { + fHref = std::move(href); +} + +void SkSVGLinearGradient::setX1(const SkSVGLength& x1) { + fX1 = x1; +} + +void SkSVGLinearGradient::setY1(const SkSVGLength& y1) { + fY1 = y1; +} + +void SkSVGLinearGradient::setX2(const SkSVGLength& x2) { + fX2 = x2; +} + +void SkSVGLinearGradient::setY2(const SkSVGLength& y2) { + fY2 = y2; +} + +void SkSVGLinearGradient::onSetAttribute(SkSVGAttribute attr, const SkSVGValue& v) { + switch (attr) { + case SkSVGAttribute::kHref: + if (const auto* href = v.as()) { + this->setHref(*href); + } + break; + case SkSVGAttribute::kX1: + if (const auto* x1 = v.as()) { + this->setX1(*x1); + } + break; + case SkSVGAttribute::kY1: + if (const auto* y1 = v.as()) { + this->setY1(*y1); + } + break; + case SkSVGAttribute::kX2: + if (const auto* x2 = v.as()) { + this->setX2(*x2); + } + break; + case SkSVGAttribute::kY2: + if (const auto* y2 = v.as()) { + this->setY2(*y2); + } + break; + default: + this->INHERITED::onSetAttribute(attr, v); + } +} + +// https://www.w3.org/TR/SVG/pservers.html#LinearGradientElementHrefAttribute +void SkSVGLinearGradient::collectColorStops(const SkSVGRenderContext& ctx, + SkSTArray<2, SkScalar, true>* pos, + SkSTArray<2, SkColor, true>* colors) const { + // Used to resolve percentage offsets. + const SkSVGLengthContext ltx(SkSize::Make(1, 1)); + + for (const auto& child : fChildren) { + if (child->tag() != SkSVGTag::kStop) { + continue; + } + + const auto& stop = static_cast(*child); + colors->push_back(SkColorSetA(stop.stopColor(), + SkScalarRoundToInt(stop.stopOpacity() * 255))); + pos->push_back(SkTPin(ltx.resolve(stop.offset(), SkSVGLengthContext::LengthType::kOther), + 0.f, 1.f)); + } + + SkASSERT(colors->count() == pos->count()); + + if (pos->empty() && !fHref.value().isEmpty()) { + const auto* ref = ctx.findNodeById(fHref); + if (ref && ref->tag() == SkSVGTag::kLinearGradient) { + static_cast(ref)->collectColorStops(ctx, pos, colors); + } + } +} + +bool SkSVGLinearGradient::onAsPaint(const SkSVGRenderContext& ctx, SkPaint* paint) const { + const auto& lctx = ctx.lengthContext(); + const auto x1 = lctx.resolve(fX1, SkSVGLengthContext::LengthType::kHorizontal); + const auto y1 = lctx.resolve(fY1, SkSVGLengthContext::LengthType::kVertical); + const auto x2 = lctx.resolve(fX2, SkSVGLengthContext::LengthType::kHorizontal); + const auto y2 = lctx.resolve(fY2, SkSVGLengthContext::LengthType::kVertical); + + const SkPoint pts[2] = { {x1, y1}, {x2, y2}}; + SkSTArray<2, SkColor , true> colors; + SkSTArray<2, SkScalar, true> pos; + + this->collectColorStops(ctx, &pos, &colors); + // TODO: + // * stop (lazy?) sorting + // * href loop detection + // * href attribute inheritance (not just color stops) + // * spreadMethods support + // * objectBoundingBox units support + + paint->setShader(SkGradientShader::MakeLinear(pts, colors.begin(), pos.begin(), colors.count(), + SkShader::kClamp_TileMode)); + return true; +} diff --git a/experimental/svg/model/SkSVGLinearGradient.h b/experimental/svg/model/SkSVGLinearGradient.h new file mode 100644 index 0000000000..1a2e332baf --- /dev/null +++ b/experimental/svg/model/SkSVGLinearGradient.h @@ -0,0 +1,49 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkSVGLinearGradient_DEFINED +#define SkSVGLinearGradient_DEFINED + +#include "SkSVGHiddenContainer.h" +#include "SkSVGTypes.h" + +class SkSVGLinearGradient : public SkSVGHiddenContainer { +public: + virtual ~SkSVGLinearGradient() = default; + static sk_sp Make() { + return sk_sp(new SkSVGLinearGradient()); + } + + void setHref(const SkSVGStringType&); + void setX1(const SkSVGLength&); + void setY1(const SkSVGLength&); + void setX2(const SkSVGLength&); + void setY2(const SkSVGLength&); + +protected: + bool onAsPaint(const SkSVGRenderContext&, SkPaint*) const override; + + void onSetAttribute(SkSVGAttribute, const SkSVGValue&) override; + +private: + SkSVGLinearGradient(); + + void collectColorStops(const SkSVGRenderContext&, + SkSTArray<2, SkScalar, true>*, + SkSTArray<2, SkColor, true>*) const; + + SkSVGLength fX1 = SkSVGLength(0 , SkSVGLength::Unit::kPercentage); + SkSVGLength fY1 = SkSVGLength(0 , SkSVGLength::Unit::kPercentage); + SkSVGLength fX2 = SkSVGLength(100, SkSVGLength::Unit::kPercentage); + SkSVGLength fY2 = SkSVGLength(0 , SkSVGLength::Unit::kPercentage); + + SkSVGStringType fHref; + + typedef SkSVGHiddenContainer INHERITED; +}; + +#endif // SkSVGLinearGradient_DEFINED diff --git a/experimental/svg/model/SkSVGNode.cpp b/experimental/svg/model/SkSVGNode.cpp index 5a73ace548..012c7d2277 100644 --- a/experimental/svg/model/SkSVGNode.cpp +++ b/experimental/svg/model/SkSVGNode.cpp @@ -24,6 +24,12 @@ void SkSVGNode::render(const SkSVGRenderContext& ctx) const { } } +bool SkSVGNode::asPaint(const SkSVGRenderContext& ctx, SkPaint* paint) const { + SkSVGRenderContext localContext(ctx); + + return this->onPrepareToRender(&localContext) && this->onAsPaint(localContext, paint); +} + bool SkSVGNode::onPrepareToRender(SkSVGRenderContext* ctx) const { ctx->applyPresentationAttributes(fPresentationAttributes); return true; diff --git a/experimental/svg/model/SkSVGNode.h b/experimental/svg/model/SkSVGNode.h index c02fd3a214..dcdb58958a 100644 --- a/experimental/svg/model/SkSVGNode.h +++ b/experimental/svg/model/SkSVGNode.h @@ -13,18 +13,22 @@ class SkCanvas; class SkMatrix; +class SkPaint; class SkSVGRenderContext; class SkSVGValue; enum class SkSVGTag { kCircle, + kDefs, kEllipse, kG, kLine, + kLinearGradient, kPath, kPolygon, kPolyline, kRect, + kStop, kSvg }; @@ -37,6 +41,7 @@ public: virtual void appendChild(sk_sp) = 0; void render(const SkSVGRenderContext&) const; + bool asPaint(const SkSVGRenderContext&, SkPaint*) const; void setAttribute(SkSVGAttribute, const SkSVGValue&); @@ -60,6 +65,8 @@ protected: virtual void onRender(const SkSVGRenderContext&) const = 0; + virtual bool onAsPaint(const SkSVGRenderContext&, SkPaint*) const { return false; } + virtual void onSetAttribute(SkSVGAttribute, const SkSVGValue&); private: diff --git a/experimental/svg/model/SkSVGRenderContext.cpp b/experimental/svg/model/SkSVGRenderContext.cpp index d8f762142f..46ed5a28c4 100644 --- a/experimental/svg/model/SkSVGRenderContext.cpp +++ b/experimental/svg/model/SkSVGRenderContext.cpp @@ -7,6 +7,7 @@ #include "SkCanvas.h" #include "SkSVGAttribute.h" +#include "SkSVGNode.h" #include "SkSVGRenderContext.h" #include "SkSVGTypes.h" @@ -98,11 +99,18 @@ SkPaint::Join toSkJoin(const SkSVGLineJoin& join) { } } -void applySvgPaint(const SkSVGPaint& svgPaint, SkPaint* p) { +void applySvgPaint(const SkSVGRenderContext& ctx, const SkSVGPaint& svgPaint, SkPaint* p) { switch (svgPaint.type()) { case SkSVGPaint::Type::kColor: p->setColor(SkColorSetA(svgPaint.color(), p->getAlpha())); break; + case SkSVGPaint::Type::kIRI: { + const auto* node = ctx.findNodeById(svgPaint.iri()); + if (!node || !node->asPaint(ctx, p)) { + p->setColor(SK_ColorTRANSPARENT); + } + break; + } case SkSVGPaint::Type::kCurrentColor: SkDebugf("unimplemented 'currentColor' paint type"); // Fall through. @@ -120,33 +128,33 @@ inline uint8_t opacity_to_alpha(SkScalar o) { // Commit the selected attribute to the paint cache. template void commitToPaint(const SkSVGPresentationAttributes&, - const SkSVGLengthContext&, + const SkSVGRenderContext&, SkSVGPresentationContext*); template <> void commitToPaint(const SkSVGPresentationAttributes& attrs, - const SkSVGLengthContext&, + const SkSVGRenderContext& ctx, SkSVGPresentationContext* pctx) { - applySvgPaint(*attrs.fFill.get(), &pctx->fFillPaint); + applySvgPaint(ctx, *attrs.fFill.get(), &pctx->fFillPaint); } template <> void commitToPaint(const SkSVGPresentationAttributes& attrs, - const SkSVGLengthContext&, + const SkSVGRenderContext& ctx, SkSVGPresentationContext* pctx) { - applySvgPaint(*attrs.fStroke.get(), &pctx->fStrokePaint); + applySvgPaint(ctx, *attrs.fStroke.get(), &pctx->fStrokePaint); } template <> void commitToPaint(const SkSVGPresentationAttributes& attrs, - const SkSVGLengthContext&, + const SkSVGRenderContext&, SkSVGPresentationContext* pctx) { pctx->fFillPaint.setAlpha(opacity_to_alpha(*attrs.fFillOpacity.get())); } template <> void commitToPaint(const SkSVGPresentationAttributes& attrs, - const SkSVGLengthContext&, + const SkSVGRenderContext&, SkSVGPresentationContext* pctx) { const auto& cap = *attrs.fStrokeLineCap.get(); if (cap.type() != SkSVGLineCap::Type::kInherit) { @@ -156,7 +164,7 @@ void commitToPaint(const SkSVGPresentationAttrib template <> void commitToPaint(const SkSVGPresentationAttributes& attrs, - const SkSVGLengthContext&, + const SkSVGRenderContext&, SkSVGPresentationContext* pctx) { const auto& join = *attrs.fStrokeLineJoin.get(); if (join.type() != SkSVGLineJoin::Type::kInherit) { @@ -166,17 +174,17 @@ void commitToPaint(const SkSVGPresentationAttri template <> void commitToPaint(const SkSVGPresentationAttributes& attrs, - const SkSVGLengthContext&, + const SkSVGRenderContext&, SkSVGPresentationContext* pctx) { pctx->fStrokePaint.setAlpha(opacity_to_alpha(*attrs.fStrokeOpacity.get())); } template <> void commitToPaint(const SkSVGPresentationAttributes& attrs, - const SkSVGLengthContext& lctx, + const SkSVGRenderContext& ctx, SkSVGPresentationContext* pctx) { - auto strokeWidth = lctx.resolve(*attrs.fStrokeWidth.get(), - SkSVGLengthContext::LengthType::kOther); + auto strokeWidth = ctx.lengthContext().resolve(*attrs.fStrokeWidth.get(), + SkSVGLengthContext::LengthType::kOther); pctx->fStrokePaint.setStrokeWidth(strokeWidth); } @@ -193,7 +201,10 @@ SkSVGPresentationContext::SkSVGPresentationContext() fStrokePaint.setAntiAlias(true); // Commit initial values to the paint cache. - SkSVGLengthContext dummy(SkSize::Make(0, 0)); + SkCanvas dummyCanvas(0, 0); + SkSVGRenderContext dummy(&dummyCanvas, SkSVGIDMapper(), SkSVGLengthContext(SkSize::Make(0, 0)), + *this); + commitToPaint(fInherited, dummy, this); commitToPaint(fInherited, dummy, this); commitToPaint(fInherited, dummy, this); @@ -204,15 +215,18 @@ SkSVGPresentationContext::SkSVGPresentationContext() } SkSVGRenderContext::SkSVGRenderContext(SkCanvas* canvas, + const SkSVGIDMapper& mapper, const SkSVGLengthContext& lctx, const SkSVGPresentationContext& pctx) - : fLengthContext(lctx) + : fIDMapper(mapper) + , fLengthContext(lctx) , fPresentationContext(pctx) , fCanvas(canvas) , fCanvasSaveCount(canvas->getSaveCount()) {} SkSVGRenderContext::SkSVGRenderContext(const SkSVGRenderContext& other) : SkSVGRenderContext(other.fCanvas, + other.fIDMapper, *other.fLengthContext, *other.fPresentationContext) {} @@ -220,6 +234,11 @@ SkSVGRenderContext::~SkSVGRenderContext() { fCanvas->restoreToCount(fCanvasSaveCount); } +const SkSVGNode* SkSVGRenderContext::findNodeById(const SkString& id) const { + const auto* v = fIDMapper.find(id); + return v ? v->get() : nullptr; +} + void SkSVGRenderContext::applyPresentationAttributes(const SkSVGPresentationAttributes& attrs) { #define ApplyLazyInheritedAttribute(ATTR) \ @@ -231,7 +250,7 @@ void SkSVGRenderContext::applyPresentationAttributes(const SkSVGPresentationAttr /* Update the local attribute value */ \ fPresentationContext.writable()->fInherited.f ## ATTR.set(*value); \ /* Update the cached paints */ \ - commitToPaint(attrs, *fLengthContext, \ + commitToPaint(attrs, *this, \ fPresentationContext.writable()); \ } \ } while (false) diff --git a/experimental/svg/model/SkSVGRenderContext.h b/experimental/svg/model/SkSVGRenderContext.h index 61f8746376..68209a73cc 100644 --- a/experimental/svg/model/SkSVGRenderContext.h +++ b/experimental/svg/model/SkSVGRenderContext.h @@ -12,6 +12,7 @@ #include "SkRect.h" #include "SkSize.h" #include "SkSVGAttribute.h" +#include "SkSVGIDMapper.h" #include "SkTLazy.h" #include "SkTypes.h" @@ -56,7 +57,8 @@ struct SkSVGPresentationContext { class SkSVGRenderContext { public: - SkSVGRenderContext(SkCanvas*, const SkSVGLengthContext&, const SkSVGPresentationContext&); + SkSVGRenderContext(SkCanvas*, const SkSVGIDMapper&, const SkSVGLengthContext&, + const SkSVGPresentationContext&); SkSVGRenderContext(const SkSVGRenderContext&); ~SkSVGRenderContext(); @@ -67,6 +69,8 @@ public: void applyPresentationAttributes(const SkSVGPresentationAttributes&); + const SkSVGNode* findNodeById(const SkString&) const; + const SkPaint* fillPaint() const; const SkPaint* strokePaint() const; @@ -76,6 +80,7 @@ private: void* operator new(size_t, void*) = delete; SkSVGRenderContext& operator=(const SkSVGRenderContext&) = delete; + const SkSVGIDMapper& fIDMapper; SkTCopyOnFirstWrite fLengthContext; SkTCopyOnFirstWrite fPresentationContext; SkCanvas* fCanvas; diff --git a/experimental/svg/model/SkSVGStop.cpp b/experimental/svg/model/SkSVGStop.cpp new file mode 100644 index 0000000000..3abf505abb --- /dev/null +++ b/experimental/svg/model/SkSVGStop.cpp @@ -0,0 +1,46 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkSVGRenderContext.h" +#include "SkSVGStop.h" +#include "SkSVGValue.h" + +SkSVGStop::SkSVGStop() : INHERITED(SkSVGTag::kStop) {} + +void SkSVGStop::setOffset(const SkSVGLength& offset) { + fOffset = offset; +} + +void SkSVGStop::setStopColor(const SkSVGColorType& color) { + fStopColor = color; +} + +void SkSVGStop::setStopOpacity(const SkSVGNumberType& opacity) { + fStopOpacity = SkTPin(opacity.value(), 0, 1); +} + +void SkSVGStop::onSetAttribute(SkSVGAttribute attr, const SkSVGValue& v) { + switch (attr) { + case SkSVGAttribute::kOffset: + if (const auto* offset = v.as()) { + this->setOffset(*offset); + } + break; + case SkSVGAttribute::kStopColor: + if (const auto* color = v.as()) { + this->setStopColor(*color); + } + break; + case SkSVGAttribute::kStopOpacity: + if (const auto* opacity = v.as()) { + this->setStopOpacity(*opacity); + } + break; + default: + this->INHERITED::onSetAttribute(attr, v); + } +} diff --git a/experimental/svg/model/SkSVGStop.h b/experimental/svg/model/SkSVGStop.h new file mode 100644 index 0000000000..2ffbc5c852 --- /dev/null +++ b/experimental/svg/model/SkSVGStop.h @@ -0,0 +1,44 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkSVGStop_DEFINED +#define SkSVGStop_DEFINED + +#include "SkSVGHiddenContainer.h" +#include "SkSVGTypes.h" + +class SkSVGLengthContext; + +class SkSVGStop : public SkSVGHiddenContainer { +public: + virtual ~SkSVGStop() = default; + static sk_sp Make() { + return sk_sp(new SkSVGStop()); + } + + const SkSVGLength& offset() const { return fOffset; } + const SkSVGColorType& stopColor() const { return fStopColor; } + const SkSVGNumberType& stopOpacity() const { return fStopOpacity; } + + void setOffset(const SkSVGLength&); + void setStopColor(const SkSVGColorType&); + void setStopOpacity(const SkSVGNumberType&); + +protected: + void onSetAttribute(SkSVGAttribute, const SkSVGValue&) override; + +private: + SkSVGStop(); + + SkSVGLength fOffset = SkSVGLength(0 , SkSVGLength::Unit::kPercentage); + SkSVGColorType fStopColor = SkSVGColorType(SK_ColorBLACK); + SkSVGNumberType fStopOpacity = SkSVGNumberType(1); + + typedef SkSVGHiddenContainer INHERITED; +}; + +#endif // SkSVGStop_DEFINED diff --git a/experimental/svg/model/SkSVGTypes.h b/experimental/svg/model/SkSVGTypes.h index 18ec3ce3cf..b2343a15e4 100644 --- a/experimental/svg/model/SkSVGTypes.h +++ b/experimental/svg/model/SkSVGTypes.h @@ -13,6 +13,7 @@ #include "SkPoint.h" #include "SkRect.h" #include "SkScalar.h" +#include "SkString.h" #include "SkTDArray.h" #include "SkTypes.h" @@ -42,6 +43,7 @@ private: using SkSVGColorType = SkSVGPrimitiveTypeWrapper; using SkSVGNumberType = SkSVGPrimitiveTypeWrapper; +using SkSVGStringType = SkSVGPrimitiveTypeWrapper; using SkSVGViewBoxType = SkSVGPrimitiveTypeWrapper; using SkSVGTransformType = SkSVGPrimitiveTypeWrapper; using SkSVGPointsType = SkSVGPrimitiveTypeWrapper>; @@ -88,27 +90,33 @@ public: kCurrentColor, kColor, kInherit, + kIRI, }; - 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() : fType(Type::kInherit), fColor(SK_ColorBLACK) {} + explicit SkSVGPaint(Type t) : fType(t), fColor(SK_ColorBLACK) {} + explicit SkSVGPaint(const SkSVGColorType& c) : fType(Type::kColor), fColor(c) {} + explicit SkSVGPaint(const SkString& iri) + : fType(Type::kIRI), fColor(SK_ColorBLACK), fIRI(iri) {} SkSVGPaint(const SkSVGPaint&) = default; SkSVGPaint& operator=(const SkSVGPaint&) = default; bool operator==(const SkSVGPaint& other) const { - return fType == other.fType && fColor == other.fColor; + return fType == other.fType && fColor == other.fColor && fIRI == other.fIRI; } bool operator!=(const SkSVGPaint& other) const { return !(*this == other); } Type type() const { return fType; } const SkSVGColorType& color() const { SkASSERT(fType == Type::kColor); return fColor; } + const SkString& iri() const { SkASSERT(fType == Type::kIRI); return fIRI; } private: Type fType; + // Logical union. SkSVGColorType fColor; + SkString fIRI; }; class SkSVGLineCap { diff --git a/experimental/svg/model/SkSVGValue.h b/experimental/svg/model/SkSVGValue.h index 8f93bd8046..e4673e9150 100644 --- a/experimental/svg/model/SkSVGValue.h +++ b/experimental/svg/model/SkSVGValue.h @@ -25,6 +25,7 @@ public: kPaint, kPath, kPoints, + kString, kTransform, kViewBox, }; @@ -77,5 +78,6 @@ using SkSVGLineCapValue = SkSVGWrapperValue; using SkSVGNumberValue = SkSVGWrapperValue; using SkSVGPointsValue = SkSVGWrapperValue; +using SkSVGStringValue = SkSVGWrapperValue; #endif // SkSVGValue_DEFINED -- cgit v1.2.3