[svg] Implement feComposite, basic filter result storage

- Plumbing to store filter results by id and resolve them as inputs
  when referenced
- Added implementation of feComposite filter
- Added call to resolve input in feColorMatrix
- Bugfix to SkSVGFilterType operator==

The tests filters-color-01-b and filters-composite-03-b should now be
passing.

Bug: skia:10841
Change-Id: I2cd099c60ac21710f25184806c5cc537656b42af
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/332723
Commit-Queue: Tyler Denniston <tdenniston@google.com>
Reviewed-by: Florin Malita <fmalita@chromium.org>
This commit is contained in:
Tyler Denniston 2020-11-09 12:46:02 -05:00 committed by Skia Commit-Bot
parent 02dd0ed8ce
commit b25caae788
13 changed files with 276 additions and 20 deletions

View File

@ -18,11 +18,15 @@ public:
~SkSVGFe() override = default;
static bool IsFilterEffect(const sk_sp<SkSVGNode>& node) {
return node->tag() == SkSVGTag::kFeTurbulence || node->tag() == SkSVGTag::kFeColorMatrix;
return node->tag() == SkSVGTag::kFeTurbulence || node->tag() == SkSVGTag::kFeColorMatrix ||
node->tag() == SkSVGTag::kFeComposite;
}
sk_sp<SkImageFilter> makeImageFilter(const SkSVGRenderContext& ctx,
SkSVGFilterContext* fctx) const;
const SkSVGFilterContext& fctx) const;
SVG_ATTR(In, SkSVGFeInputType, SkSVGFeInputType(SkSVGFeInputType::Type::kSourceGraphic))
SVG_ATTR(Result, SkSVGStringType, SkSVGStringType())
protected:
explicit SkSVGFe(SkSVGTag t) : INHERITED(t) {}
@ -30,6 +34,8 @@ protected:
virtual sk_sp<SkImageFilter> onMakeImageFilter(const SkSVGRenderContext&,
const SkSVGFilterContext&) const = 0;
bool parseAndSetAttribute(const char*, const char*) override;
private:
using INHERITED = SkSVGHiddenContainer;
};

View File

