[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:
parent
90b5cc31f3
commit
2d961e086b
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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[] = {
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user