[svg] Add SkSVGProperty class for presentation attributes

This CL adds a new SkSVGProperty<T,B> class and uses it instead of
SkTLazy for the presentation attributes. Ideally this will form the
foundation for improvements to our presentation attribute parsing
as well as correctness for inherited/non-inherited properties.

Change-Id: Ie1cdb3db9674c55376e127cc1a8b8cb303a1bd13
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/334837
Commit-Queue: Tyler Denniston <tdenniston@google.com>
Reviewed-by: Florin Malita <fmalita@chromium.org>
This commit is contained in:
Tyler Denniston 2020-11-17 12:26:25 -05:00 committed by Skia Commit-Bot
parent 98e17bf01a
commit 75c38f94ef
4 changed files with 106 additions and 46 deletions

View File

@ -72,38 +72,38 @@ enum class SkSVGAttribute {
struct SkSVGPresentationAttributes {
static SkSVGPresentationAttributes MakeInitial();
// TODO: SkTLazy adds an extra ptr per attribute; refactor to reduce overhead.
// TODO: SkSVGProperty adds an extra ptr per attribute; refactor to reduce overhead.
SkTLazy<SkSVGPaint> fFill;
SkTLazy<SkSVGNumberType> fFillOpacity;
SkTLazy<SkSVGFillRule> fFillRule;
SkTLazy<SkSVGFillRule> fClipRule;
SkSVGProperty<SkSVGPaint , true> fFill;
SkSVGProperty<SkSVGNumberType, true> fFillOpacity;
SkSVGProperty<SkSVGFillRule , true> fFillRule;
SkSVGProperty<SkSVGFillRule , true> fClipRule;
SkTLazy<SkSVGPaint> fStroke;
SkTLazy<SkSVGDashArray> fStrokeDashArray;
SkTLazy<SkSVGLength> fStrokeDashOffset;
SkTLazy<SkSVGLineCap> fStrokeLineCap;
SkTLazy<SkSVGLineJoin> fStrokeLineJoin;
SkTLazy<SkSVGNumberType> fStrokeMiterLimit;
SkTLazy<SkSVGNumberType> fStrokeOpacity;
SkTLazy<SkSVGLength> fStrokeWidth;
SkSVGProperty<SkSVGPaint , true> fStroke;
SkSVGProperty<SkSVGDashArray , true> fStrokeDashArray;
SkSVGProperty<SkSVGLength , true> fStrokeDashOffset;
SkSVGProperty<SkSVGLineCap , true> fStrokeLineCap;
SkSVGProperty<SkSVGLineJoin , true> fStrokeLineJoin;
SkSVGProperty<SkSVGNumberType, true> fStrokeMiterLimit;
SkSVGProperty<SkSVGNumberType, true> fStrokeOpacity;
SkSVGProperty<SkSVGLength , true> fStrokeWidth;
SkTLazy<SkSVGVisibility> fVisibility;
SkSVGProperty<SkSVGVisibility, true> fVisibility;
SkTLazy<SkSVGColorType> fColor;
SkSVGProperty<SkSVGColorType , true> fColor;
SkTLazy<SkSVGFontFamily> fFontFamily;
SkTLazy<SkSVGFontStyle> fFontStyle;
SkTLazy<SkSVGFontSize> fFontSize;
SkTLazy<SkSVGFontWeight> fFontWeight;
SkTLazy<SkSVGTextAnchor> fTextAnchor;
SkSVGProperty<SkSVGFontFamily, true> fFontFamily;
SkSVGProperty<SkSVGFontStyle , true> fFontStyle;
SkSVGProperty<SkSVGFontSize , true> fFontSize;
SkSVGProperty<SkSVGFontWeight, true> fFontWeight;
SkSVGProperty<SkSVGTextAnchor, true> fTextAnchor;
// TODO(tdenniston): add SkSVGStopColor
// uninherited
SkTLazy<SkSVGNumberType> fOpacity;
SkTLazy<SkSVGClip> fClipPath;
SkTLazy<SkSVGFilterType> fFilter;
SkSVGProperty<SkSVGNumberType, false> fOpacity;
SkSVGProperty<SkSVGClip , false> fClipPath;
SkSVGProperty<SkSVGFilterType, false> fFilter;
};
#endif // SkSVGAttribute_DEFINED

View File

@ -55,21 +55,21 @@ public: \
return fPresentationAttributes.f##attr_name.getMaybeNull(); \
} \
void set##attr_name(const attr_type& v) { \
if (!attr_inherited || v.type() != attr_type::Type::kInherit) { \
fPresentationAttributes.f##attr_name.set(v); \
auto* dest = &fPresentationAttributes.f##attr_name; \
if (!dest->isInheritable() || \
v.type() != attr_type::Type::kInherit) { \
dest->set(v); \
} else { \
/* kInherited values are semantically equivalent to \
the absence of a local presentation attribute.*/ \
fPresentationAttributes.f##attr_name.reset(); \
dest->set(SkSVGPropertyState::kInherit); \
} \
} \
void set##attr_name(attr_type&& v) { \
if (!attr_inherited || v.type() != attr_type::Type::kInherit) { \
fPresentationAttributes.f##attr_name.set(std::move(v)); \
auto* dest = &fPresentationAttributes.f##attr_name; \
if (!dest->isInheritable() || \
v.type() != attr_type::Type::kInherit) { \
dest->set(std::move(v)); \
} else { \
/* kInherited values are semantically equivalent to \
the absence of a local presentation attribute.*/ \
fPresentationAttributes.f##attr_name.reset(); \
dest->set(SkSVGPropertyState::kInherit); \
} \
}

