[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:
parent
98e17bf01a
commit
75c38f94ef
@ -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
|
||||
|
@ -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); \
|
||||
} \
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user