xref: /aosp_15_r20/external/skia/modules/svg/src/SkSVGFilterContext.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2020 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "modules/svg/include/SkSVGFilterContext.h"
9 
10 #include "include/core/SkBlendMode.h"
11 #include "include/core/SkColor.h"
12 #include "include/core/SkColorFilter.h"
13 #include "include/core/SkColorSpace.h"
14 #include "include/core/SkPaint.h"
15 #include "include/core/SkShader.h"
16 #include "include/effects/SkColorMatrix.h"
17 #include "include/effects/SkImageFilters.h"
18 #include "include/private/base/SkAssert.h"
19 #include "include/private/base/SkDebug.h"
20 #include "modules/svg/include/SkSVGRenderContext.h"
21 #include "modules/svg/include/SkSVGTypes.h"
22 #include "src/base/SkTLazy.h"
23 
24 #include <utility>
25 
26 namespace {
27 
ConvertFilterColorspace(sk_sp<SkImageFilter> && input,SkSVGColorspace src,SkSVGColorspace dst)28 sk_sp<SkImageFilter> ConvertFilterColorspace(sk_sp<SkImageFilter>&& input,
29                                              SkSVGColorspace src,
30                                              SkSVGColorspace dst) {
31     if (src == dst) {
32         return std::move(input);
33     } else if (src == SkSVGColorspace::kSRGB && dst == SkSVGColorspace::kLinearRGB) {
34         return SkImageFilters::ColorFilter(SkColorFilters::SRGBToLinearGamma(), input);
35     } else {
36         SkASSERT(src == SkSVGColorspace::kLinearRGB && dst == SkSVGColorspace::kSRGB);
37         return SkImageFilters::ColorFilter(SkColorFilters::LinearToSRGBGamma(), input);
38     }
39 }
40 
paint_as_shader(const SkPaint & paint)41 sk_sp<SkShader> paint_as_shader(const SkPaint& paint) {
42     sk_sp<SkShader> shader = paint.refShader();
43     auto color = paint.getColor4f();
44     if (shader && color.fA < 1.f) {
45         // Multiply by paint alpha
46         shader = shader->makeWithColorFilter(
47                 SkColorFilters::Blend(color, /*colorSpace=*/nullptr, SkBlendMode::kDstIn));
48     } else if (!shader) {
49         shader = SkShaders::Color(color, /*colorSpace=*/nullptr);
50     }
51     if (paint.getColorFilter()) {
52         shader = shader->makeWithColorFilter(paint.refColorFilter());
53     }
54     return shader;
55 }
56 
57 }  // namespace
58 
findResultById(const SkSVGStringType & id) const59 const SkSVGFilterContext::Result* SkSVGFilterContext::findResultById(
60         const SkSVGStringType& id) const {
61     return fResults.find(id);
62 }
63 
filterPrimitiveSubregion(const SkSVGFeInputType & input) const64 const SkRect& SkSVGFilterContext::filterPrimitiveSubregion(const SkSVGFeInputType& input) const {
65     const Result* res = nullptr;
66     if (input.type() == SkSVGFeInputType::Type::kFilterPrimitiveReference) {
67         res = fResults.find(input.id());
68     } else if (input.type() == SkSVGFeInputType::Type::kUnspecified) {
69         res = &fPreviousResult;
70     }
71     return res ? res->fFilterSubregion : fFilterEffectsRegion;
72 }
73 
registerResult(const SkSVGStringType & id,const sk_sp<SkImageFilter> & result,const SkRect & subregion,SkSVGColorspace resultColorspace)74 void SkSVGFilterContext::registerResult(const SkSVGStringType& id,
75                                         const sk_sp<SkImageFilter>& result,
76                                         const SkRect& subregion,
77                                         SkSVGColorspace resultColorspace) {
78     SkASSERT(!id.isEmpty());
79     fResults[id] = {result, subregion, resultColorspace};
80 }
81 
setPreviousResult(const sk_sp<SkImageFilter> & result,const SkRect & subregion,SkSVGColorspace resultColorspace)82 void SkSVGFilterContext::setPreviousResult(const sk_sp<SkImageFilter>& result,
83                                            const SkRect& subregion,
84                                            SkSVGColorspace resultColorspace) {
85     fPreviousResult = {result, subregion, resultColorspace};
86 }
87 
previousResultIsSourceGraphic() const88 bool SkSVGFilterContext::previousResultIsSourceGraphic() const {
89     return fPreviousResult.fImageFilter == nullptr;
90 }
91 
92 // https://www.w3.org/TR/SVG11/filters.html#FilterPrimitiveInAttribute
getInput(const SkSVGRenderContext & ctx,const SkSVGFeInputType & inputType) const93 std::tuple<sk_sp<SkImageFilter>, SkSVGColorspace> SkSVGFilterContext::getInput(
94         const SkSVGRenderContext& ctx, const SkSVGFeInputType& inputType) const {
95     SkSVGColorspace inputCS = SkSVGColorspace::kSRGB;
96     sk_sp<SkImageFilter> result;
97     switch (inputType.type()) {
98         case SkSVGFeInputType::Type::kSourceAlpha: {
99             SkColorMatrix m;
100             m.setScale(0, 0, 0, 1.0f);
101             result = SkImageFilters::ColorFilter(SkColorFilters::Matrix(m), nullptr);
102             break;
103         }
104         case SkSVGFeInputType::Type::kSourceGraphic:
105             // Do nothing.
106             break;
107         case SkSVGFeInputType::Type::kFillPaint: {
108             const auto& fillPaint = ctx.fillPaint();
109             if (fillPaint.isValid()) {
110                 auto dither = fillPaint->isDither() ? SkImageFilters::Dither::kYes
111                                                     : SkImageFilters::Dither::kNo;
112                 result = SkImageFilters::Shader(paint_as_shader(*fillPaint), dither);
113             }
114             break;
115         }
116         case SkSVGFeInputType::Type::kStrokePaint: {
117             // The paint filter doesn't apply fill/stroke styling, but use the paint settings
118             // defined for strokes.
119             const auto& strokePaint = ctx.strokePaint();
120             if (strokePaint.isValid()) {
121                 auto dither = strokePaint->isDither() ? SkImageFilters::Dither::kYes
122                                                       : SkImageFilters::Dither::kNo;
123                 result = SkImageFilters::Shader(paint_as_shader(*strokePaint), dither);
124             }
125             break;
126         }
127         case SkSVGFeInputType::Type::kFilterPrimitiveReference: {
128             const Result* res = findResultById(inputType.id());
129             if (res) {
130                 result = res->fImageFilter;
131                 inputCS = res->fColorspace;
132             }
133             break;
134         }
135         case SkSVGFeInputType::Type::kUnspecified: {
136             result = fPreviousResult.fImageFilter;
137             inputCS = fPreviousResult.fColorspace;
138             break;
139         }
140         default:
141             SkDEBUGF("unhandled filter input type %d\n", (int)inputType.type());
142             break;
143     }
144 
145     return {result, inputCS};
146 }
147 
resolveInputColorspace(const SkSVGRenderContext & ctx,const SkSVGFeInputType & inputType) const148 SkSVGColorspace SkSVGFilterContext::resolveInputColorspace(
149         const SkSVGRenderContext& ctx, const SkSVGFeInputType& inputType) const {
150     return std::get<1>(this->getInput(ctx, inputType));
151 }
152 
resolveInput(const SkSVGRenderContext & ctx,const SkSVGFeInputType & inputType) const153 sk_sp<SkImageFilter> SkSVGFilterContext::resolveInput(const SkSVGRenderContext& ctx,
154                                                       const SkSVGFeInputType& inputType) const {
155     return std::get<0>(this->getInput(ctx, inputType));
156 }
157 
resolveInput(const SkSVGRenderContext & ctx,const SkSVGFeInputType & inputType,SkSVGColorspace colorspace) const158 sk_sp<SkImageFilter> SkSVGFilterContext::resolveInput(const SkSVGRenderContext& ctx,
159                                                       const SkSVGFeInputType& inputType,
160                                                       SkSVGColorspace colorspace) const {
161     auto [result, inputCS] = this->getInput(ctx, inputType);
162     return ConvertFilterColorspace(std::move(result), inputCS, colorspace);
163 }
164