Reland "[svg] Perform colorspace conversions for filter effects"

This reverts commit 36acb7b10c.

Reason for revert: artifacts in 1010102 are expected

Original change's description:
> Revert "[svg] Perform colorspace conversions for filter effects"
>
> This reverts commit a0880eda22.
>
> Reason for revert: visual artifacts in 10-bit color depth (10-10-10-2)
>
> Original change's description:
> > [svg] Perform colorspace conversions for filter effects
> >
> > A filter effect can optionally be specified to operate in either sRGB
> > or linearRGB, according to the SVG spec:
> >
> > https://www.w3.org/TR/SVG11/painting.html#ColorInterpolationProperties
> >
> > This CL adds any necessary conversion steps (SkColorFilters) while
> > constructing the filter DAG. The default filter effect color space is
> > linearRGB. We should now be passing the filters-gauss-* W3C tests.
> >
> > Specific changes:
> > - Tag filter effect results with their colorspace when storing them in
> >   the filter context map
> > - Add an SkColorFolor conversion step as necessary when resolving filter
> >   effect inputs
> >
> > Bug: skia:10841
> > Change-Id: Ide12698ea64c4d40f09df93a60718788809086fa
> > Reviewed-on: https://skia-review.googlesource.com/c/skia/+/353078
> > Commit-Queue: Tyler Denniston <tdenniston@google.com>
> > Reviewed-by: Florin Malita <fmalita@chromium.org>
>
> TBR=fmalita@chromium.org,tdenniston@google.com
>
> Change-Id: Id4a33c49643039cfb2d2867a1513e8ee1d7b181a
> No-Presubmit: true
> No-Tree-Checks: true
> No-Try: true
> Bug: skia:10841
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/353630
> Reviewed-by: John Stiles <johnstiles@google.com>
> Commit-Queue: John Stiles <johnstiles@google.com>

TBR=fmalita@chromium.org,tdenniston@google.com,johnstiles@google.com

# Not skipping CQ checks because this is a reland.

Bug: skia:10841
Change-Id: Id6d9e01d9b18ebfb6f9a6cb74518ad5cd73ea00a
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/353777
Reviewed-by: Tyler Denniston <tdenniston@google.com>
Commit-Queue: Tyler Denniston <tdenniston@google.com>
This commit is contained in:
Tyler Denniston 2021-01-14 14:23:13 +00:00 committed by Skia Commit-Bot
parent bf562de155
commit 8f78d55284
7 changed files with 69 additions and 24 deletions

View File

@ -30,17 +30,18 @@ public:
const SkSVGObjectBoundingBoxUnits& primitiveUnits() const { return fPrimitiveUnits; } const SkSVGObjectBoundingBoxUnits& primitiveUnits() const { return fPrimitiveUnits; }
void registerResult(const SkSVGStringType&, const sk_sp<SkImageFilter>&, const SkRect&); void registerResult(const SkSVGStringType&, const sk_sp<SkImageFilter>&, const SkRect&, SkSVGColorspace);
sk_sp<SkImageFilter> resolveInput(const SkSVGRenderContext&, const SkSVGFeInputType&) const; sk_sp<SkImageFilter> resolveInput(const SkSVGRenderContext&, const SkSVGFeInputType&, SkSVGColorspace) const;
private: private:
struct Result { struct Result {
sk_sp<SkImageFilter> fImageFilter; sk_sp<SkImageFilter> fImageFilter;
SkRect fFilterSubregion; SkRect fFilterSubregion;
SkSVGColorspace fColorspace;
}; };
sk_sp<SkImageFilter> findResultById(const SkSVGStringType&) const; const Result* findResultById(const SkSVGStringType&) const;
SkRect fFilterEffectsRegion; SkRect fFilterEffectsRegion;

View File

