From bffc2566872f99d378a1113d0a49ec9ee0d60b7a Mon Sep 17 00:00:00 2001 From: fmalita Date: Wed, 3 Aug 2016 10:21:11 -0700 Subject: [PATCH] [SVGDom] Initial SVGLength support Mostly plumb new length types, but also introduce a stateful parser, rect shape and named color support. R=reed@google.com,robertphillips@google.com,stephana@google.com GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2202053002 Review-Url: https://codereview.chromium.org/2202053002 --- experimental/svg/model/SkSVGAttribute.cpp | 4 +- experimental/svg/model/SkSVGAttribute.h | 14 +- .../svg/model/SkSVGAttributeParser.cpp | 194 ++++++++++++++++++ experimental/svg/model/SkSVGAttributeParser.h | 45 ++++ experimental/svg/model/SkSVGDOM.cpp | 69 +++---- experimental/svg/model/SkSVGNode.cpp | 5 +- experimental/svg/model/SkSVGNode.h | 2 +- experimental/svg/model/SkSVGPath.cpp | 15 +- experimental/svg/model/SkSVGPath.h | 14 +- experimental/svg/model/SkSVGRect.cpp | 68 ++++++ experimental/svg/model/SkSVGRect.h | 40 ++++ experimental/svg/model/SkSVGRenderContext.cpp | 38 +++- experimental/svg/model/SkSVGRenderContext.h | 29 ++- experimental/svg/model/SkSVGSVG.cpp | 44 ++++ experimental/svg/model/SkSVGSVG.h | 14 ++ experimental/svg/model/SkSVGShape.cpp | 25 +++ experimental/svg/model/SkSVGShape.h | 33 +++ experimental/svg/model/SkSVGTypes.h | 72 +++++++ experimental/svg/model/SkSVGValue.cpp | 1 + experimental/svg/model/SkSVGValue.h | 19 +- gyp/svg.gyp | 7 + 21 files changed, 674 insertions(+), 78 deletions(-) create mode 100644 experimental/svg/model/SkSVGAttributeParser.cpp create mode 100644 experimental/svg/model/SkSVGAttributeParser.h create mode 100644 experimental/svg/model/SkSVGRect.cpp create mode 100644 experimental/svg/model/SkSVGRect.h create mode 100644 experimental/svg/model/SkSVGShape.cpp create mode 100644 experimental/svg/model/SkSVGShape.h create mode 100644 experimental/svg/model/SkSVGTypes.h diff --git a/experimental/svg/model/SkSVGAttribute.cpp b/experimental/svg/model/SkSVGAttribute.cpp index c4a374e7e1..79c355176f 100644 --- a/experimental/svg/model/SkSVGAttribute.cpp +++ b/experimental/svg/model/SkSVGAttribute.cpp @@ -12,12 +12,12 @@ SkSVGPresentationAttributes::SkSVGPresentationAttributes() : fFillIsSet(false) , fStrokeIsSet(false) { } -void SkSVGPresentationAttributes::setFill(SkColor c) { +void SkSVGPresentationAttributes::setFill(const SkSVGColor& c) { fFill = c; fFillIsSet = true; } -void SkSVGPresentationAttributes::setStroke(SkColor c) { +void SkSVGPresentationAttributes::setStroke(const SkSVGColor& c) { fStroke = c; fStrokeIsSet = true; } diff --git a/experimental/svg/model/SkSVGAttribute.h b/experimental/svg/model/SkSVGAttribute.h index 83a35527c5..64465b13c9 100644 --- a/experimental/svg/model/SkSVGAttribute.h +++ b/experimental/svg/model/SkSVGAttribute.h @@ -8,14 +8,18 @@ #ifndef SkSVGAttribute_DEFINED #define SkSVGAttribute_DEFINED -#include "SkColor.h" +#include "SkSVGTypes.h" #include "SkTLazy.h" enum class SkSVGAttribute { kD, kFill, + kHeight, kStroke, kTransform, + kWidth, + kX, + kY, kUnknown, }; @@ -26,15 +30,15 @@ class SkSVGPresentationAttributes { public: SkSVGPresentationAttributes(); - void setFill(SkColor); - void setStroke(SkColor); + void setFill(const SkSVGColor&); + void setStroke(const SkSVGColor&); void applyTo(SkTCopyOnFirstWrite&) const; private: // Color only for now. - SkColor fFill; - SkColor fStroke; + SkSVGColor fFill; + SkSVGColor fStroke; unsigned fFillIsSet : 1; unsigned fStrokeIsSet : 1; diff --git a/experimental/svg/model/SkSVGAttributeParser.cpp b/experimental/svg/model/SkSVGAttributeParser.cpp new file mode 100644 index 0000000000..75e5d12f38 --- /dev/null +++ b/experimental/svg/model/SkSVGAttributeParser.cpp @@ -0,0 +1,194 @@ +/* + * 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 "SkParse.h" +#include "SkSVGAttributeParser.h" +#include "SkSVGTypes.h" + +namespace { + +// TODO: these should be shared with SkParse.cpp + +inline bool is_between(char c, char min, char max) { + SkASSERT(min <= max); + return (unsigned)(c - min) <= (unsigned)(max - min); +} + +inline bool is_eos(char c) { + return !c; +} + +inline bool is_ws(char c) { + return is_between(c, 1, 32); +} + +inline bool is_sep(char c) { + return is_ws(c) || c == ',' || c == ';'; +} + +} // anonymous ns + +SkSVGAttributeParser::SkSVGAttributeParser(const char attributeString[]) + : fCurPos(attributeString) {} + +template +inline bool SkSVGAttributeParser::advanceWhile(F f) { + auto initial = fCurPos; + while (f(*fCurPos)) { + fCurPos++; + } + return fCurPos != initial; +} + +inline bool SkSVGAttributeParser::parseEOSToken() { + return is_eos(*fCurPos); +} + +inline bool SkSVGAttributeParser::parseSepToken() { + return this->advanceWhile(is_sep); +} + +inline bool SkSVGAttributeParser::parseWSToken() { + return this->advanceWhile(is_ws); +} + +inline bool SkSVGAttributeParser::parseExpectedStringToken(const char* expected) { + const char* c = fCurPos; + + while (*c && *expected && *c == *expected) { + c++; + expected++; + } + + if (*expected) { + return false; + } + + fCurPos = c; + return true; +} + +bool SkSVGAttributeParser::parseScalarToken(SkScalar* res) { + if (const char* next = SkParse::FindScalar(fCurPos, res)) { + fCurPos = next; + return true; + } + return false; +} + +bool SkSVGAttributeParser::parseHexToken(uint32_t* res) { + if (const char* next = SkParse::FindHex(fCurPos, res)) { + fCurPos = next; + return true; + } + return false; +} + +bool SkSVGAttributeParser::parseLengthUnitToken(SkSVGLength::Unit* unit) { + static const struct { + const char* fUnitName; + SkSVGLength::Unit fUnit; + } gUnitInfo[] = { + { "%" , SkSVGLength::Unit::kPercentage }, + { "em", SkSVGLength::Unit::kEMS }, + { "ex", SkSVGLength::Unit::kEXS }, + { "px", SkSVGLength::Unit::kPX }, + { "cm", SkSVGLength::Unit::kCM }, + { "mm", SkSVGLength::Unit::kMM }, + { "in", SkSVGLength::Unit::kIN }, + { "pt", SkSVGLength::Unit::kPT }, + { "pc", SkSVGLength::Unit::kPC }, + }; + + for (size_t i = 0; i < SK_ARRAY_COUNT(gUnitInfo); ++i) { + if (this->parseExpectedStringToken(gUnitInfo[i].fUnitName)) { + *unit = gUnitInfo[i].fUnit; + return true; + } + } + return false; +} + +bool SkSVGAttributeParser::parseNamedColorToken(SkColor* c) { + if (const char* next = SkParse::FindNamedColor(fCurPos, strlen(fCurPos), c)) { + fCurPos = next; + return true; + } + return false; +} + +bool SkSVGAttributeParser::parseHexColorToken(SkColor* c) { + uint32_t v; + const char* initial = fCurPos; + + if (!this->parseExpectedStringToken("#") || !this->parseHexToken(&v)) { + return false; + } + + switch (fCurPos - initial) { + case 7: + // matched #xxxxxxx + break; + case 4: + // matched '#xxx; + v = ((v << 12) & 0x00f00000) | + ((v << 8) & 0x000ff000) | + ((v << 4) & 0x00000ff0) | + ((v << 0) & 0x0000000f); + break; + default: + return false; + } + + *c = v | 0xff000000; + return true; +} + +// https://www.w3.org/TR/SVG/types.html#DataTypeColor +bool SkSVGAttributeParser::parseColor(SkSVGColor* color) { + SkColor c; + + // TODO: rgb(...) + if (this->parseHexColorToken(&c) || this->parseNamedColorToken(&c)) { + *color = SkSVGColor(c); + return true; + } + + return false; +} + +// https://www.w3.org/TR/SVG/types.html#DataTypeNumber +bool SkSVGAttributeParser::parseNumber(SkSVGNumber* number) { + // consume WS + this->parseWSToken(); + + SkScalar s; + if (this->parseScalarToken(&s)) { + *number = SkSVGNumber(s); + // consume trailing separators + this->parseSepToken(); + return true; + } + + return false; +} + +// https://www.w3.org/TR/SVG/types.html#DataTypeLength +bool SkSVGAttributeParser::parseLength(SkSVGLength* length) { + SkScalar s; + SkSVGLength::Unit u = SkSVGLength::Unit::kNumber; + + if (this->parseScalarToken(&s) && + (this->parseLengthUnitToken(&u) || this->parseSepToken() || this->parseEOSToken())) { + *length = SkSVGLength(s, u); + // consume trailing separators + this->parseSepToken(); + return true; + } + + return false; +} diff --git a/experimental/svg/model/SkSVGAttributeParser.h b/experimental/svg/model/SkSVGAttributeParser.h new file mode 100644 index 0000000000..aef0286504 --- /dev/null +++ b/experimental/svg/model/SkSVGAttributeParser.h @@ -0,0 +1,45 @@ +/* + * 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 SkSVGAttributeParser_DEFINED +#define SkSVGAttributeParser_DEFINED + +#include "SkSVGTypes.h" + +class SkSVGAttributeParser : public SkNoncopyable { +public: + SkSVGAttributeParser(const char[]); + + bool parseColor(SkSVGColor*); + bool parseNumber(SkSVGNumber*); + bool parseLength(SkSVGLength*); + +private: + // Stack-only + void* operator new(size_t) = delete; + void* operator new(size_t, void*) = delete; + + template + bool advanceWhile(F func); + + bool parseWSToken(); + bool parseEOSToken(); + bool parseSepToken(); + bool parseExpectedStringToken(const char*); + bool parseScalarToken(SkScalar*); + bool parseHexToken(uint32_t*); + bool parseLengthUnitToken(SkSVGLength::Unit*); + bool parseNamedColorToken(SkColor*); + bool parseHexColorToken(SkColor*); + + // The current position in the input string. + const char* fCurPos; + + typedef SkNoncopyable INHERITED; +}; + +#endif // SkSVGAttributeParser_DEFINED diff --git a/experimental/svg/model/SkSVGDOM.cpp b/experimental/svg/model/SkSVGDOM.cpp index 4e2f898942..df589230e0 100644 --- a/experimental/svg/model/SkSVGDOM.cpp +++ b/experimental/svg/model/SkSVGDOM.cpp @@ -10,45 +10,20 @@ #include "SkParse.h" #include "SkParsePath.h" #include "SkString.h" +#include "SkSVGAttributeParser.h" #include "SkSVGDOM.h" #include "SkSVGG.h" #include "SkSVGNode.h" #include "SkSVGPath.h" +#include "SkSVGRect.h" +#include "SkSVGRenderContext.h" #include "SkSVGSVG.h" +#include "SkSVGTypes.h" #include "SkSVGValue.h" #include "SkTSearch.h" namespace { -SkColor ParseColor(const char* str) { - // FIXME: real parser - if (*str++ != '#') { - return SK_ColorBLACK; - } - - uint32_t v; - const char* consumed = SkParse::FindHex(str, &v); - - switch(consumed - str) { - case 6: - // matched '#xxxxxx' - break; - case 3: - // matched '#xxx; - v = ((v << 12) & 0x00f00000) | - ((v << 8) & 0x000ff000) | - ((v << 4) & 0x00000ff0) | - ((v << 0) & 0x0000000f); - break; - default: - // failed - v = 0; - break; - } - - return v | 0xff000000; -} - const char* ParseScalarPair(const char* str, SkScalar v[2]) { str = SkParse::FindScalar(str, v); if (str) { @@ -98,7 +73,13 @@ SkMatrix ParseTransform(const char* str) { bool SetPaintAttribute(const sk_sp& node, SkSVGAttribute attr, const char* stringValue) { - node->setAttribute(attr, SkSVGColorValue(ParseColor(stringValue))); + SkSVGColor color; + SkSVGAttributeParser parser(stringValue); + if (!parser.parseColor(&color)) { + return false; + } + + node->setAttribute(attr, SkSVGColorValue(color)); return true; } @@ -119,6 +100,18 @@ bool SetTransformAttribute(const sk_sp& node, SkSVGAttribute attr, return true; } +bool SetLengthAttribute(const sk_sp& node, SkSVGAttribute attr, + const char* stringValue) { + SkSVGLength length; + SkSVGAttributeParser parser(stringValue); + if (!parser.parseLength(&length)) { + return false; + } + + node->setAttribute(attr, SkSVGLengthValue(length)); + return true; +} + // Breaks a "foo: bar; baz: ..." string into key:value pairs. class StyleIterator { public: @@ -185,16 +178,21 @@ struct AttrParseInfo { }; SortedDictionaryEntry gAttributeParseInfo[] = { - { "d", { SkSVGAttribute::kD, SetPathDataAttribute }}, - { "fill", { SkSVGAttribute::kFill, SetPaintAttribute }}, - { "stroke", { SkSVGAttribute::kStroke, SetPaintAttribute }}, - { "style", { SkSVGAttribute::kUnknown, SetStyleAttributes }}, + { "d" , { SkSVGAttribute::kD , SetPathDataAttribute }}, + { "fill" , { SkSVGAttribute::kFill , SetPaintAttribute }}, + { "height" , { SkSVGAttribute::kHeight , SetLengthAttribute }}, + { "stroke" , { SkSVGAttribute::kStroke , SetPaintAttribute }}, + { "style" , { SkSVGAttribute::kUnknown , SetStyleAttributes }}, { "transform", { SkSVGAttribute::kTransform, SetTransformAttribute }}, + { "width" , { SkSVGAttribute::kWidth , SetLengthAttribute }}, + { "x" , { SkSVGAttribute::kX , SetLengthAttribute }}, + { "y" , { SkSVGAttribute::kY , SetLengthAttribute }}, }; SortedDictionaryEntry(*)()> gTagFactories[] = { { "g" , []() -> sk_sp { return SkSVGG::Make(); }}, { "path", []() -> sk_sp { return SkSVGPath::Make(); }}, + { "rect", []() -> sk_sp { return SkSVGRect::Make(); }}, { "svg" , []() -> sk_sp { return SkSVGSVG::Make(); }}, }; @@ -294,7 +292,8 @@ sk_sp SkSVGDOM::MakeFromStream(SkStream& svgStream, const SkSize& cont void SkSVGDOM::render(SkCanvas* canvas) const { if (fRoot) { - fRoot->render(canvas); + SkSVGRenderContext ctx(fContainerSize); + fRoot->render(canvas, ctx); } } diff --git a/experimental/svg/model/SkSVGNode.cpp b/experimental/svg/model/SkSVGNode.cpp index 58e0ad4989..eb82834f38 100644 --- a/experimental/svg/model/SkSVGNode.cpp +++ b/experimental/svg/model/SkSVGNode.cpp @@ -16,10 +16,6 @@ SkSVGNode::SkSVGNode(SkSVGTag t) : fTag(t) { } SkSVGNode::~SkSVGNode() { } -void SkSVGNode::render(SkCanvas* canvas) const { - this->render(canvas, SkSVGRenderContext()); -} - void SkSVGNode::render(SkCanvas* canvas, const SkSVGRenderContext& ctx) const { SkTCopyOnFirstWrite localContext(ctx); fPresentationAttributes.applyTo(localContext); @@ -51,6 +47,7 @@ void SkSVGNode::onSetAttribute(SkSVGAttribute attr, const SkSVGValue& v) { } break; default: + SkDebugf("attribute ID <%d> ignored for node <%d>\n", attr, fTag); break; } } diff --git a/experimental/svg/model/SkSVGNode.h b/experimental/svg/model/SkSVGNode.h index 407e69ac63..168b03c73b 100644 --- a/experimental/svg/model/SkSVGNode.h +++ b/experimental/svg/model/SkSVGNode.h @@ -19,6 +19,7 @@ class SkSVGValue; enum class SkSVGTag { kG, kPath, + kRect, kSvg }; @@ -30,7 +31,6 @@ public: virtual void appendChild(sk_sp) = 0; - void render(SkCanvas*) const; void render(SkCanvas*, const SkSVGRenderContext&) const; void setAttribute(SkSVGAttribute, const SkSVGValue&); diff --git a/experimental/svg/model/SkSVGPath.cpp b/experimental/svg/model/SkSVGPath.cpp index 7d905945bc..07e0a3e545 100644 --- a/experimental/svg/model/SkSVGPath.cpp +++ b/experimental/svg/model/SkSVGPath.cpp @@ -13,17 +13,6 @@ SkSVGPath::SkSVGPath() : INHERITED(SkSVGTag::kPath) { } -void SkSVGPath::doRender(SkCanvas* canvas, const SkPaint* paint) const { - if (paint) { - canvas->drawPath(fPath, *paint); - } -} - -void SkSVGPath::onRender(SkCanvas* canvas, const SkSVGRenderContext& ctx) const { - this->doRender(canvas, ctx.fillPaint()); - this->doRender(canvas, ctx.strokePaint()); -} - void SkSVGPath::onSetAttribute(SkSVGAttribute attr, const SkSVGValue& v) { switch (attr) { case SkSVGAttribute::kD: @@ -35,3 +24,7 @@ void SkSVGPath::onSetAttribute(SkSVGAttribute attr, const SkSVGValue& v) { this->INHERITED::onSetAttribute(attr, v); } } + +void SkSVGPath::onDraw(SkCanvas* canvas, const SkSVGLengthContext&, const SkPaint& paint) const { + canvas->drawPath(fPath, paint); +} diff --git a/experimental/svg/model/SkSVGPath.h b/experimental/svg/model/SkSVGPath.h index b650f67463..e72f0d137e 100644 --- a/experimental/svg/model/SkSVGPath.h +++ b/experimental/svg/model/SkSVGPath.h @@ -9,30 +9,26 @@ #define SkSVGPath_DEFINED #include "SkPath.h" -#include "SkSVGTransformableNode.h" +#include "SkSVGShape.h" -class SkSVGPath final : public SkSVGTransformableNode { +class SkSVGPath final : public SkSVGShape { public: virtual ~SkSVGPath() = default; static sk_sp Make() { return sk_sp(new SkSVGPath()); } - void appendChild(sk_sp) override { } - void setPath(const SkPath& path) { fPath = path; } protected: - void onRender(SkCanvas*, const SkSVGRenderContext&) const override; - void onSetAttribute(SkSVGAttribute, const SkSVGValue&) override; + void onDraw(SkCanvas*, const SkSVGLengthContext&, const SkPaint&) const override; + private: SkSVGPath(); - void doRender(SkCanvas*, const SkPaint*) const; - SkPath fPath; - typedef SkSVGTransformableNode INHERITED; + typedef SkSVGShape INHERITED; }; #endif // SkSVGPath_DEFINED diff --git a/experimental/svg/model/SkSVGRect.cpp b/experimental/svg/model/SkSVGRect.cpp new file mode 100644 index 0000000000..288fc48258 --- /dev/null +++ b/experimental/svg/model/SkSVGRect.cpp @@ -0,0 +1,68 @@ +/* + * 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 "SkCanvas.h" +#include "SkRect.h" +#include "SkSVGRect.h" +#include "SkSVGRenderContext.h" +#include "SkSVGValue.h" + +SkSVGRect::SkSVGRect() : INHERITED(SkSVGTag::kRect) {} + +void SkSVGRect::setX(const SkSVGLength& x) { + fX = x; +} + +void SkSVGRect::setY(const SkSVGLength& y) { + fY = y; +} + +void SkSVGRect::setWidth(const SkSVGLength& w) { + fWidth = w; +} + +void SkSVGRect::setHeight(const SkSVGLength& h) { + fHeight = h; +} + +void SkSVGRect::onSetAttribute(SkSVGAttribute attr, const SkSVGValue& v) { + switch (attr) { + case SkSVGAttribute::kX: + if (const auto* x = v.as()) { + this->setX(*x); + } + break; + case SkSVGAttribute::kY: + if (const auto* y = v.as()) { + this->setY(*y); + } + break; + case SkSVGAttribute::kWidth: + if (const auto* w = v.as()) { + this->setWidth(*w); + } + break; + case SkSVGAttribute::kHeight: + if (const auto* h = v.as()) { + this->setHeight(*h); + } + break; + default: + this->INHERITED::onSetAttribute(attr, v); + } +} + +void SkSVGRect::onDraw(SkCanvas* canvas, const SkSVGLengthContext& lctx, + const SkPaint& paint) const { + const SkRect r = SkRect::MakeXYWH( + lctx.resolve(fX, SkSVGLengthContext::LengthType::kHorizontal), + lctx.resolve(fY, SkSVGLengthContext::LengthType::kVertical), + lctx.resolve(fWidth, SkSVGLengthContext::LengthType::kHorizontal), + lctx.resolve(fHeight, SkSVGLengthContext::LengthType::kVertical)); + + canvas->drawRect(r, paint); +} diff --git a/experimental/svg/model/SkSVGRect.h b/experimental/svg/model/SkSVGRect.h new file mode 100644 index 0000000000..b4cd363d77 --- /dev/null +++ b/experimental/svg/model/SkSVGRect.h @@ -0,0 +1,40 @@ +/* + * 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 SkSVGRect_DEFINED +#define SkSVGRect_DEFINED + +#include "SkSVGShape.h" +#include "SkSVGTypes.h" + +class SkSVGRect final : public SkSVGShape { +public: + virtual ~SkSVGRect() = default; + static sk_sp Make() { return sk_sp(new SkSVGRect()); } + + void setX(const SkSVGLength&); + void setY(const SkSVGLength&); + void setWidth(const SkSVGLength&); + void setHeight(const SkSVGLength&); + +protected: + void onSetAttribute(SkSVGAttribute, const SkSVGValue&) override; + + void onDraw(SkCanvas*, const SkSVGLengthContext&, const SkPaint&) const override; + +private: + SkSVGRect(); + + SkSVGLength fX = SkSVGLength(0); + SkSVGLength fY = SkSVGLength(0); + SkSVGLength fWidth = SkSVGLength(0); + SkSVGLength fHeight = SkSVGLength(0); + + typedef SkSVGShape INHERITED; +}; + +#endif // SkSVGRect_DEFINED diff --git a/experimental/svg/model/SkSVGRenderContext.cpp b/experimental/svg/model/SkSVGRenderContext.cpp index 703606c528..38498d36c8 100644 --- a/experimental/svg/model/SkSVGRenderContext.cpp +++ b/experimental/svg/model/SkSVGRenderContext.cpp @@ -6,8 +6,44 @@ */ #include "SkSVGRenderContext.h" +#include "SkSVGTypes.h" -SkSVGRenderContext::SkSVGRenderContext() { } +namespace { + +SkScalar length_size_for_type(const SkSize& viewport, SkSVGLengthContext::LengthType t) { + switch (t) { + case SkSVGLengthContext::LengthType::kHorizontal: + return viewport.width(); + case SkSVGLengthContext::LengthType::kVertical: + return viewport.height(); + case SkSVGLengthContext::LengthType::kOther: + return SkScalarSqrt(viewport.width() * viewport.height()); + } + + SkASSERT(false); // Not reached. + return 0; +} + +} // anonymous ns + +SkScalar SkSVGLengthContext::resolve(const SkSVGLength& l, LengthType t) const { + switch (l.unit()) { + case SkSVGLength::Unit::kNumber: + 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; +} + +SkSVGRenderContext::SkSVGRenderContext(const SkSize& initialViewport) + : fLengthContext(initialViewport) {} SkSVGRenderContext& SkSVGRenderContext::operator=(const SkSVGRenderContext& other) { if (other.fFill.isValid()) { diff --git a/experimental/svg/model/SkSVGRenderContext.h b/experimental/svg/model/SkSVGRenderContext.h index 8ebaf12f0c..50a6d59e5e 100644 --- a/experimental/svg/model/SkSVGRenderContext.h +++ b/experimental/svg/model/SkSVGRenderContext.h @@ -8,17 +8,39 @@ #ifndef SkSVGRenderContext_DEFINED #define SkSVGRenderContext_DEFINED +#include "SkSize.h" #include "SkPaint.h" #include "SkTLazy.h" class SkPaint; +class SkSVGLength; + +class SkSVGLengthContext { +public: + SkSVGLengthContext(const SkSize& viewport) : fViewport(viewport) {} + + enum class LengthType { + kHorizontal, + kVertical, + kOther, + }; + + void setViewPort(const SkSize& viewport) { fViewport = viewport; } + + SkScalar resolve(const SkSVGLength&, LengthType) const; + +private: + SkSize fViewport; +}; class SkSVGRenderContext { public: - SkSVGRenderContext(); + explicit SkSVGRenderContext(const SkSize& initialViewport); SkSVGRenderContext(const SkSVGRenderContext&) = default; SkSVGRenderContext& operator=(const SkSVGRenderContext&); + const SkSVGLengthContext& lengthContext() const { return fLengthContext; } + const SkPaint* fillPaint() const { return fFill.getMaybeNull(); } const SkPaint* strokePaint() const { return fStroke.getMaybeNull(); } @@ -29,8 +51,9 @@ private: SkPaint& ensureFill(); SkPaint& ensureStroke(); - SkTLazy fFill; - SkTLazy fStroke; + SkSVGLengthContext fLengthContext; + SkTLazy fFill; + SkTLazy fStroke; }; #endif // SkSVGRenderContext_DEFINED diff --git a/experimental/svg/model/SkSVGSVG.cpp b/experimental/svg/model/SkSVGSVG.cpp index aeccc7082b..42bd280a5e 100644 --- a/experimental/svg/model/SkSVGSVG.cpp +++ b/experimental/svg/model/SkSVGSVG.cpp @@ -6,5 +6,49 @@ */ #include "SkSVGSVG.h" +#include "SkSVGValue.h" SkSVGSVG::SkSVGSVG() : INHERITED(SkSVGTag::kSvg) { } + +void SkSVGSVG::setX(const SkSVGLength& x) { + fX = x; +} + +void SkSVGSVG::setY(const SkSVGLength& y) { + fY = y; +} + +void SkSVGSVG::setWidth(const SkSVGLength& w) { + fWidth = w; +} + +void SkSVGSVG::setHeight(const SkSVGLength& h) { + fHeight = h; +} + +void SkSVGSVG::onSetAttribute(SkSVGAttribute attr, const SkSVGValue& v) { + switch (attr) { + case SkSVGAttribute::kX: + if (const auto* x = v.as()) { + this->setX(*x); + } + break; + case SkSVGAttribute::kY: + if (const auto* y = v.as()) { + this->setY(*y); + } + break; + case SkSVGAttribute::kWidth: + if (const auto* w = v.as()) { + this->setWidth(*w); + } + break; + case SkSVGAttribute::kHeight: + if (const auto* h = v.as()) { + this->setHeight(*h); + } + break; + default: + this->INHERITED::onSetAttribute(attr, v); + } +} diff --git a/experimental/svg/model/SkSVGSVG.h b/experimental/svg/model/SkSVGSVG.h index 8e69d144d6..27631f5a31 100644 --- a/experimental/svg/model/SkSVGSVG.h +++ b/experimental/svg/model/SkSVGSVG.h @@ -9,6 +9,7 @@ #define SkSVGSVG_DEFINED #include "SkSVGContainer.h" +#include "SkSVGTypes.h" class SkSVGSVG : public SkSVGContainer { public: @@ -16,9 +17,22 @@ public: static sk_sp Make() { return sk_sp(new SkSVGSVG()); } + void setX(const SkSVGLength&); + void setY(const SkSVGLength&); + void setWidth(const SkSVGLength&); + void setHeight(const SkSVGLength&); + +protected: + void onSetAttribute(SkSVGAttribute, const SkSVGValue&) override; + private: SkSVGSVG(); + SkSVGLength fX = SkSVGLength(0); + SkSVGLength fY = SkSVGLength(0); + SkSVGLength fWidth = SkSVGLength(100, SkSVGLength::Unit::kPercentage); + SkSVGLength fHeight = SkSVGLength(100, SkSVGLength::Unit::kPercentage); + typedef SkSVGContainer INHERITED; }; diff --git a/experimental/svg/model/SkSVGShape.cpp b/experimental/svg/model/SkSVGShape.cpp new file mode 100644 index 0000000000..d26eff482d --- /dev/null +++ b/experimental/svg/model/SkSVGShape.cpp @@ -0,0 +1,25 @@ +/* + * 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 "SkSVGShape.h" + +SkSVGShape::SkSVGShape(SkSVGTag t) : INHERITED(t) {} + +void SkSVGShape::onRender(SkCanvas* canvas, const SkSVGRenderContext& ctx) const { + if (const SkPaint* fillPaint = ctx.fillPaint()) { + this->onDraw(canvas, ctx.lengthContext(), *fillPaint); + } + + if (const SkPaint* strokePaint = ctx.strokePaint()) { + this->onDraw(canvas, ctx.lengthContext(), *strokePaint); + } +} + +void SkSVGShape::appendChild(sk_sp) { + SkDebugf("cannot append child nodes to an SVG shape.\n"); +} diff --git a/experimental/svg/model/SkSVGShape.h b/experimental/svg/model/SkSVGShape.h new file mode 100644 index 0000000000..2b3ea96d54 --- /dev/null +++ b/experimental/svg/model/SkSVGShape.h @@ -0,0 +1,33 @@ +/* + * 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 SkSVGShape_DEFINED +#define SkSVGShape_DEFINED + +#include "SkSVGTransformableNode.h" + +class SkSVGLengthContext; +class SkPaint; + +class SkSVGShape : public SkSVGTransformableNode { +public: + virtual ~SkSVGShape() = default; + + void appendChild(sk_sp) override; + +protected: + SkSVGShape(SkSVGTag); + + void onRender(SkCanvas*, const SkSVGRenderContext&) const final; + + virtual void onDraw(SkCanvas*, const SkSVGLengthContext&, const SkPaint&) const = 0; + +private: + typedef SkSVGTransformableNode INHERITED; +}; + +#endif // SkSVGShape_DEFINED diff --git a/experimental/svg/model/SkSVGTypes.h b/experimental/svg/model/SkSVGTypes.h new file mode 100644 index 0000000000..6d34d9943a --- /dev/null +++ b/experimental/svg/model/SkSVGTypes.h @@ -0,0 +1,72 @@ +/* + * 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 SkSVGTypes_DEFINED +#define SkSVGTypes_DEFINED + +#include "SkColor.h" +#include "SkScalar.h" +#include "SkTypes.h" + +class SkSVGNumber { +public: + constexpr SkSVGNumber() : fValue(0) {} + explicit constexpr SkSVGNumber(SkScalar v) : fValue(v) {} + SkSVGNumber(const SkSVGNumber&) = default; + SkSVGNumber& operator=(const SkSVGNumber&) = default; + + + const SkScalar& value() const { return fValue; } + + operator const SkScalar&() const { return fValue; } + +private: + SkScalar fValue; +}; + +class SkSVGLength { +public: + enum class Unit { + kUnknown, + kNumber, + kPercentage, + kEMS, + kEXS, + kPX, + kCM, + kMM, + kIN, + kPT, + kPC, + }; + + constexpr SkSVGLength() : fValue(0), fUnit(Unit::kUnknown) {} + explicit constexpr SkSVGLength(SkScalar v, Unit u = Unit::kNumber) + : fValue(v), fUnit(u) {} + SkSVGLength(const SkSVGLength&) = default; + SkSVGLength& operator=(const SkSVGLength&) = default; + + const SkScalar& value() const { return fValue; } + const Unit& unit() const { return fUnit; } + +private: + SkScalar fValue; + Unit fUnit; +}; + +class SkSVGColor { +public: + constexpr SkSVGColor() : fValue(SK_ColorBLACK) {} + explicit constexpr SkSVGColor(SkColor c) : fValue(c) {} + + operator const SkColor&() const { return fValue; } + +private: + SkColor fValue; +}; + +#endif // SkSVGTypes_DEFINED diff --git a/experimental/svg/model/SkSVGValue.cpp b/experimental/svg/model/SkSVGValue.cpp index 674a200a34..aec49d239f 100644 --- a/experimental/svg/model/SkSVGValue.cpp +++ b/experimental/svg/model/SkSVGValue.cpp @@ -5,4 +5,5 @@ * found in the LICENSE file. */ +#include "SkSVGTypes.h" #include "SkSVGValue.h" diff --git a/experimental/svg/model/SkSVGValue.h b/experimental/svg/model/SkSVGValue.h index 3d3cd6efc1..a83c4fe07f 100644 --- a/experimental/svg/model/SkSVGValue.h +++ b/experimental/svg/model/SkSVGValue.h @@ -11,14 +11,16 @@ #include "SkColor.h" #include "SkMatrix.h" #include "SkPath.h" +#include "SkSVGTypes.h" #include "SkTypes.h" class SkSVGValue : public SkNoncopyable { public: enum class Type { - Color, - Path, - Transform, + kColor, + kLength, + kPath, + kTransform, }; Type type() const { return fType; } @@ -33,6 +35,8 @@ protected: private: Type fType; + + typedef SkNoncopyable INHERITED; }; template @@ -49,11 +53,12 @@ public: private: SkiaType fWrappedValue; - using INHERITED = SkSVGValue; + typedef SkSVGValue INHERITED; }; -using SkSVGColorValue = SkSVGWrapperValue; -using SkSVGPathValue = SkSVGWrapperValue; -using SkSVGTransformValue = SkSVGWrapperValue; +using SkSVGColorValue = SkSVGWrapperValue; +using SkSVGLengthValue = SkSVGWrapperValue; +using SkSVGPathValue = SkSVGWrapperValue; +using SkSVGTransformValue = SkSVGWrapperValue; #endif // SkSVGValue_DEFINED diff --git a/gyp/svg.gyp b/gyp/svg.gyp index f5b43f8f07..4151a3d7b7 100644 --- a/gyp/svg.gyp +++ b/gyp/svg.gyp @@ -46,6 +46,8 @@ 'sources': [ '../experimental/svg/model/SkSVGAttribute.h', '../experimental/svg/model/SkSVGAttribute.cpp', + '../experimental/svg/model/SkSVGAttributeParser.h', + '../experimental/svg/model/SkSVGAttributeParser.cpp', '../experimental/svg/model/SkSVGContainer.h', '../experimental/svg/model/SkSVGContainer.cpp', '../experimental/svg/model/SkSVGDOM.h', @@ -55,12 +57,17 @@ '../experimental/svg/model/SkSVGNode.cpp', '../experimental/svg/model/SkSVGPath.h', '../experimental/svg/model/SkSVGPath.cpp', + '../experimental/svg/model/SkSVGRect.h', + '../experimental/svg/model/SkSVGRect.cpp', '../experimental/svg/model/SkSVGRenderContext.h', '../experimental/svg/model/SkSVGRenderContext.cpp', + '../experimental/svg/model/SkSVGShape.h', + '../experimental/svg/model/SkSVGShape.cpp', '../experimental/svg/model/SkSVGSVG.h', '../experimental/svg/model/SkSVGSVG.cpp', '../experimental/svg/model/SkSVGTransformableNode.h', '../experimental/svg/model/SkSVGTransformableNode.cpp', + '../experimental/svg/model/SkSVGTypes.h', '../experimental/svg/model/SkSVGValue.h', '../experimental/svg/model/SkSVGValue.cpp', ],