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