[SVGDom] Add viewBox support
authorfmalita <fmalita@chromium.org>
Mon, 8 Aug 2016 18:38:55 +0000 (11:38 -0700)
committerCommit bot <commit-bot@chromium.org>
Mon, 8 Aug 2016 18:38:55 +0000 (11:38 -0700)
The main feature is <svg> viewBox and proper viewport support, but the CL
touches a few other things:

* refactor SkSVGRenderContext to auto-restore canvas state, and split the
  presentation bits into a separate CoW SkSVGPresentationContext

* introduce SkSVGNode::onPrepareToRender(), as a way for nodes to push their
  custom state before the actual onRender() call (instead of relying on
  non-virtual SkSVGNode to know about all possible state bits)

* add a "Type" suffix to SVG types, to disambiguate (e.g. SkSVGRectType vs.
  SkSVGRect)

R=robertphillips@google.com,stephana@google.com
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2222793002

Review-Url: https://codereview.chromium.org/2222793002

20 files changed:
experimental/svg/model/SkSVGAttribute.cpp
experimental/svg/model/SkSVGAttribute.h
experimental/svg/model/SkSVGAttributeParser.cpp
experimental/svg/model/SkSVGAttributeParser.h
experimental/svg/model/SkSVGContainer.cpp
experimental/svg/model/SkSVGContainer.h
experimental/svg/model/SkSVGDOM.cpp
experimental/svg/model/SkSVGNode.cpp
experimental/svg/model/SkSVGNode.h
experimental/svg/model/SkSVGRect.cpp
experimental/svg/model/SkSVGRenderContext.cpp
experimental/svg/model/SkSVGRenderContext.h
experimental/svg/model/SkSVGSVG.cpp
experimental/svg/model/SkSVGSVG.h
experimental/svg/model/SkSVGShape.cpp
experimental/svg/model/SkSVGShape.h
experimental/svg/model/SkSVGTransformableNode.cpp
experimental/svg/model/SkSVGTransformableNode.h
experimental/svg/model/SkSVGTypes.h
experimental/svg/model/SkSVGValue.h

index 79c3551..2f1cace 100644 (file)
@@ -12,23 +12,23 @@ SkSVGPresentationAttributes::SkSVGPresentationAttributes()
     : fFillIsSet(false)
     , fStrokeIsSet(false) { }
 