@ -84,7 +84,9 @@ SkRect SkSVGFe::resolveFilterSubregion(const SkSVGRenderContext& ctx,
} }
SkSVGColorspace SkSVGFe::resolveColorspace(const SkSVGRenderContext& ctx) const { SkSVGColorspace SkSVGFe::resolveColorspace(const SkSVGRenderContext& ctx) const {
return *ctx.presentationContext().fInherited.fColorInterpolationFilters; constexpr SkSVGColorspace kDefaultCS = SkSVGColorspace::kSRGB;
const SkSVGColorspace cs = *ctx.presentationContext().fInherited.fColorInterpolationFilters;
return cs == SkSVGColorspace::kAuto ? kDefaultCS : cs;
} }
void SkSVGFe::applyProperties(SkSVGRenderContext* ctx) const { this->onPrepareToRender(ctx); } void SkSVGFe::applyProperties(SkSVGRenderContext* ctx) const { this->onPrepareToRender(ctx); }

View File

@ -91,9 +91,10 @@ SkColorMatrix SkSVGFeColorMatrix::MakeLuminanceToAlpha() {
sk_sp<SkImageFilter> SkSVGFeColorMatrix::onMakeImageFilter(const SkSVGRenderContext& ctx, sk_sp<SkImageFilter> SkSVGFeColorMatrix::onMakeImageFilter(const SkSVGRenderContext& ctx,
const SkSVGFilterContext& fctx) const { const SkSVGFilterContext& fctx) const {
return SkImageFilters::ColorFilter(SkColorFilters::Matrix(makeMatrixForType()), return SkImageFilters::ColorFilter(
fctx.resolveInput(ctx, this->getIn()), SkColorFilters::Matrix(makeMatrixForType()),
this->resolveFilterSubregion(ctx, fctx)); fctx.resolveInput(ctx, this->getIn(), this->resolveColorspace(ctx)),
this->resolveFilterSubregion(ctx, fctx));
} }
template <> bool SkSVGAttributeParser::parse(SkSVGFeColorMatrixValues* values) { template <> bool SkSVGAttributeParser::parse(SkSVGFeColorMatrixValues* values) {

View File

@ -47,8 +47,9 @@ SkBlendMode SkSVGFeComposite::BlendModeForOperator(SkSVGFeCompositeOperator op)
sk_sp<SkImageFilter> SkSVGFeComposite::onMakeImageFilter(const SkSVGRenderContext& ctx, sk_sp<SkImageFilter> SkSVGFeComposite::onMakeImageFilter(const SkSVGRenderContext& ctx,
const SkSVGFilterContext& fctx) const { const SkSVGFilterContext& fctx) const {
const SkRect cropRect = this->resolveFilterSubregion(ctx, fctx); const SkRect cropRect = this->resolveFilterSubregion(ctx, fctx);
const sk_sp<SkImageFilter> background = fctx.resolveInput(ctx, fIn2); const SkSVGColorspace colorspace = this->resolveColorspace(ctx);
const sk_sp<SkImageFilter> foreground = fctx.resolveInput(ctx, this->getIn()); const sk_sp<SkImageFilter> background = fctx.resolveInput(ctx, fIn2, colorspace);
const sk_sp<SkImageFilter> foreground = fctx.resolveInput(ctx, this->getIn(), colorspace);
if (fOperator == SkSVGFeCompositeOperator::kArithmetic) { if (fOperator == SkSVGFeCompositeOperator::kArithmetic) {
constexpr bool enforcePMColor = true; constexpr bool enforcePMColor = true;
return SkImageFilters::Arithmetic( return SkImageFilters::Arithmetic(

View File

@ -30,7 +30,7 @@ sk_sp<SkImageFilter> SkSVGFeGaussianBlur::onMakeImageFilter(const SkSVGRenderCon
} }
return SkImageFilters::Blur(sigmaX, sigmaY, return SkImageFilters::Blur(sigmaX, sigmaY,
fctx.resolveInput(ctx, this->getIn()), fctx.resolveInput(ctx, this->getIn(), this->resolveColorspace(ctx)),
this->resolveFilterSubregion(ctx, fctx)); this->resolveFilterSubregion(ctx, fctx));
} }

View File

@ -5,6 +5,7 @@
* found in the LICENSE file. * found in the LICENSE file.
*/ */
#include "include/core/SkColorFilter.h"
#include "include/effects/SkImageFilters.h" #include "include/effects/SkImageFilters.h"
#include "modules/svg/include/SkSVGFe.h" #include "modules/svg/include/SkSVGFe.h"
#include "modules/svg/include/SkSVGFilter.h" #include "modules/svg/include/SkSVGFilter.h"
@ -46,6 +47,7 @@ SkRect SkSVGFilter::resolveFilterRegion(const SkSVGRenderContext& ctx) const {
sk_sp<SkImageFilter> SkSVGFilter::buildFilterDAG(const SkSVGRenderContext& ctx) const { sk_sp<SkImageFilter> SkSVGFilter::buildFilterDAG(const SkSVGRenderContext& ctx) const {
sk_sp<SkImageFilter> filter; sk_sp<SkImageFilter> filter;
SkSVGFilterContext fctx(resolveFilterRegion(ctx), fPrimitiveUnits); SkSVGFilterContext fctx(resolveFilterRegion(ctx), fPrimitiveUnits);
SkSVGColorspace cs = SkSVGColorspace::kSRGB;
for (const auto& child : fChildren) { for (const auto& child : fChildren) {
if (!SkSVGFe::IsFilterEffect(child)) { if (!SkSVGFe::IsFilterEffect(child)) {
continue; continue;
@ -62,14 +64,19 @@ sk_sp<SkImageFilter> SkSVGFilter::buildFilterDAG(const SkSVGRenderContext& ctx)
feNode.applyProperties(&localCtx); feNode.applyProperties(&localCtx);
// TODO: there are specific composition rules that need to be followed // TODO: there are specific composition rules that need to be followed
// TODO: perform colorspace conversions depending on 'color-interpolation-filters' setting cs = feNode.resolveColorspace(ctx);
// of the current node and its inputs.
filter = feNode.makeImageFilter(localCtx, fctx); filter = feNode.makeImageFilter(localCtx, fctx);
if (!feResultType.isEmpty()) { if (!feResultType.isEmpty()) {
fctx.registerResult(feResultType, filter, feNode.resolveFilterSubregion(localCtx, fctx)); fctx.registerResult(
feResultType, filter, feNode.resolveFilterSubregion(localCtx, fctx), cs);
} }
} }
// Convert to final destination colorspace
if (cs != SkSVGColorspace::kSRGB) {
filter = SkImageFilters::ColorFilter(SkColorFilters::LinearToSRGBGamma(), filter);
}
return filter; return filter;
} }

View File

@ -5,15 +5,33 @@
* found in the LICENSE file. * found in the LICENSE file.
*/ */
#include "include/core/SkColorFilter.h"
#include "include/effects/SkImageFilters.h" #include "include/effects/SkImageFilters.h"
#include "modules/svg/include/SkSVGFilterContext.h" #include "modules/svg/include/SkSVGFilterContext.h"
#include "modules/svg/include/SkSVGNode.h" #include "modules/svg/include/SkSVGNode.h"
#include "modules/svg/include/SkSVGRenderContext.h" #include "modules/svg/include/SkSVGRenderContext.h"
#include "modules/svg/include/SkSVGTypes.h" #include "modules/svg/include/SkSVGTypes.h"
sk_sp<SkImageFilter> SkSVGFilterContext::findResultById(const SkSVGStringType& id) const { namespace {
const Result* res = fResults.find(id);
return res ? res->fImageFilter : nullptr; sk_sp<SkImageFilter> ConvertFilterColorspace(sk_sp<SkImageFilter>&& input,
SkSVGColorspace src,
SkSVGColorspace dst) {
if (src == dst) {
return std::move(input);
} else if (src == SkSVGColorspace::kSRGB && dst == SkSVGColorspace::kLinearRGB) {
return SkImageFilters::ColorFilter(SkColorFilters::SRGBToLinearGamma(), input);
} else {
SkASSERT(src == SkSVGColorspace::kLinearRGB && dst == SkSVGColorspace::kSRGB);
return SkImageFilters::ColorFilter(SkColorFilters::LinearToSRGBGamma(), input);
}
}
} // namespace
const SkSVGFilterContext::Result* SkSVGFilterContext::findResultById(
const SkSVGStringType& id) const {
return fResults.find(id);
} }
const SkRect& SkSVGFilterContext::filterPrimitiveSubregion(const SkSVGFeInputType& input) const { const SkRect& SkSVGFilterContext::filterPrimitiveSubregion(const SkSVGFeInputType& input) const {
@ -25,18 +43,24 @@ const SkRect& SkSVGFilterContext::filterPrimitiveSubregion(const SkSVGFeInputTyp
void SkSVGFilterContext::registerResult(const SkSVGStringType& id, void SkSVGFilterContext::registerResult(const SkSVGStringType& id,
const sk_sp<SkImageFilter>& result, const sk_sp<SkImageFilter>& result,
const SkRect& subregion) { const SkRect& subregion,
SkSVGColorspace resultColorspace) {
SkASSERT(!id.isEmpty()); SkASSERT(!id.isEmpty());
fResults[id] = {result, subregion}; fResults[id] = {result, subregion, resultColorspace};
} }
sk_sp<SkImageFilter> SkSVGFilterContext::resolveInput(const SkSVGRenderContext& ctx, sk_sp<SkImageFilter> SkSVGFilterContext::resolveInput(const SkSVGRenderContext& ctx,
const SkSVGFeInputType& inputType) const { const SkSVGFeInputType& inputType,
SkSVGColorspace colorspace) const {
SkSVGColorspace inputCS = SkSVGColorspace::kSRGB;
sk_sp<SkImageFilter> result;
switch (inputType.type()) { switch (inputType.type()) {
case SkSVGFeInputType::Type::kSourceGraphic: case SkSVGFeInputType::Type::kSourceGraphic:
return nullptr; // Do nothing.
break;
case SkSVGFeInputType::Type::kFillPaint: case SkSVGFeInputType::Type::kFillPaint:
return SkImageFilters::Paint(*ctx.fillPaint()); result = SkImageFilters::Paint(*ctx.fillPaint());
break;
case SkSVGFeInputType::Type::kStrokePaint: { case SkSVGFeInputType::Type::kStrokePaint: {
// The paint filter doesn't handle stroke paints properly, so convert to fill for // The paint filter doesn't handle stroke paints properly, so convert to fill for
// simplicity. // simplicity.
@ -44,12 +68,21 @@ sk_sp<SkImageFilter> SkSVGFilterContext::resolveInput(const SkSVGRenderContext&
// requires some extra work to handle all paint features (gradients, etc). // requires some extra work to handle all paint features (gradients, etc).
SkPaint p = *ctx.strokePaint(); SkPaint p = *ctx.strokePaint();
p.setStyle(SkPaint::kFill_Style); p.setStyle(SkPaint::kFill_Style);
return SkImageFilters::Paint(p); result = SkImageFilters::Paint(p);
break;
}
case SkSVGFeInputType::Type::kFilterPrimitiveReference: {
const Result* res = findResultById(inputType.id());
if (res) {
result = res->fImageFilter;
inputCS = res->fColorspace;
}
break;
} }
case SkSVGFeInputType::Type::kFilterPrimitiveReference:
return findResultById(inputType.id());
default: default:
SkDebugf("unhandled filter input type %d\n", inputType.type()); SkDebugf("unhandled filter input type %d\n", inputType.type());
return nullptr; return nullptr;
} }
return ConvertFilterColorspace(std::move(result), inputCS, colorspace);
} }