@ -0,0 +1,43 @@
/*
* Copyright 2020 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef SkSVGFeComposite_DEFINED
#define SkSVGFeComposite_DEFINED
#include "include/core/SkBlendMode.h"
#include "modules/svg/include/SkSVGFe.h"
#include "modules/svg/include/SkSVGTypes.h"
class SkSVGFeComposite final : public SkSVGFe {
public:
~SkSVGFeComposite() override = default;
static sk_sp<SkSVGFeComposite> Make() {
return sk_sp<SkSVGFeComposite>(new SkSVGFeComposite());
}
SVG_ATTR(In2, SkSVGFeInputType, SkSVGFeInputType())
SVG_ATTR(K1, SkSVGNumberType, SkSVGNumberType(0))
SVG_ATTR(K2, SkSVGNumberType, SkSVGNumberType(0))
SVG_ATTR(K3, SkSVGNumberType, SkSVGNumberType(0))
SVG_ATTR(K4, SkSVGNumberType, SkSVGNumberType(0))
SVG_ATTR(Operator, SkSVGFeCompositeOperator, SkSVGFeCompositeOperator::kOver)
protected:
sk_sp<SkImageFilter> onMakeImageFilter(const SkSVGRenderContext&,
const SkSVGFilterContext&) const override;
bool parseAndSetAttribute(const char*, const char*) override;
private:
SkSVGFeComposite() : INHERITED(SkSVGTag::kFeComposite) {}
static SkBlendMode BlendModeForOperator(SkSVGFeCompositeOperator);
using INHERITED = SkSVGFe;
};
#endif // SkSVGFeComposite_DEFINED

View File

@ -10,23 +10,31 @@
#include "include/core/SkRect.h"
#include "include/core/SkRefCnt.h"
#include "include/core/SkString.h"
#include "include/private/SkTHash.h"
#include "modules/svg/include/SkSVGTypes.h"
class SkImageFilter;
class SkPicture;
class SkString;
class SkSVGFeInputType;
class SkSVGRenderContext;
class SkSVGFilterContext {
public:
SkSVGFilterContext(const SkRect& filterEffectsRegion)
: fFilterEffectsRegion(filterEffectsRegion) {}
sk_sp<SkImageFilter> findResultById(const SkString& id) const;
const SkRect& filterEffectsRegion() const { return fFilterEffectsRegion; }
void registerResult(const SkSVGStringType&, const sk_sp<SkImageFilter>&);
sk_sp<SkImageFilter> resolveInput(const SkSVGRenderContext&, const SkSVGFeInputType&) const;
private:
sk_sp<SkImageFilter> findResultById(const SkSVGStringType&) const;
SkRect fFilterEffectsRegion;
SkTHashMap<SkSVGStringType, sk_sp<SkImageFilter>> fResults;
};
#endif // SkSVGFilterContext_DEFINED

View File

@ -26,6 +26,7 @@ enum class SkSVGTag {
kDefs,
kEllipse,
kFeColorMatrix,
kFeComposite,
kFeTurbulence,
kFilter,
kG,

View File

@ -501,7 +501,9 @@ public:
explicit SkSVGFilterType(Type t) : fType(t) {}
explicit SkSVGFilterType(const SkString& iri) : fType(Type::kIRI), fIRI(iri) {}
bool operator==(const SkSVGFilterType& other) const { return fType == other.fType; }
bool operator==(const SkSVGFilterType& other) const {
return fType == other.fType && fIRI == other.fIRI;
}
bool operator!=(const SkSVGFilterType& other) const { return !(*this == other); }
const SkString& iri() const {
@ -516,6 +518,40 @@ private:
SkString fIRI;
};
class SkSVGFeInputType {
public:
enum class Type {
kSourceGraphic,
kSourceAlpha,
kBackgroundImage,
kBackgroundAlpha,
kFillPaint,
kStrokePaint,
kFilterPrimitiveReference,
};
SkSVGFeInputType() : fType(Type::kSourceGraphic) {}
explicit SkSVGFeInputType(Type t) : fType(t) {}
explicit SkSVGFeInputType(const SkSVGStringType& id)
: fType(Type::kFilterPrimitiveReference), fId(id) {}
bool operator==(const SkSVGFeInputType& other) const {
return fType == other.fType && fId == other.fId;
}
bool operator!=(const SkSVGFeInputType& other) const { return !(*this == other); }
const SkString& id() const {
SkASSERT(fType == Type::kFilterPrimitiveReference);
return fId;
}
Type type() const { return fType; }
private:
Type fType;
SkString fId;
};
enum class SkSVGFeColorMatrixType {
kMatrix,
kSaturate,
@ -525,6 +561,15 @@ enum class SkSVGFeColorMatrixType {
using SkSVGFeColorMatrixValues = SkTDArray<SkSVGNumberType>;
enum class SkSVGFeCompositeOperator {
kOver,
kIn,
kOut,
kAtop,
kXor,
kArithmetic,
};
class SkSVGFeTurbulenceBaseFrequency {
public:
SkSVGFeTurbulenceBaseFrequency() : fFreqX(0), fFreqY(0) {}

View File

@ -269,6 +269,16 @@ bool SkSVGAttributeParser::parseFuncIRI(SkSVGStringType* iri) {
}, iri);
}
template <>
bool SkSVGAttributeParser::parse(SkSVGStringType* result) {
if (this->parseEOSToken()) {
return false;
}
*result = SkSVGStringType(fCurPos);
fCurPos += result->size();
return this->parseEOSToken();
}
// https://www.w3.org/TR/SVG11/types.html#DataTypeNumber
bool SkSVGAttributeParser::parseNumber(SkSVGNumberType* number) {
// consume WS

View File

@ -16,6 +16,7 @@
#include "modules/svg/include/SkSVGDefs.h"
#include "modules/svg/include/SkSVGEllipse.h"
#include "modules/svg/include/SkSVGFeColorMatrix.h"
#include "modules/svg/include/SkSVGFeComposite.h"
#include "modules/svg/include/SkSVGFeTurbulence.h"
#include "modules/svg/include/SkSVGFilter.h"
#include "modules/svg/include/SkSVGG.h"
@ -333,6 +334,7 @@ SortedDictionaryEntry<sk_sp<SkSVGNode>(*)()> gTagFactories[] = {
{ "defs" , []() -> sk_sp<SkSVGNode> { return SkSVGDefs::Make(); }},
{ "ellipse" , []() -> sk_sp<SkSVGNode> { return SkSVGEllipse::Make(); }},
{ "feColorMatrix" , []() -> sk_sp<SkSVGNode> { return SkSVGFeColorMatrix::Make(); }},
{ "feComposite" , []() -> sk_sp<SkSVGNode> { return SkSVGFeComposite::Make(); }},
{ "feTurbulence" , []() -> sk_sp<SkSVGNode> { return SkSVGFeTurbulence::Make(); }},
{ "filter" , []() -> sk_sp<SkSVGNode> { return SkSVGFilter::Make(); }},
{ "g" , []() -> sk_sp<SkSVGNode> { return SkSVGG::Make(); }},

View File

@ -6,10 +6,40 @@
*/
#include "include/effects/SkImageFilters.h"
#include "modules/svg/include/SkSVGAttributeParser.h"
#include "modules/svg/include/SkSVGFe.h"
#include "modules/svg/include/SkSVGFilterContext.h"
sk_sp<SkImageFilter> SkSVGFe::makeImageFilter(const SkSVGRenderContext& ctx,
SkSVGFilterContext* fctx) const {
return this->onMakeImageFilter(ctx, *fctx);
const SkSVGFilterContext& fctx) const {
return this->onMakeImageFilter(ctx, fctx);
}
bool SkSVGFe::parseAndSetAttribute(const char* name, const char* value) {
return INHERITED::parseAndSetAttribute(name, value) ||
this->setIn(SkSVGAttributeParser::parse<SkSVGFeInputType>("in", name, value)) ||
this->setResult(SkSVGAttributeParser::parse<SkSVGStringType>("result", name, value));
}
template <> bool SkSVGAttributeParser::parse(SkSVGFeInputType* type) {
static constexpr std::tuple<const char*, SkSVGFeInputType::Type> gTypeMap[] = {
{"SourceGraphic", SkSVGFeInputType::Type::kSourceGraphic},
{"SourceAlpha", SkSVGFeInputType::Type::kSourceAlpha},
{"BackgroundImage", SkSVGFeInputType::Type::kBackgroundImage},
{"BackgroundAlpha", SkSVGFeInputType::Type::kBackgroundAlpha},
{"FillPaint", SkSVGFeInputType::Type::kFillPaint},
{"StrokePaint", SkSVGFeInputType::Type::kStrokePaint},
};
SkSVGStringType resultId;
SkSVGFeInputType::Type t;
bool parsedValue = false;
if (this->parseEnumMap(gTypeMap, &t)) {
*type = SkSVGFeInputType(t);
parsedValue = true;
} else if (parse(&resultId)) {
*type = SkSVGFeInputType(resultId);
parsedValue = true;
}
return parsedValue && this->parseEOSToken();
}

