[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:
parent
02dd0ed8ce
commit
b25caae788
@ -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;
|
||||
};
|
||||
|
43
modules/svg/include/SkSVGFeComposite.h
Normal file
43
modules/svg/include/SkSVGFeComposite.h
Normal 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
|
@ -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
|
||||
|
@ -26,6 +26,7 @@ enum class SkSVGTag {
|
||||
kDefs,
|
||||
kEllipse,
|
||||
kFeColorMatrix,
|
||||
kFeComposite,
|
||||
kFeTurbulence,
|
||||
kFilter,
|
||||
kG,
|
||||
|
@ -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) {}
|
||||
|
@ -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
|
||||
|
@ -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(); }},
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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) {
|
||||
|
73
modules/svg/src/SkSVGFeComposite.cpp
Normal file
73
modules/svg/src/SkSVGFeComposite.cpp
Normal 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();
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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",
|
||||
|
Loading…
Reference in New Issue
Block a user