1 /*
2  * Copyright 2016 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "include/core/SkCanvas.h"
9 #include "include/core/SkMatrix.h"
10 #include "include/pathops/SkPathOps.h"
11 #include "include/private/SkTPin.h"
12 #include "modules/svg/include/SkSVGNode.h"
13 #include "modules/svg/include/SkSVGRenderContext.h"
14 #include "modules/svg/include/SkSVGValue.h"
15 #include "src/core/SkTLazy.h"
16 
SkSVGNode(SkSVGTag t)17 SkSVGNode::SkSVGNode(SkSVGTag t) : fTag(t) {
18     // Uninherited presentation attributes need a non-null default value.
19     fPresentationAttributes.fStopColor.set(SkSVGColor(SK_ColorBLACK));
20     fPresentationAttributes.fStopOpacity.set(SkSVGNumberType(1.0f));
21     fPresentationAttributes.fFloodColor.set(SkSVGColor(SK_ColorBLACK));
22     fPresentationAttributes.fFloodOpacity.set(SkSVGNumberType(1.0f));
23     fPresentationAttributes.fLightingColor.set(SkSVGColor(SK_ColorWHITE));
24 }
25 
~SkSVGNode()26 SkSVGNode::~SkSVGNode() { }
27 
render(const SkSVGRenderContext & ctx) const28 void SkSVGNode::render(const SkSVGRenderContext& ctx) const {
29     SkSVGRenderContext localContext(ctx, this);
30 
31     if (this->onPrepareToRender(&localContext)) {
32         this->onRender(localContext);
33     }
34 }
35 
asPaint(const SkSVGRenderContext & ctx,SkPaint * paint) const36 bool SkSVGNode::asPaint(const SkSVGRenderContext& ctx, SkPaint* paint) const {
37     SkSVGRenderContext localContext(ctx);
38 
39     return this->onPrepareToRender(&localContext) && this->onAsPaint(localContext, paint);
40 }
41 
asPath(const SkSVGRenderContext & ctx) const42 SkPath SkSVGNode::asPath(const SkSVGRenderContext& ctx) const {
43     SkSVGRenderContext localContext(ctx);
44     if (!this->onPrepareToRender(&localContext)) {
45         return SkPath();
46     }
47 
48     SkPath path = this->onAsPath(localContext);
49 
50     if (const auto* clipPath = localContext.clipPath()) {
51         // There is a clip-path present on the current node.
52         Op(path, *clipPath, kIntersect_SkPathOp, &path);
53     }
54 
55     return path;
56 }
57 
objectBoundingBox(const SkSVGRenderContext & ctx) const58 SkRect SkSVGNode::objectBoundingBox(const SkSVGRenderContext& ctx) const {
59     return this->onObjectBoundingBox(ctx);
60 }
61 
onPrepareToRender(SkSVGRenderContext * ctx) const62 bool SkSVGNode::onPrepareToRender(SkSVGRenderContext* ctx) const {
63     ctx->applyPresentationAttributes(fPresentationAttributes,
64                                      this->hasChildren() ? 0 : SkSVGRenderContext::kLeaf);
65 
66     // visibility:hidden and display:none disable rendering.
67     // TODO: if display is not a value (true when display="inherit"), we currently
68     //   ignore it. Eventually we should be able to add SkASSERT(display.isValue()).
69     const auto visibility = ctx->presentationContext().fInherited.fVisibility->type();
70     const auto display = fPresentationAttributes.fDisplay;  // display is uninherited
71     return visibility != SkSVGVisibility::Type::kHidden &&
72            (!display.isValue() || *display != SkSVGDisplay::kNone);
73 }
74 
setAttribute(SkSVGAttribute attr,const SkSVGValue & v)75 void SkSVGNode::setAttribute(SkSVGAttribute attr, const SkSVGValue& v) {
76     this->onSetAttribute(attr, v);
77 }
78 
79 template <typename T>
SetInheritedByDefault(SkTLazy<T> & presentation_attribute,const T & value)80 void SetInheritedByDefault(SkTLazy<T>& presentation_attribute, const T& value) {
81     if (value.type() != T::Type::kInherit) {
82         presentation_attribute.set(value);
83     } else {
84         // kInherited values are semantically equivalent to
85         // the absence of a local presentation attribute.
86         presentation_attribute.reset();
87     }
88 }
89 
parseAndSetAttribute(const char * n,const char * v)90 bool SkSVGNode::parseAndSetAttribute(const char* n, const char* v) {
91 #define PARSE_AND_SET(svgName, attrName)                                                        \
92     this->set##attrName(                                                                        \
93             SkSVGAttributeParser::parseProperty<decltype(fPresentationAttributes.f##attrName)>( \
94                     svgName, n, v))
95 
96     return PARSE_AND_SET(   "clip-path"                  , ClipPath)
97            || PARSE_AND_SET("clip-rule"                  , ClipRule)
98            || PARSE_AND_SET("color"                      , Color)
99            || PARSE_AND_SET("color-interpolation"        , ColorInterpolation)
100            || PARSE_AND_SET("color-interpolation-filters", ColorInterpolationFilters)
101            || PARSE_AND_SET("display"                    , Display)
102            || PARSE_AND_SET("fill"                       , Fill)
103            || PARSE_AND_SET("fill-opacity"               , FillOpacity)
104            || PARSE_AND_SET("fill-rule"                  , FillRule)
105            || PARSE_AND_SET("filter"                     , Filter)
106            || PARSE_AND_SET("flood-color"                , FloodColor)
107            || PARSE_AND_SET("flood-opacity"              , FloodOpacity)
108            || PARSE_AND_SET("font-family"                , FontFamily)
109            || PARSE_AND_SET("font-size"                  , FontSize)
110            || PARSE_AND_SET("font-style"                 , FontStyle)
111            || PARSE_AND_SET("font-weight"                , FontWeight)
112            || PARSE_AND_SET("lighting-color"             , LightingColor)
113            || PARSE_AND_SET("mask"                       , Mask)
114            || PARSE_AND_SET("opacity"                    , Opacity)
115            || PARSE_AND_SET("stop-color"                 , StopColor)
116            || PARSE_AND_SET("stop-opacity"               , StopOpacity)
117            || PARSE_AND_SET("stroke"                     , Stroke)
118            || PARSE_AND_SET("stroke-dasharray"           , StrokeDashArray)
119            || PARSE_AND_SET("stroke-dashoffset"          , StrokeDashOffset)
120            || PARSE_AND_SET("stroke-linecap"             , StrokeLineCap)
121            || PARSE_AND_SET("stroke-linejoin"            , StrokeLineJoin)
122            || PARSE_AND_SET("stroke-miterlimit"          , StrokeMiterLimit)
123            || PARSE_AND_SET("stroke-opacity"             , StrokeOpacity)
124            || PARSE_AND_SET("stroke-width"               , StrokeWidth)
125            || PARSE_AND_SET("text-anchor"                , TextAnchor)
126            || PARSE_AND_SET("visibility"                 , Visibility);
127 
128 #undef PARSE_AND_SET
129 }
130 
131 // https://www.w3.org/TR/SVG11/coords.html#PreserveAspectRatioAttribute
ComputeViewboxMatrix(const SkRect & viewBox,const SkRect & viewPort,SkSVGPreserveAspectRatio par)132 SkMatrix SkSVGNode::ComputeViewboxMatrix(const SkRect& viewBox,
133                                          const SkRect& viewPort,
134                                          SkSVGPreserveAspectRatio par) {
135     SkASSERT(!viewBox.isEmpty());
136     SkASSERT(!viewPort.isEmpty());
137 
138     auto compute_scale = [&]() -> SkV2 {
139         const auto sx = viewPort.width()  / viewBox.width(),
140                    sy = viewPort.height() / viewBox.height();
141 
142         if (par.fAlign == SkSVGPreserveAspectRatio::kNone) {
143             // none -> anisotropic scaling, regardless of fScale
144             return {sx, sy};
145         }
146 
147         // isotropic scaling
148         const auto s = par.fScale == SkSVGPreserveAspectRatio::kMeet
149                             ? std::min(sx, sy)
150                             : std::max(sx, sy);
151         return {s, s};
152     };
153 
154     auto compute_trans = [&](const SkV2& scale) -> SkV2 {
155         static constexpr float gAlignCoeffs[] = {
156                 0.0f, // Min
157                 0.5f, // Mid
158                 1.0f  // Max
159         };
160 
161         const size_t x_coeff = par.fAlign >> 0 & 0x03,
162                      y_coeff = par.fAlign >> 2 & 0x03;
163 
164         SkASSERT(x_coeff < SK_ARRAY_COUNT(gAlignCoeffs) &&
165                  y_coeff < SK_ARRAY_COUNT(gAlignCoeffs));
166 
167         const auto tx = -viewBox.x() * scale.x,
168                    ty = -viewBox.y() * scale.y,
169                    dx = viewPort.width()  - viewBox.width() * scale.x,
170                    dy = viewPort.height() - viewBox.height() * scale.y;
171 
172         return {
173             tx + dx * gAlignCoeffs[x_coeff],
174             ty + dy * gAlignCoeffs[y_coeff]
175         };
176     };
177 
178     const auto s = compute_scale(),
179                t = compute_trans(s);
180 
181     return SkMatrix::Translate(t.x, t.y) *
182            SkMatrix::Scale(s.x, s.y);
183 }
184