View File

@ -91,10 +91,9 @@ SkColorMatrix SkSVGFeColorMatrix::MakeLuminanceToAlpha() {
sk_sp<SkImageFilter> SkSVGFeColorMatrix::onMakeImageFilter(const SkSVGRenderContext& ctx,
const SkSVGFilterContext& fctx) const {
// TODO: "in" param should supply filter source
const sk_sp<SkImageFilter> input = nullptr;
return SkImageFilters::ColorFilter(
SkColorFilters::Matrix(makeMatrixForType()), input, fctx.filterEffectsRegion());
return SkImageFilters::ColorFilter(SkColorFilters::Matrix(makeMatrixForType()),
fctx.resolveInput(ctx, this->getIn()),
fctx.filterEffectsRegion());
}
template <> bool SkSVGAttributeParser::parse(SkSVGFeColorMatrixValues* values) {

View File

@ -0,0 +1,73 @@
/*
* Copyright 2020 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "include/effects/SkImageFilters.h"
#include "modules/svg/include/SkSVGAttributeParser.h"
#include "modules/svg/include/SkSVGFeComposite.h"
#include "modules/svg/include/SkSVGFilterContext.h"
#include "modules/svg/include/SkSVGRenderContext.h"
bool SkSVGFeComposite::parseAndSetAttribute(const char* name, const char* value) {
return INHERITED::parseAndSetAttribute(name, value) ||
// SkSVGFeInputType parsing defined in SkSVGFe.cpp:
this->setIn2(SkSVGAttributeParser::parse<SkSVGFeInputType>("in2", name, value)) ||
this->setK1(SkSVGAttributeParser::parse<SkSVGNumberType>("k1", name, value)) ||
this->setK2(SkSVGAttributeParser::parse<SkSVGNumberType>("k2", name, value)) ||
this->setK3(SkSVGAttributeParser::parse<SkSVGNumberType>("k3", name, value)) ||
this->setK4(SkSVGAttributeParser::parse<SkSVGNumberType>("k4", name, value)) ||
this->setOperator(
SkSVGAttributeParser::parse<SkSVGFeCompositeOperator>("operator", name, value));
}
SkBlendMode SkSVGFeComposite::BlendModeForOperator(SkSVGFeCompositeOperator op) {
switch (op) {
case SkSVGFeCompositeOperator::kOver:
return SkBlendMode::kSrcOver;
case SkSVGFeCompositeOperator::kIn:
return SkBlendMode::kSrcIn;
case SkSVGFeCompositeOperator::kOut:
return SkBlendMode::kSrcOut;
case SkSVGFeCompositeOperator::kAtop:
return SkBlendMode::kSrcATop;
case SkSVGFeCompositeOperator::kXor:
return SkBlendMode::kXor;
case SkSVGFeCompositeOperator::kArithmetic:
// Arithmetic is not handled with a blend
SkASSERT(false);
return SkBlendMode::kSrcOver;
}
SkUNREACHABLE;
}
sk_sp<SkImageFilter> SkSVGFeComposite::onMakeImageFilter(const SkSVGRenderContext& ctx,
const SkSVGFilterContext& fctx) const {
const SkRect cropRect = fctx.filterEffectsRegion();
const sk_sp<SkImageFilter> background = fctx.resolveInput(ctx, fIn2);
const sk_sp<SkImageFilter> foreground = fctx.resolveInput(ctx, this->getIn());
if (fOperator == SkSVGFeCompositeOperator::kArithmetic) {
constexpr bool enforcePMColor = true;
return SkImageFilters::Arithmetic(
fK1, fK2, fK3, fK4, enforcePMColor, background, foreground, cropRect);
} else {
return SkImageFilters::Blend(
BlendModeForOperator(fOperator), background, foreground, cropRect);
}
}
template <> bool SkSVGAttributeParser::parse(SkSVGFeCompositeOperator* op) {
static constexpr std::tuple<const char*, SkSVGFeCompositeOperator> gOpMap[] = {
{"over", SkSVGFeCompositeOperator::kOver},
{"in", SkSVGFeCompositeOperator::kIn},
{"out", SkSVGFeCompositeOperator::kOut},
{"atop", SkSVGFeCompositeOperator::kAtop},
{"xor", SkSVGFeCompositeOperator::kXor},
{"arithmetic", SkSVGFeCompositeOperator::kArithmetic},
};
return this->parseEnumMap(gOpMap, op) && this->parseEOSToken();
}

View File

@ -72,10 +72,13 @@ sk_sp<SkImageFilter> SkSVGFilter::buildFilterDAG(const SkSVGRenderContext& ctx)
}
const auto& feNode = static_cast<const SkSVGFe&>(*child);
sk_sp<SkImageFilter> imageFilter = feNode.makeImageFilter(ctx, &fctx);
if (imageFilter) {
// TODO: there are specific composition rules that need to be followed
filter = SkImageFilters::Compose(imageFilter, filter);
const auto& feResultType = feNode.getResult();
// TODO: there are specific composition rules that need to be followed
filter = feNode.makeImageFilter(ctx, fctx);
if (!feResultType.isEmpty()) {
fctx.registerResult(feResultType, filter);
}
}

View File

@ -7,7 +7,41 @@
#include "include/effects/SkImageFilters.h"
#include "modules/svg/include/SkSVGFilterContext.h"
#include "modules/svg/include/SkSVGNode.h"
#include "modules/svg/include/SkSVGRenderContext.h"
#include "modules/svg/include/SkSVGTypes.h"
sk_sp<SkImageFilter> SkSVGFilterContext::findResultById(const SkString& id) const {
return nullptr;
sk_sp<SkImageFilter> SkSVGFilterContext::findResultById(const SkSVGStringType& id) const {
const sk_sp<SkImageFilter>* res = fResults.find(id);
return res ? *res : nullptr;
}
void SkSVGFilterContext::registerResult(const SkSVGStringType& id,
const sk_sp<SkImageFilter>& result) {
SkASSERT(!id.isEmpty());
fResults[id] = result;
}
sk_sp<SkImageFilter> SkSVGFilterContext::resolveInput(const SkSVGRenderContext& ctx,
const SkSVGFeInputType& inputType) const {
switch (inputType.type()) {
case SkSVGFeInputType::Type::kSourceGraphic:
return nullptr;
case SkSVGFeInputType::Type::kFillPaint:
return SkImageFilters::Paint(*ctx.fillPaint());
case SkSVGFeInputType::Type::kStrokePaint: {
// The paint filter doesn't handle stroke paints properly, so convert to fill for
// simplicity.
// TODO: Paint filter is deprecated, but the replacement (SkShaders::*())
// requires some extra work to handle all paint features (gradients, etc).
SkPaint p = *ctx.strokePaint();
p.setStyle(SkPaint::kFill_Style);
return SkImageFilters::Paint(p);
}
case SkSVGFeInputType::Type::kFilterPrimitiveReference:
return findResultById(inputType.id());
default:
SkDebugf("unhandled filter input type %d\n", inputType.type());
return nullptr;
}
}

View File

@ -18,6 +18,7 @@ skia_svg_public = [
"$_include/SkSVGEllipse.h",
"$_include/SkSVGFe.h",
"$_include/SkSVGFeColorMatrix.h",
"$_include/SkSVGFeComposite.h",
"$_include/SkSVGFeTurbulence.h",
"$_include/SkSVGFilter.h",
"$_include/SkSVGFilterContext.h",
@ -54,6 +55,7 @@ skia_svg_sources = [
"$_src/SkSVGEllipse.cpp",
"$_src/SkSVGFe.cpp",
"$_src/SkSVGFeColorMatrix.cpp",
"$_src/SkSVGFeComposite.cpp",
"$_src/SkSVGFeTurbulence.cpp",
"$_src/SkSVGFilter.cpp",
"$_src/SkSVGFilterContext.cpp",