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