| Index: experimental/svg/model/SkSVGRenderContext.cpp
|
| diff --git a/experimental/svg/model/SkSVGRenderContext.cpp b/experimental/svg/model/SkSVGRenderContext.cpp
|
| index e902d4ecf3d389018535e941299ef989c1e7bd42..ee92c47d542ea39744f4487e4007f8a5ed916a7c 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;
|
| +}
|
|
|