[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
This commit is contained in:
fmalita 2016-08-11 09:16:29 -07:00 committed by Commit bot
parent 90b5cc31f3
commit 2d961e086b
11 changed files with 467 additions and 127 deletions

View File

@ -6,29 +6,18 @@
*/
#include "SkSVGAttribute.h"
#include "SkSVGRenderContext.h"
SkSVGPresentationAttributes::SkSVGPresentationAttributes()
: fFillIsSet(false)
, fStrokeIsSet(false) { }
SkSVGPresentationAttributes SkSVGPresentationAttributes::MakeInitial() {
SkSVGPresentationAttributes result;
void SkSVGPresentationAttributes::setFill(const SkSVGColorType& c) {
fFill = c;
fFillIsSet = true;
}
void SkSVGPresentationAttributes::setStroke(const SkSVGColorType& c) {
fStroke = c;
fStrokeIsSet = true;
}
void SkSVGPresentationAttributes::applyTo(SkSVGRenderContext* ctx) const {
if (fFillIsSet) {
ctx->writablePresentationContext()->setFillColor(fFill);
}
if (fStrokeIsSet) {
ctx->writablePresentationContext()->setStrokeColor(fStroke);
}
result.fFill.set(SkSVGPaint(SkSVGColorType(SK_ColorBLACK)));
result.fFillOpacity.set(SkSVGNumberType(1));
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));
return result;
}

View File

@ -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

View File

@ -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();
}

View File

@ -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

View File

@ -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[] = {

View File

@ -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:

View File

@ -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);
}
SkSVGPresentationContext& SkSVGPresentationContext::operator=(const SkSVGPresentationContext& o) {
this->initFrom(o);
return *this;
}
void SkSVGPresentationContext::initFrom(const SkSVGPresentationContext& other) {
if (other.fFill.isValid()) {
fFill.set(*other.fFill.get());
} else {
fFill.reset();
}
if (other.fStroke.isValid()) {
fStroke.set(*other.fStroke.get());
} else {
fStroke.reset();
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;
}
}
SkPaint& SkSVGPresentationContext::ensureFill() {
if (!fFill.isValid()) {
fFill.init();
fFill.get()->setStyle(SkPaint::kFill_Style);
fFill.get()->setAntiAlias(true);
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;
}
return *fFill.get();
}
SkPaint& SkSVGPresentationContext::ensureStroke() {
if (!fStroke.isValid()) {
fStroke.init();
fStroke.get()->setStyle(SkPaint::kStroke_Style);
fStroke.get()->setAntiAlias(true);
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;
}
return *fStroke.get();
}
void SkSVGPresentationContext::setFillColor(SkColor color) {
this->ensureFill().setColor(color);
// 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);
}
void SkSVGPresentationContext::setStrokeColor(SkColor color) {
this->ensureStroke().setColor(color);
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));
}
}
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));
}
}
template <>
void commitToPaint<SkSVGAttribute::kStrokeOpacity>(const SkSVGPresentationAttributes& attrs,
const SkSVGLengthContext&,
SkSVGPresentationContext* pctx) {
pctx->fStrokePaint.setAlpha(static_cast<uint8_t>(*attrs.fStrokeOpacity.get() * 255));
}
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;
}

View File

@ -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&);
SkSVGPresentationContext(const SkSVGPresentationContext&) = default;
SkSVGPresentationContext& operator=(const SkSVGPresentationContext&) = default;
const SkPaint* fillPaint() const { return fFill.getMaybeNull(); }
const SkPaint* strokePaint() const { return fStroke.getMaybeNull(); }
// Inherited presentation attributes, computed for the current node.
SkSVGPresentationAttributes fInherited;
void setFillColor(SkColor);
void setStrokeColor(SkColor);
private:
void initFrom(const SkSVGPresentationContext&);
SkPaint& ensureFill();
SkPaint& ensureStroke();
// 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;

View File

@ -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);
}
}

View File

@ -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

View File

@ -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