• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2021 Google LLC
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 "include/core/SkCanvas.h"
9 #include "include/core/SkPaint.h"
10 #include "include/effects/SkImageFilters.h"
11 #include "include/effects/SkRuntimeEffect.h"
12 #include "src/core/SkImageFilter_Base.h"
13 #include "src/core/SkReadBuffer.h"
14 #include "src/core/SkRuntimeEffectPriv.h"
15 #include "src/core/SkSpecialImage.h"
16 #include "src/core/SkSpecialSurface.h"
17 #include "src/core/SkWriteBuffer.h"
18 #include "src/effects/imagefilters/SkRuntimeImageFilter.h"
19 
20 #ifdef SK_ENABLE_SKSL
21 
22 namespace {
23 
24 class SkRuntimeImageFilter final : public SkImageFilter_Base {
25 public:
SkRuntimeImageFilter(sk_sp<SkRuntimeEffect> effect,sk_sp<SkData> uniforms,sk_sp<SkImageFilter> input)26     SkRuntimeImageFilter(sk_sp<SkRuntimeEffect> effect,
27                          sk_sp<SkData> uniforms,
28                          sk_sp<SkImageFilter> input)
29             : INHERITED(&input, 1, /*cropRect=*/nullptr)
30             , fEffect(std::move(effect))
31             , fUniforms(std::move(uniforms)) {}
32 
onAffectsTransparentBlack() const33     bool onAffectsTransparentBlack() const override { return true; }
onGetCTMCapability() const34     MatrixCapability onGetCTMCapability() const override { return MatrixCapability::kTranslate; }
35 
36 protected:
37     void flatten(SkWriteBuffer&) const override;
38     sk_sp<SkSpecialImage> onFilterImage(const Context&, SkIPoint* offset) const override;
39 
40 private:
41     friend void ::SkRegisterRuntimeImageFilterFlattenable();
42     SK_FLATTENABLE_HOOKS(SkRuntimeImageFilter)
43 
44     sk_sp<SkRuntimeEffect> fEffect;
45     sk_sp<SkData>          fUniforms;
46 
47     using INHERITED = SkImageFilter_Base;
48 };
49 
50 } // end namespace
51 
SkMakeRuntimeImageFilter(sk_sp<SkRuntimeEffect> effect,sk_sp<SkData> uniforms,sk_sp<SkImageFilter> input)52 sk_sp<SkImageFilter> SkMakeRuntimeImageFilter(sk_sp<SkRuntimeEffect> effect,
53                                               sk_sp<SkData> uniforms,
54                                               sk_sp<SkImageFilter> input) {
55     // Rather than replicate all of the checks from makeShader here, just try to create a shader
56     // once, to determine if everything is valid.
57     sk_sp<SkShader> child = nullptr;
58     auto shader = effect->makeShader(uniforms, &child, 1, nullptr, false);
59     if (!shader) {
60         // Could be wrong signature, wrong uniform block size, wrong number/type of children, etc...
61         return nullptr;
62     }
63 
64     return sk_sp<SkImageFilter>(
65             new SkRuntimeImageFilter(std::move(effect), std::move(uniforms), std::move(input)));
66 }
67 
SkRegisterRuntimeImageFilterFlattenable()68 void SkRegisterRuntimeImageFilterFlattenable() {
69     SK_REGISTER_FLATTENABLE(SkRuntimeImageFilter);
70 }
71 
CreateProc(SkReadBuffer & buffer)72 sk_sp<SkFlattenable> SkRuntimeImageFilter::CreateProc(SkReadBuffer& buffer) {
73     SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1);
74     SkString sksl;
75     buffer.readString(&sksl);
76     sk_sp<SkData> uniforms = buffer.readByteArrayAsData();
77 
78     auto effect = SkMakeCachedRuntimeEffect(SkRuntimeEffect::MakeForShader, std::move(sksl));
79     if (!buffer.validate(effect != nullptr)) {
80         return nullptr;
81     }
82     if (common.cropRect()) {
83         return nullptr;
84     }
85 
86     return SkMakeRuntimeImageFilter(std::move(effect), std::move(uniforms), common.getInput(0));
87 }
88 
flatten(SkWriteBuffer & buffer) const89 void SkRuntimeImageFilter::flatten(SkWriteBuffer& buffer) const {
90     this->INHERITED::flatten(buffer);
91     buffer.writeString(fEffect->source().c_str());
92     buffer.writeDataAsByteArray(fUniforms.get());
93 }
94 
95 ///////////////////////////////////////////////////////////////////////////////////////////////////
96 
onFilterImage(const Context & ctx,SkIPoint * offset) const97 sk_sp<SkSpecialImage> SkRuntimeImageFilter::onFilterImage(const Context& ctx,
98                                                           SkIPoint* offset) const {
99     SkIPoint inputOffset = SkIPoint::Make(0, 0);
100     sk_sp<SkSpecialImage> input(this->filterInput(0, ctx, &inputOffset));
101     if (!input) {
102         return nullptr;
103     }
104 
105     SkIRect outputBounds = SkIRect(ctx.desiredOutput());
106     sk_sp<SkSpecialSurface> surf(ctx.makeSurface(outputBounds.size()));
107     if (!surf) {
108         return nullptr;
109     }
110 
111     SkMatrix ctm = ctx.ctm();
112     SkMatrix inverse;
113     SkAssertResult(ctm.invert(&inverse));
114 
115     SkMatrix localM = inverse *
116                       SkMatrix::Translate(inputOffset) *
117                       SkMatrix::Translate(-input->subset().topLeft());
118     sk_sp<SkShader> inputShader =
119             input->asImage()->makeShader(SkSamplingOptions(SkFilterMode::kLinear), &localM);
120     SkASSERT(inputShader);
121 
122     auto shader = fEffect->makeShader(fUniforms, &inputShader, 1, nullptr, false);
123     SkASSERT(shader);
124 
125     SkPaint paint;
126     paint.setShader(std::move(shader));
127     paint.setBlendMode(SkBlendMode::kSrc);
128 
129     SkCanvas* canvas = surf->getCanvas();
130     SkASSERT(canvas);
131 
132     // Translate from layer space into surf's image space
133     canvas->translate(-outputBounds.fLeft, -outputBounds.fTop);
134     // Ensure shader parameters are relative to parameter space, not layer space
135     canvas->concat(ctx.ctm());
136 
137     canvas->drawPaint(paint);
138 
139     *offset = outputBounds.topLeft();
140     return surf->makeImageSnapshot();
141 }
142 
143 #endif  // SK_ENABLE_SKSL
144