View File

@ -17,6 +17,7 @@
#include "include/core/SkString.h"
#include "include/core/SkTypes.h"
#include "include/private/SkTDArray.h"
#include "src/core/SkTLazy.h"
using SkSVGColorType = SkColor;
using SkSVGIntegerType = int;
@ -26,6 +27,65 @@ using SkSVGViewBoxType = SkRect;
using SkSVGTransformType = SkMatrix;
using SkSVGPointsType = SkTDArray<SkPoint>;
enum class SkSVGPropertyState {
kUnspecified,
kInherit,
kValue,
};
// https://www.w3.org/TR/SVG11/intro.html#TermProperty
template <typename T, bool kInheritable> class SkSVGProperty {
public:
SkSVGProperty() : fState(SkSVGPropertyState::kUnspecified) {}
template <typename... Args>
void init(Args&&... args) {
fState = SkSVGPropertyState::kValue;
fValue.init(std::forward<Args>(args)...);
}
constexpr bool isInheritable() const { return kInheritable; }
bool isValue() const { return fState == SkSVGPropertyState::kValue; }
T* getMaybeNull() const {
return fValue.getMaybeNull();
}
void set(SkSVGPropertyState state) {
fState = state;
if (fState != SkSVGPropertyState::kValue) {
fValue.reset();
}
}
void set(const T& value) {
fState = SkSVGPropertyState::kValue;
fValue.set(value);
}
void set(T&& value) {
fState = SkSVGPropertyState::kValue;
fValue.set(std::move(value));
}
T* operator->() const {
SkASSERT(fState == SkSVGPropertyState::kValue);
SkASSERT(fValue.isValid());
return fValue.get();
}
T& operator*() const {
SkASSERT(fState == SkSVGPropertyState::kValue);
SkASSERT(fValue.isValid());
return *fValue;
}
private:
SkSVGPropertyState fState;
SkTLazy<T> fValue;
};
class SkSVGLength {
public:
enum class Unit {

View File

@ -168,7 +168,7 @@ template <>
void commitToPaint<SkSVGAttribute::kStrokeDashArray>(const SkSVGPresentationAttributes& attrs,
const SkSVGRenderContext& ctx,
SkSVGPresentationContext* pctx) {
const auto& dashArray = attrs.fStrokeDashArray.get();
const auto& dashArray = attrs.fStrokeDashArray.getMaybeNull();
SkASSERT(dashArray->type() != SkSVGDashArray::Type::kInherit);
if (dashArray->type() != SkSVGDashArray::Type::kDashArray) {
@ -391,13 +391,13 @@ void SkSVGRenderContext::applyPresentationAttributes(const SkSVGPresentationAttr
#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()) { \
SkASSERT(fPresentationContext->fInherited.f ## ATTR.isValue()); \
const auto& attr = attrs.f ## ATTR; \
if (attr.isValue() && *attr != *fPresentationContext->fInherited.f ## ATTR) { \
/* Update the local attribute value */ \
fPresentationContext.writable()->fInherited.f ## ATTR.set(*value); \
fPresentationContext.writable()->fInherited.f ## ATTR.set(*attr); \
/* Update the cached paints */ \
commitToPaint<SkSVGAttribute::k ## ATTR>(attrs, *this, \
commitToPaint<SkSVGAttribute::k ## ATTR>(attrs, *this, \
fPresentationContext.writable()); \
} \
} while (false)
@ -423,7 +423,7 @@ void SkSVGRenderContext::applyPresentationAttributes(const SkSVGPresentationAttr
ApplyLazyInheritedAttribute(Color);
// Local 'color' attribute: update paints for attributes that are set to 'currentColor'.
if (attrs.fColor.isValid()) {
if (attrs.fColor.isValue()) {
updatePaintsWithCurrentColor(attrs);
}
@ -431,17 +431,17 @@ void SkSVGRenderContext::applyPresentationAttributes(const SkSVGPresentationAttr
// Uninherited attributes. Only apply to the current context.
if (auto* opacity = attrs.fOpacity.getMaybeNull()) {
this->applyOpacity(*opacity, flags);
if (attrs.fOpacity.isValue()) {
this->applyOpacity(*attrs.fOpacity, flags);
}
if (auto* clip = attrs.fClipPath.getMaybeNull()) {
this->applyClip(*clip);
if (attrs.fClipPath.isValue()) {
this->applyClip(*attrs.fClipPath);
}
// TODO: when both a filter and opacity are present, we can apply both with a single layer
if (auto* filter = attrs.fFilter.getMaybeNull()) {
this->applyFilter(*filter);
if (attrs.fFilter.isValue()) {
this->applyFilter(*attrs.fFilter);
}
}