-void SkSVGPresentationAttributes::setFill(const SkSVGColor& c) {
+void SkSVGPresentationAttributes::setFill(const SkSVGColorType& c) {
     fFill = c;
     fFillIsSet = true;
 }
 
-void SkSVGPresentationAttributes::setStroke(const SkSVGColor& c) {
+void SkSVGPresentationAttributes::setStroke(const SkSVGColorType& c) {
     fStroke = c;
     fStrokeIsSet = true;
 }
 
 
-void SkSVGPresentationAttributes::applyTo(SkTCopyOnFirstWrite<SkSVGRenderContext>& ctx) const {
+void SkSVGPresentationAttributes::applyTo(SkSVGRenderContext* ctx) const {
     if (fFillIsSet) {
-        ctx.writable()->setFillColor(fFill);
+        ctx->writablePresentationContext()->setFillColor(fFill);
     }
 
     if (fStrokeIsSet) {
-        ctx.writable()->setStrokeColor(fStroke);
+        ctx->writablePresentationContext()->setStrokeColor(fStroke);
     }
 }
index 64465b1..d05737b 100644 (file)
 #include "SkSVGTypes.h"
 #include "SkTLazy.h"
 
+class SkSVGRenderContext;
+
 enum class SkSVGAttribute {
     kD,
     kFill,
     kHeight,
     kStroke,
     kTransform,
+    kViewBox,
     kWidth,
     kX,
     kY,
@@ -24,21 +27,19 @@ enum class SkSVGAttribute {
     kUnknown,
 };
 
-class SkSVGRenderContext;
-
 class SkSVGPresentationAttributes {
 public:
     SkSVGPresentationAttributes();
 
-    void setFill(const SkSVGColor&);
-    void setStroke(const SkSVGColor&);
+    void setFill(const SkSVGColorType&);
+    void setStroke(const SkSVGColorType&);
 
-    void applyTo(SkTCopyOnFirstWrite<SkSVGRenderContext>&) const;
+    void applyTo(SkSVGRenderContext*) const;
 
 private:
     // Color only for now.
-    SkSVGColor fFill;
-    SkSVGColor fStroke;
+    SkSVGColorType fFill;
+    SkSVGColorType fStroke;
 
     unsigned fFillIsSet   : 1;
     unsigned fStrokeIsSet : 1;
index 75e5d12..62ca4c8 100644 (file)
@@ -149,12 +149,12 @@ bool SkSVGAttributeParser::parseHexColorToken(SkColor* c) {
 }
 
 // https://www.w3.org/TR/SVG/types.html#DataTypeColor
-bool SkSVGAttributeParser::parseColor(SkSVGColor* color) {
+bool SkSVGAttributeParser::parseColor(SkSVGColorType* color) {
     SkColor c;
 
     // TODO: rgb(...)
     if (this->parseHexColorToken(&c) || this->parseNamedColorToken(&c)) {
-        *color = SkSVGColor(c);
+        *color = SkSVGColorType(c);
         return true;
     }
 
@@ -162,13 +162,13 @@ bool SkSVGAttributeParser::parseColor(SkSVGColor* color) {
 }
 
 // https://www.w3.org/TR/SVG/types.html#DataTypeNumber
-bool SkSVGAttributeParser::parseNumber(SkSVGNumber* number) {
+bool SkSVGAttributeParser::parseNumber(SkSVGNumberType* number) {
     // consume WS
     this->parseWSToken();
 
     SkScalar s;
     if (this->parseScalarToken(&s)) {
-        *number = SkSVGNumber(s);
+        *number = SkSVGNumberType(s);
         // consume trailing separators
         this->parseSepToken();
         return true;
@@ -192,3 +192,22 @@ bool SkSVGAttributeParser::parseLength(SkSVGLength* length) {
 
     return false;
 }
+
+// https://www.w3.org/TR/SVG/coords.html#ViewBoxAttribute
+bool SkSVGAttributeParser::parseViewBox(SkSVGViewBoxType* vb) {
+    SkScalar x, y, w, h;
+    this->parseWSToken();
+
+    bool parsedValue = false;
+    if (this->parseScalarToken(&x) && this->parseSepToken() &&
+        this->parseScalarToken(&y) && this->parseSepToken() &&
+        this->parseScalarToken(&w) && this->parseSepToken() &&
+        this->parseScalarToken(&h)) {
+
+        *vb = SkSVGViewBoxType(SkRect::MakeXYWH(x, y, w, h));
+        parsedValue = true;
+        // consume trailing whitespace
+        this->parseWSToken();
+    }
+    return parsedValue && this->parseEOSToken();
+}
index aef0286..cd50479 100644 (file)
@@ -14,9 +14,10 @@ class SkSVGAttributeParser : public SkNoncopyable {
 public:
     SkSVGAttributeParser(const char[]);
 
-    bool parseColor(SkSVGColor*);
-    bool parseNumber(SkSVGNumber*);
+    bool parseColor(SkSVGColorType*);
+    bool parseNumber(SkSVGNumberType*);
     bool parseLength(SkSVGLength*);
+    bool parseViewBox(SkSVGViewBoxType*);
 
 private:
     // Stack-only
index f866797..4f91b30 100644 (file)
@@ -14,8 +14,8 @@ void SkSVGContainer::appendChild(sk_sp<SkSVGNode> node) {
     fChildren.push_back(std::move(node));
 }
 
-void SkSVGContainer::onRender(SkCanvas* canvas, const SkSVGRenderContext& ctx) const {
+void SkSVGContainer::onRender(const SkSVGRenderContext& ctx) const {
     for (int i = 0; i < fChildren.count(); ++i) {
-        fChildren[i]->render(canvas, ctx);
+        fChildren[i]->render(ctx);
     }
 }
index 27c7944..9a3fd62 100644 (file)
@@ -20,7 +20,7 @@ public:
 protected:
     SkSVGContainer(SkSVGTag);
 
-    void onRender(SkCanvas*, const SkSVGRenderContext&) const override;
+    void onRender(const SkSVGRenderContext&) const override;
 
 private:
     SkSTArray<1, sk_sp<SkSVGNode>, true> fChildren;
index df58923..13d67b9 100644 (file)
@@ -73,7 +73,7 @@ SkMatrix ParseTransform(const char* str) {
 
 bool SetPaintAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr,
                        const char* stringValue) {
-    SkSVGColor color;
+    SkSVGColorType color;
     SkSVGAttributeParser parser(stringValue);
     if (!parser.parseColor(&color)) {
         return false;
@@ -112,6 +112,18 @@ bool SetLengthAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr,
     return true;
 }
 
+bool SetViewBoxAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr,
+                         const char* stringValue) {
+    SkSVGViewBoxType viewBox;
+    SkSVGAttributeParser parser(stringValue);
+    if (!parser.parseViewBox(&viewBox)) {
+        return false;
+    }
+
+    node->setAttribute(attr, SkSVGViewBoxValue(viewBox));
+    return true;
+}
+
 // Breaks a "foo: bar; baz: ..." string into key:value pairs.
 class StyleIterator {
 public:
@@ -184,6 +196,7 @@ SortedDictionaryEntry<AttrParseInfo> gAttributeParseInfo[] = {
     { "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    }},
@@ -292,8 +305,10 @@ sk_sp<SkSVGDOM> SkSVGDOM::MakeFromStream(SkStream& svgStream, const SkSize& cont
 
 void SkSVGDOM::render(SkCanvas* canvas) const {
     if (fRoot) {
-        SkSVGRenderContext ctx(fContainerSize);
-        fRoot->render(canvas, ctx);
+        SkSVGRenderContext ctx(canvas,
+                               SkSVGLengthContext(fContainerSize),
+                               SkSVGPresentationContext());
+        fRoot->render(ctx);
     }
 }
 
index eb82834..34c6e17 100644 (file)
@@ -16,18 +16,17 @@ SkSVGNode::SkSVGNode(SkSVGTag t) : fTag(t) { }
 
 SkSVGNode::~SkSVGNode() { }
 
-void SkSVGNode::render(SkCanvas* canvas, const SkSVGRenderContext& ctx) const {
-    SkTCopyOnFirstWrite<SkSVGRenderContext> localContext(ctx);
-    fPresentationAttributes.applyTo(localContext);
-
-    SkAutoCanvasRestore acr(canvas, false);
-    const SkMatrix& m = this->onLocalMatrix();
-    if (!m.isIdentity()) {
-        canvas->save();
-        canvas->concat(m);
+void SkSVGNode::render(const SkSVGRenderContext& ctx) const {
+    SkSVGRenderContext localContext(ctx);
+
+    if (this->onPrepareToRender(&localContext)) {
+        this->onRender(localContext);
     }
+}
 
-    this->onRender(canvas, *localContext);
+bool SkSVGNode::onPrepareToRender(SkSVGRenderContext* ctx) const {
+    fPresentationAttributes.applyTo(ctx);
+    return true;
 }
 
 void SkSVGNode::setAttribute(SkSVGAttribute attr, const SkSVGValue& v) {
@@ -51,7 +50,3 @@ void SkSVGNode::onSetAttribute(SkSVGAttribute attr, const SkSVGValue& v) {
         break;
     }
 }
-
-const SkMatrix& SkSVGNode::onLocalMatrix() const {
-    return SkMatrix::I();
-}
index 168b03c..38a17d8 100644 (file)
@@ -31,18 +31,24 @@ public:
 
     virtual void appendChild(sk_sp<SkSVGNode>) = 0;
 
-    void render(SkCanvas*, const SkSVGRenderContext&) const;
+    void render(const SkSVGRenderContext&) const;
 
     void setAttribute(SkSVGAttribute, const SkSVGValue&);
 
 protected:
     SkSVGNode(SkSVGTag);
 
-    virtual void onRender(SkCanvas*, const SkSVGRenderContext&) const = 0;
+    // Called before onRender(), to apply local attributes to the context.  Unlike onRender(),
+    // onPrepareToRender() bubbles up the inheritance chain: overriders should always call
+    // INHERITED::onPrepareToRender(), unless they intend to short-circuit rendering
+    // (return false).
+    // Implementations are expected to return true if rendering is to continue, or false if
+    // the node/subtree rendering is disabled.
+    virtual bool onPrepareToRender(SkSVGRenderContext*) const;
 
-    virtual void onSetAttribute(SkSVGAttribute, const SkSVGValue&);
+    virtual void onRender(const SkSVGRenderContext&) const = 0;
 
-    virtual const SkMatrix& onLocalMatrix() const;
+    virtual void onSetAttribute(SkSVGAttribute, const SkSVGValue&);
 
 private:
     SkSVGTag                    fTag;
index 288fc48..b65c2b9 100644 (file)
@@ -58,11 +58,5 @@ void SkSVGRect::onSetAttribute(SkSVGAttribute attr, const SkSVGValue& 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);
+    canvas->drawRect(lctx.resolveRect(fX, fY, fWidth, fHeight), paint);
 }
index 38498d3..e902d4e 100644 (file)
@@ -5,6 +5,7 @@
  * found in the LICENSE file.
  */
 
+#include "SkCanvas.h"
 #include "SkSVGRenderContext.h"
 #include "SkSVGTypes.h"
 
@@ -42,10 +43,27 @@ SkScalar SkSVGLengthContext::resolve(const SkSVGLength& l, LengthType t) const {
     return 0;
 }
 
-SkSVGRenderContext::SkSVGRenderContext(const SkSize& initialViewport)
-    : fLengthContext(initialViewport) {}
+SkRect SkSVGLengthContext::resolveRect(const SkSVGLength& x, const SkSVGLength& y,
+                                       const SkSVGLength& w, const SkSVGLength& h) const {
+    return SkRect::MakeXYWH(
+        this->resolve(x, SkSVGLengthContext::LengthType::kHorizontal),
+        this->resolve(y, SkSVGLengthContext::LengthType::kVertical),
+        this->resolve(w, SkSVGLengthContext::LengthType::kHorizontal),
+        this->resolve(h, SkSVGLengthContext::LengthType::kVertical));
+}
+
+SkSVGPresentationContext::SkSVGPresentationContext() {}
+
+SkSVGPresentationContext::SkSVGPresentationContext(const SkSVGPresentationContext& o) {
+    this->initFrom(o);
+}
+
+SkSVGPresentationContext& SkSVGPresentationContext::operator=(const SkSVGPresentationContext& o) {
+    this->initFrom(o);
+    return *this;
+}
 
-SkSVGRenderContext& SkSVGRenderContext::operator=(const SkSVGRenderContext& other) {
+void SkSVGPresentationContext::initFrom(const SkSVGPresentationContext& other) {
     if (other.fFill.isValid()) {
         fFill.set(*other.fFill.get());
     } else {
@@ -57,11 +75,9 @@ SkSVGRenderContext& SkSVGRenderContext::operator=(const SkSVGRenderContext& othe
     } else {
         fStroke.reset();
     }
-
-    return *this;
 }
 
-SkPaint& SkSVGRenderContext::ensureFill() {
+SkPaint& SkSVGPresentationContext::ensureFill() {
     if (!fFill.isValid()) {
         fFill.init();
         fFill.get()->setStyle(SkPaint::kFill_Style);
@@ -70,7 +86,7 @@ SkPaint& SkSVGRenderContext::ensureFill() {
     return *fFill.get();
 }
 
-SkPaint& SkSVGRenderContext::ensureStroke() {
+SkPaint& SkSVGPresentationContext::ensureStroke() {
     if (!fStroke.isValid()) {
         fStroke.init();
         fStroke.get()->setStyle(SkPaint::kStroke_Style);
@@ -79,10 +95,27 @@ SkPaint& SkSVGRenderContext::ensureStroke() {
     return *fStroke.get();
 }
 
-void SkSVGRenderContext::setFillColor(SkColor color) {
+void SkSVGPresentationContext::setFillColor(SkColor color) {
     this->ensureFill().setColor(color);
 }
 
-void SkSVGRenderContext::setStrokeColor(SkColor color) {
+void SkSVGPresentationContext::setStrokeColor(SkColor color) {
     this->ensureStroke().setColor(color);
 }
+
+SkSVGRenderContext::SkSVGRenderContext(SkCanvas* canvas,
+                                       const SkSVGLengthContext& lctx,
+                                       const SkSVGPresentationContext& pctx)
+    : fLengthContext(lctx)
+    , fPresentationContext(pctx)
+    , fCanvas(canvas)
+    , fCanvasSaveCount(canvas->getSaveCount()) {}
+
+SkSVGRenderContext::SkSVGRenderContext(const SkSVGRenderContext& other)
+    : SkSVGRenderContext(other.canvas(),
+                         other.lengthContext(),
+                         other.presentationContext()) {}
+
+SkSVGRenderContext::~SkSVGRenderContext() {
+    fCanvas->restoreToCount(fCanvasSaveCount);
+}
index 50a6d59..47886d7 100644 (file)
@@ -8,11 +8,13 @@
 #ifndef SkSVGRenderContext_DEFINED
 #define SkSVGRenderContext_DEFINED
 
-#include "SkSize.h"
 #include "SkPaint.h"
+#include "SkRect.h"
+#include "SkSize.h"
 #include "SkTLazy.h"
+#include "SkTypes.h"
 
-class SkPaint;
+class SkCanvas;
 class SkSVGLength;
 
 class SkSVGLengthContext {
@@ -25,21 +27,22 @@ public:
         kOther,
     };
 
+    const SkSize& viewPort() const { return fViewport; }
     void setViewPort(const SkSize& viewport) { fViewport = viewport; }
 
     SkScalar resolve(const SkSVGLength&, LengthType) const;
+    SkRect   resolveRect(const SkSVGLength& x, const SkSVGLength& y,
+                         const SkSVGLength& w, const SkSVGLength& h) const;
 
 private:
     SkSize fViewport;
 };
 
-class SkSVGRenderContext {
+class SkSVGPresentationContext {
 public:
-    explicit SkSVGRenderContext(const SkSize& initialViewport);
-    SkSVGRenderContext(const SkSVGRenderContext&) = default;
-    SkSVGRenderContext& operator=(const SkSVGRenderContext&);
-
-    const SkSVGLengthContext& lengthContext() const { return fLengthContext; }
+    SkSVGPresentationContext();
+    SkSVGPresentationContext(const SkSVGPresentationContext&);
+    SkSVGPresentationContext& operator=(const SkSVGPresentationContext&);
 
     const SkPaint* fillPaint() const { return fFill.getMaybeNull(); }
     const SkPaint* strokePaint() const { return fStroke.getMaybeNull(); }
@@ -48,12 +51,44 @@ public:
     void setStrokeColor(SkColor);
 
 private:
+    void initFrom(const SkSVGPresentationContext&);
+
     SkPaint& ensureFill();
     SkPaint& ensureStroke();
 
-    SkSVGLengthContext fLengthContext;
-    SkTLazy<SkPaint>   fFill;
-    SkTLazy<SkPaint>   fStroke;
+    // TODO: convert to regular SkPaints and track explicit attribute values instead.
+    SkTLazy<SkPaint> fFill;
+    SkTLazy<SkPaint> fStroke;
+};
+
+class SkSVGRenderContext {
+public:
+    SkSVGRenderContext(SkCanvas*, const SkSVGLengthContext&, const SkSVGPresentationContext&);
+    SkSVGRenderContext(const SkSVGRenderContext&);
+    ~SkSVGRenderContext();
+
+    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; }
+
+private:
+    // Stack-only
+    void* operator new(size_t)                               = delete;
+    void* operator new(size_t, void*)                        = delete;
+    SkSVGRenderContext& operator=(const SkSVGRenderContext&) = delete;
+
+    SkTCopyOnFirstWrite<SkSVGLengthContext>       fLengthContext;
+    SkTCopyOnFirstWrite<SkSVGPresentationContext> fPresentationContext;
+    SkCanvas*                                     fCanvas;
+    // The save count on 'fCanvas' at construction time.
+    // A restoreToCount() will be issued on destruction.
+    int                                           fCanvasSaveCount;
 };
 
 #endif // SkSVGRenderContext_DEFINED
index 42bd280..13f1804 100644 (file)
@@ -5,11 +5,45 @@
  * found in the LICENSE file.
  */
 
+#include "SkCanvas.h"
+#include "SkSVGRenderContext.h"
 #include "SkSVGSVG.h"
 #include "SkSVGValue.h"
 
 SkSVGSVG::SkSVGSVG() : INHERITED(SkSVGTag::kSvg) { }
 
+bool SkSVGSVG::onPrepareToRender(SkSVGRenderContext* ctx) const {
+    auto viewPortRect  = ctx->lengthContext().resolveRect(fX, fY, fWidth, fHeight);
+    auto contentMatrix = SkMatrix::MakeTrans(viewPortRect.x(), viewPortRect.y());
+    auto viewPort      = SkSize::Make(viewPortRect.width(), viewPortRect.height());
+
+    if (fViewBox.isValid()) {
+        const SkRect& viewBox = *fViewBox.get();
+
+        // An empty viewbox disables rendering.
+        if (viewBox.isEmpty()) {
+            return false;
+        }
+
+        // A viewBox overrides the intrinsic viewport.
+        viewPort = SkSize::Make(viewBox.width(), viewBox.height());
+
+        contentMatrix.preConcat(
+            SkMatrix::MakeRectToRect(viewBox, viewPortRect, SkMatrix::kFill_ScaleToFit));
+    }
+
+    if (!contentMatrix.isIdentity()) {
+        ctx->canvas()->save();
+        ctx->canvas()->concat(contentMatrix);
+    }
+
+    if (viewPort != ctx->lengthContext().viewPort()) {
+        ctx->writableLengthContext()->setViewPort(viewPort);
+    }
+
+    return this->INHERITED::onPrepareToRender(ctx);
+}
+
 void SkSVGSVG::setX(const SkSVGLength& x) {
     fX = x;
 }
@@ -26,6 +60,10 @@ void SkSVGSVG::setHeight(const SkSVGLength& h) {
     fHeight = h;
 }
 
+void SkSVGSVG::setViewBox(const SkSVGViewBoxType& vb) {
+    fViewBox.set(vb);
+}
+
 void SkSVGSVG::onSetAttribute(SkSVGAttribute attr, const SkSVGValue& v) {
     switch (attr) {
     case SkSVGAttribute::kX:
@@ -48,6 +86,11 @@ void SkSVGSVG::onSetAttribute(SkSVGAttribute attr, const SkSVGValue& v) {
             this->setHeight(*h);
         }
         break;
+    case SkSVGAttribute::kViewBox:
+        if (const auto* vb = v.as<SkSVGViewBoxValue>()) {
+            this->setViewBox(*vb);
+        }
+        break;
     default:
         this->INHERITED::onSetAttribute(attr, v);
     }
index 27631f5..ae4787c 100644 (file)
@@ -10,6 +10,7 @@
 
 #include "SkSVGContainer.h"
 #include "SkSVGTypes.h"
+#include "SkTLazy.h"
 
 class SkSVGSVG : public SkSVGContainer {
 public:
@@ -21,8 +22,11 @@ public:
     void setY(const SkSVGLength&);
     void setWidth(const SkSVGLength&);
     void setHeight(const SkSVGLength&);
+    void setViewBox(const SkSVGViewBoxType&);
 
 protected:
+    bool onPrepareToRender(SkSVGRenderContext*) const override;
+
     void onSetAttribute(SkSVGAttribute, const SkSVGValue&) override;
 
 private:
@@ -33,6 +37,8 @@ private:
     SkSVGLength fWidth  = SkSVGLength(100, SkSVGLength::Unit::kPercentage);
     SkSVGLength fHeight = SkSVGLength(100, SkSVGLength::Unit::kPercentage);
 
+    SkTLazy<SkSVGViewBoxType> fViewBox;
+
     typedef SkSVGContainer INHERITED;
 };
 
index d26eff4..ad4b5ce 100644 (file)
 
 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);
+void SkSVGShape::onRender(const SkSVGRenderContext& ctx) const {
+    if (const SkPaint* fillPaint = ctx.presentationContext().fillPaint()) {
+        this->onDraw(ctx.canvas(), ctx.lengthContext(), *fillPaint);
     }
 
-    if (const SkPaint* strokePaint = ctx.strokePaint()) {
-        this->onDraw(canvas, ctx.lengthContext(), *strokePaint);
+    if (const SkPaint* strokePaint = ctx.presentationContext().strokePaint()) {
+        this->onDraw(ctx.canvas(), ctx.lengthContext(), *strokePaint);
     }
 }
 
index 2b3ea96..1f3c455 100644 (file)
@@ -22,7 +22,7 @@ public:
 protected:
     SkSVGShape(SkSVGTag);
 
-    void onRender(SkCanvas*, const SkSVGRenderContext&) const final;
+    void onRender(const SkSVGRenderContext&) const final;
 
     virtual void onDraw(SkCanvas*, const SkSVGLengthContext&, const SkPaint&) const = 0;
 
index 3636754..2686a4c 100644 (file)
@@ -5,6 +5,8 @@
  * found in the LICENSE file.
  */
 
+#include "SkCanvas.h"
+#include "SkSVGRenderContext.h"
 #include "SkSVGTransformableNode.h"
 #include "SkSVGValue.h"
 
@@ -12,6 +14,16 @@ SkSVGTransformableNode::SkSVGTransformableNode(SkSVGTag tag)
     : INHERITED(tag)
     , fMatrix(SkMatrix::I()) { }
 
+
+bool SkSVGTransformableNode::onPrepareToRender(SkSVGRenderContext* ctx) const {
+    if (!fMatrix.isIdentity()) {
+        ctx->canvas()->save();
+        ctx->canvas()->concat(fMatrix);
+    }
+
+    return this->INHERITED::onPrepareToRender(ctx);
+}
+
 void SkSVGTransformableNode::onSetAttribute(SkSVGAttribute attr, const SkSVGValue& v) {
     switch (attr) {
     case SkSVGAttribute::kTransform:
index c345e85..475fafb 100644 (file)
@@ -20,9 +20,9 @@ public:
 protected:
     SkSVGTransformableNode(SkSVGTag);
 
-    void onSetAttribute(SkSVGAttribute, const SkSVGValue&) override;
+    bool onPrepareToRender(SkSVGRenderContext*) const override;
 
-    const SkMatrix& onLocalMatrix() const override { return fMatrix; }
+    void onSetAttribute(SkSVGAttribute, const SkSVGValue&) override;
 
 private:
     // FIXME: should be sparse
index 6d34d99..ab07d7b 100644 (file)
@@ -9,25 +9,30 @@
 #define SkSVGTypes_DEFINED
 
 #include "SkColor.h"
+#include "SkRect.h"
 #include "SkScalar.h"
 #include "SkTypes.h"
 
-class SkSVGNumber {
+template <typename T>
+class SkSVGPrimitiveTypeWrapper {
 public:
-    constexpr SkSVGNumber()                    : fValue(0) {}
-    explicit constexpr SkSVGNumber(SkScalar v) : fValue(v) {}
-    SkSVGNumber(const SkSVGNumber&)            = default;
-    SkSVGNumber& operator=(const SkSVGNumber&) = default;
+    SkSVGPrimitiveTypeWrapper() = default;
+    explicit constexpr SkSVGPrimitiveTypeWrapper(T v) : fValue(v) {}
 
+    SkSVGPrimitiveTypeWrapper(const SkSVGPrimitiveTypeWrapper&)            = default;
+    SkSVGPrimitiveTypeWrapper& operator=(const SkSVGPrimitiveTypeWrapper&) = default;
 
-    const SkScalar& value() const { return fValue; }
-
-    operator const SkScalar&() const { return fValue; }
+    const T& value() const { return fValue; }
+    operator const T&() const { return fValue; }
 
 private:
-    SkScalar fValue;
+    T fValue;
 };
 
+using SkSVGColorType   = SkSVGPrimitiveTypeWrapper<SkColor >;
+using SkSVGNumberType  = SkSVGPrimitiveTypeWrapper<SkScalar>;
+using SkSVGViewBoxType = SkSVGPrimitiveTypeWrapper<SkRect  >;
+
 class SkSVGLength {
 public:
     enum class Unit {
@@ -58,15 +63,4 @@ private:
     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
index a83c4fe..77619c2 100644 (file)
@@ -21,6 +21,7 @@ public:
         kLength,
         kPath,
         kTransform,
+        kViewBox,
     };
 
     Type type() const { return fType; }
@@ -39,26 +40,31 @@ private:
     typedef SkNoncopyable INHERITED;
 };
 
-template <typename SkiaType, SkSVGValue::Type ValueType>
+template <typename T, SkSVGValue::Type ValueType>
 class SkSVGWrapperValue final : public SkSVGValue {
 public:
     static constexpr Type TYPE = ValueType;
 
-    explicit SkSVGWrapperValue(const SkiaType& v)
+    explicit SkSVGWrapperValue(const T& v)
         : INHERITED(ValueType)
         , fWrappedValue(v) { }
 
-    operator const SkiaType&() const { return fWrappedValue; }
+    operator const T&() const { return fWrappedValue; }
 
 private:
-    SkiaType fWrappedValue;
+    // Stack-only
+    void* operator new(size_t) = delete;
+    void* operator new(size_t, void*) = delete;
+
+    const T& fWrappedValue;
 
     typedef SkSVGValue INHERITED;
 };
 
-using SkSVGColorValue     = SkSVGWrapperValue<SkSVGColor , SkSVGValue::Type::kColor    >;
-using SkSVGLengthValue    = SkSVGWrapperValue<SkSVGLength, SkSVGValue::Type::kLength   >;
-using SkSVGPathValue      = SkSVGWrapperValue<SkPath     , SkSVGValue::Type::kPath     >;
-using SkSVGTransformValue = SkSVGWrapperValue<SkMatrix   , SkSVGValue::Type::kTransform>;
+using SkSVGColorValue     = SkSVGWrapperValue<SkSVGColorType  , SkSVGValue::Type::kColor    >;
+using SkSVGLengthValue    = SkSVGWrapperValue<SkSVGLength     , SkSVGValue::Type::kLength   >;
+using SkSVGPathValue      = SkSVGWrapperValue<SkPath          , SkSVGValue::Type::kPath     >;
+using SkSVGTransformValue = SkSVGWrapperValue<SkMatrix        , SkSVGValue::Type::kTransform>;
+using SkSVGViewBoxValue   = SkSVGWrapperValue<SkSVGViewBoxType, SkSVGValue::Type::kViewBox  >;
 
 #endif // SkSVGValue_DEFINED