1 /*
2  * Copyright (c) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "ge_mesa_blur_shader_filter.h"
17 
18 #include "ge_log.h"
19 #include "ge_system_properties.h"
20 #include "src/core/SkOpts.h"
21 
22 #include "effect/color_matrix.h"
23 #include <vector>
24 
25 namespace OHOS {
26 namespace Rosen {
27 #define PROPERTY_KAWASE_ORIGINAL_IMAGE "persist.sys.graphic.kawaseOriginalEnable"
28 
29 namespace {
30 static constexpr float BASE_BLUR_SCALE = 0.5f; // 0.5: base downSample radio
31 static constexpr float BLUR_SCALE_1 = 0.25f; // 0.25 : downSample scale for step1
32 static constexpr float BLUR_SCALE_2 = 0.125f; // 0.125 : downSample scale for step1p5
33 static constexpr float BLUR_SCALE_3 = 0.0625f; // 0.0625 : downSample scale for step2
34 static constexpr float BLUR_SCALE_4 = 0.03125f; // 0.03125 : downSample scale for step3
35 static constexpr int BLUR_RADIUS_1 = 8; // 8 : radius step1
36 static constexpr int BLUR_RADIUS_1P5 = 20; // 20 : radius step1.5
37 static constexpr int BLUR_RADIUS_20 = 24; // 24 : radius step2-0
38 static constexpr int BLUR_RADIUS_21 = 80; // 80 : radius step2-1
39 static constexpr int BLUR_RADIUS_2 = 100; // 100 : radius step2
40 static constexpr int BLUR_RADIUS_3 = 200; // 200 : radius step3
41 static std::shared_ptr<Drawing::RuntimeEffect> g_blurEffect;
42 static std::shared_ptr<Drawing::RuntimeEffect> g_mixEffect;
43 static std::shared_ptr<Drawing::RuntimeEffect> g_simpleFilter;
44 static std::shared_ptr<Drawing::RuntimeEffect> g_greyAdjustEffect;
45 
46 static const std::vector<std::vector<float>> offsetTableFourPasses = {
47     /*
48         sampling offsets and weights for different blur radius in 4-pass algorithm
49     */
50     {{1.421365322425756, 1.421365322425756, 2.52888420687976, 1.6983888474288191,
51       0.5060327516701945, 2.477687296510791, 2.645538868459866, 0.4675865935441517}},
52     {{1.5532466535764577, 1.5532466535764577, 3.260647277662576, 2.6149639458271343,
53       1.492416795484721, 3.3432890011050254, 2.559487807195717, 0.362113179598861}}
54 };
55 
56 static const std::vector<std::vector<float>> offsetTableFivePasses = {
57     /*
58         sampling offsets and weights for different blur radius in 5-pass algorithm
59     */
60     {{1.29351593, 1.29351593, 0.53793921, 1.5822416, 1.58224067,
61       0.53794761, 1.58223699, 0.5379565, 0.53795194, 1.58223997}},
62     {{1.4925693309454728, 1.4925693309454728, 1.248431515423821, 2.1609475057344536, 2.3006446212434306,
63       0.38158524446502357, 2.160947472355003, 1.248431447998051, 0.3815852898151447, 2.300644665028775}},
64     {{2.461169146598135, 2.461169146598135, 1.5094283743591717, 1.5185693547666796, 3.2470361889434103,
65       0.5465662643903497, 2.6710526969877204, 1.456340654010302, 0.47564642926095513, 2.5790346948641893}},
66     {{2.7877137008630277, 2.7877137008630277, 1.368781571748399, 1.3779906041976446, 4.275711858457529,
67       1.5175122219974202, 2.779896417187395, 1.5946441694737459, 0.6545369425231907, 3.5714310651049317}},
68     {{3.409158646436388, 3.409158646436388, 1.3807194929023436, 1.6494765647589356, 4.638321454662683,
69       2.304041644534522, 2.6625386778188735, 1.554841535232042, 0.5313373875839217, 4.660706115409412}},
70     {{4.425333740948246, 4.425333740948246, 1.1845267031766928, 1.4500896889624584, 5.313559133773294,
71       2.4323499776778643, 2.606322065282447, 1.2553675953527443, 1.1729490480100304, 5.166409017496668}},
72     {{4.710484187853573, 4.710484187853573, 1.4007370136555897, 1.2562595222759485, 6.5684867433401495,
73       2.4384921005985807, 3.2541990928397246, 1.4465647404038315, 1.436550871193364, 5.52173166041527}},
74     {{5.561630104245352, 5.561630104245352, 1.419409904905922, 1.556544555489513, 6.592301531601723,
75       3.277577833641203, 3.3151278290421873, 1.573561193200789, 1.3588630736274054, 6.361664806350875}},
76 };
77 
78 } // namespace
79 
GetKawaseOriginalEnabled()80 static bool GetKawaseOriginalEnabled()
81 {
82 #ifdef GE_OHOS
83     static bool kawaseOriginalEnabled =
84         (std::atoi(GESystemProperties::GetEventProperty(PROPERTY_KAWASE_ORIGINAL_IMAGE).c_str()) != 0);
85     return kawaseOriginalEnabled;
86 #else
87     return false;
88 #endif
89 }
90 
GEMESABlurShaderFilter(const Drawing::GEMESABlurShaderFilterParams & params)91 GEMESABlurShaderFilter::GEMESABlurShaderFilter(const Drawing::GEMESABlurShaderFilterParams& params)
92     : radius_(params.radius), greyCoef1_(params.greyCoef1), greyCoef2_(params.greyCoef2),
93       stretchOffsetX_(params.offsetX), stretchOffsetY_(params.offsetY),
94       stretchOffsetZ_(params.offsetZ), stretchOffsetW_(params.offsetW),
95       tileMode_(static_cast<Drawing::TileMode>(params.tileMode)), width_(params.width), height_(params.height)
96 {
97     if (!InitBlurEffect() || !InitMixEffect() || !InitSimpleFilter()) {
98         return;
99     }
100 
101     if (radius_ < 1) {
102         LOGD("GEMESABlurShaderFilter radius(%{public}d) should be [1, 8k], ignore blur.", radius_);
103         radius_ = 0;
104     }
105 
106     if (radius_ > 8000) { // 8000 experienced value
107         LOGD("GEMESABlurShaderFilter radius(%{public}d) should be [1, 8k], change to 8k.", radius_);
108         radius_ = 8000; // 8000 experienced value
109     }
110 
111     if (greyCoef1_ > 1e-6 || greyCoef2_ > 1e-6) {
112         isGreyX_ = 1;
113         LOGD("GEMESABlurShaderFilter::GreyAdjustment fuzed with blur:greycoef1 = %{public}f, greycoef2 = %{public}f",
114             greyCoef1_, greyCoef2_);
115         if (!InitGreyAdjustmentEffect()) {
116             return;
117         }
118     }
119 }
120 
SetBlurParamsFivePassSmall(NewBlurParams & bParam)121 void GEMESABlurShaderFilter::SetBlurParamsFivePassSmall(NewBlurParams& bParam)
122 {
123     int stride = 2;     // 2: stride
124     // 5: five passes
125     int numberOfPasses = 5;
126     int index;
127     // 1.f: initial scaling rate
128     float scale = 1.f;
129     float w1;
130     if (blurRadius_ < BLUR_RADIUS_20) {
131         scale = blurRadius_ / static_cast<float>(BLUR_RADIUS_20);
132         index = 0;
133         w1 = 0;
134     } else if (blurRadius_ > BLUR_RADIUS_21) {
135         scale = blurRadius_ / static_cast<float>(BLUR_RADIUS_21);
136         // 8: blur param
137         index = (BLUR_RADIUS_21 - BLUR_RADIUS_20) / 8;
138         w1 = 0;
139     } else {
140         // 0.125: blur param
141         float findex = (blurRadius_ - BLUR_RADIUS_20) * 0.125;
142         index = floor(findex);
143         w1 = findex - index;
144     }
145     if (fabs(w1) < 1e-6) {
146         for (int i = 0; i < numberOfPasses; i++) {
147             bParam.offsets[stride * i] = scale * offsetTableFivePasses[index][stride * i];
148             bParam.offsets[stride * i + 1] = scale * offsetTableFivePasses[index][stride * i + 1];
149         }
150     } else {
151         float w2 = 1 - w1;
152         for (int i = 0; i < numberOfPasses; i++) {
153             bParam.offsets[stride * i] = w2 * offsetTableFivePasses[index][stride * i] +
154                 w1 * offsetTableFivePasses[index + 1][stride * i];
155             bParam.offsets[stride * i + 1] = w2 * offsetTableFivePasses[index][stride * i + 1] +
156                 w1 * offsetTableFivePasses[index + 1][stride * i + 1];
157         }
158     }
159     bParam.numberOfPasses = numberOfPasses;
160     return;
161 }
162 
SetBlurParamsFivePassLarge(NewBlurParams & bParam)163 void GEMESABlurShaderFilter::SetBlurParamsFivePassLarge(NewBlurParams& bParam)
164 {
165     int stride = 2;     // 2: stride
166     // 5: five passes
167     int numberOfPasses = 5;
168     float scale;
169     if (blurRadius_ < BLUR_RADIUS_3) {
170         // 0.5: scaling rate
171         scale = blurRadius_ / static_cast<float>(BLUR_RADIUS_21) * 0.5;
172     } else {
173         // 0.25: scaling rate
174         scale = blurRadius_ / static_cast<float>(BLUR_RADIUS_21) * 0.25;
175     }
176     int index = (BLUR_RADIUS_21 - BLUR_RADIUS_20) / 8;
177     for (int i = 0; i < numberOfPasses; i++) {
178         bParam.offsets[stride * i] = scale * offsetTableFivePasses[index][stride * i];
179         bParam.offsets[stride * i + 1] = scale * offsetTableFivePasses[index][stride * i + 1];
180     }
181     bParam.numberOfPasses = numberOfPasses;
182     return;
183 }
184 
SetBlurParams(NewBlurParams & bParam)185 void GEMESABlurShaderFilter::SetBlurParams(NewBlurParams& bParam)
186 {
187     int stride = 2;     // 2: stride
188     int numberOfPasses;
189     if (blurRadius_ < BLUR_RADIUS_1) {
190         // 2: min number of pass, 4: fixed four passes
191         numberOfPasses = std::clamp(static_cast<int>(blurRadius_), 2, 4);
192         float mys = blurRadius_ * 0.125;    // 0.125: scaling rate
193         for (int i = 0; i < numberOfPasses; i++) {
194             bParam.offsets[stride * i] = offsetTableFourPasses[0][stride * i] * mys;
195             bParam.offsets[stride * i + 1] = offsetTableFourPasses[0][stride * i + 1] * mys;
196         }
197         if (blurRadius_ >= BLUR_RADIUS_1 - stride) {
198             // 3: pre-filtering when the radius is larger than the calculated value
199             // 0.33333333: scaling rate.
200             // 1.8: blur param
201             float blurParam = 1.8;
202             float perf = (blurRadius_ - BLUR_RADIUS_1) * 0.33333333 + 1;
203             bParam.offsets[stride * numberOfPasses] = blurParam * perf;
204             bParam.offsets[stride * numberOfPasses + 1] = blurParam * perf;
205             numberOfPasses++;
206         }
207         bParam.numberOfPasses = numberOfPasses;
208         return;
209     }
210     if (blurRadius_ < BLUR_RADIUS_1P5) {
211         // 4: four passes
212         numberOfPasses = 4;
213         // 16.0: scaling rate
214         float scale = blurRadius_ / 16.0;
215         for (int i = 0; i < numberOfPasses; i++) {
216             bParam.offsets[stride * i] = scale * offsetTableFourPasses[0][stride * i];
217             bParam.offsets[stride * i + 1] = scale * offsetTableFourPasses[0][stride * i + 1];
218         }
219 
220         int newStride = 3;
221         static constexpr float BLUR_PARAM_A = 0.4;
222         static constexpr float BLUR_PARAM_B = 0.35;
223         if (blurRadius_ >= BLUR_RADIUS_1P5 - newStride) {
224             // 4: pre-filtering when the radius is larger than the calculated value
225             float perf = (blurRadius_ - BLUR_RADIUS_1P5 + newStride + 1);
226             bParam.offsets[stride * numberOfPasses] = BLUR_PARAM_A * perf + BLUR_PARAM_B;
227             bParam.offsets[stride * numberOfPasses + 1] = BLUR_PARAM_A * perf + BLUR_PARAM_B;
228             numberOfPasses++;
229         }
230         bParam.numberOfPasses = numberOfPasses;
231         return;
232     }
233     if (blurRadius_ < BLUR_RADIUS_2) {
234         SetBlurParamsFivePassSmall(bParam);
235     } else {
236         SetBlurParamsFivePassLarge(bParam);
237     }
238     return;
239 }
240 
GetRadius() const241 int GEMESABlurShaderFilter::GetRadius() const
242 {
243     return radius_;
244 }
245 
ApplyGreyAdjustmentFilter(Drawing::Canvas & canvas,const std::shared_ptr<Drawing::Image> & input,const std::shared_ptr<Drawing::ShaderEffect> & prevShader,const Drawing::ImageInfo & scaledInfo,const Drawing::SamplingOptions & linear) const246 std::shared_ptr<Drawing::ShaderEffect> GEMESABlurShaderFilter::ApplyGreyAdjustmentFilter(Drawing::Canvas& canvas,
247     const std::shared_ptr<Drawing::Image>& input, const std::shared_ptr<Drawing::ShaderEffect>& prevShader,
248     const Drawing::ImageInfo& scaledInfo, const Drawing::SamplingOptions& linear) const
249 {
250     Drawing::RuntimeShaderBuilder builder(g_greyAdjustEffect);
251     builder.SetChild("imageShader", prevShader);
252     builder.SetUniform("coefficient1", greyCoef1_);
253     builder.SetUniform("coefficient2", greyCoef2_);
254     std::shared_ptr<Drawing::Image> tmpBlur(builder.MakeImage(
255         canvas.GetGPUContext().get(), nullptr, scaledInfo, false));
256     return GetShaderEffect(tmpBlur, linear, Drawing::Matrix());
257 }
258 
GetShaderEffect(const std::shared_ptr<Drawing::Image> & image,const Drawing::SamplingOptions & linear,const Drawing::Matrix & matrix) const259 std::shared_ptr<Drawing::ShaderEffect> GEMESABlurShaderFilter::GetShaderEffect(
260     const std::shared_ptr<Drawing::Image>& image, const Drawing::SamplingOptions& linear,
261     const Drawing::Matrix& matrix) const
262 {
263     if (!image) {
264         return nullptr;
265     }
266     return Drawing::ShaderEffect::CreateImageShader(*image, Drawing::TileMode::CLAMP,
267         Drawing::TileMode::CLAMP, linear, matrix);
268 }
269 
DownSampling2X(Drawing::Canvas & canvas,const std::shared_ptr<Drawing::Image> & input,const Drawing::Rect & src,const Drawing::ImageInfo & scaledInfo,const Drawing::SamplingOptions & linear,const NewBlurParams & blur) const270 std::shared_ptr<Drawing::Image> GEMESABlurShaderFilter::DownSampling2X(Drawing::Canvas& canvas,
271     const std::shared_ptr<Drawing::Image>& input, const Drawing::Rect& src, const Drawing::ImageInfo& scaledInfo,
272     const Drawing::SamplingOptions& linear, const NewBlurParams& blur) const
273 {
274     Drawing::RuntimeShaderBuilder blurBuilder(g_blurEffect);
275     const auto& blurMatrix = BuildMatrix(src, scaledInfo, input);
276     auto tmpShader = Drawing::ShaderEffect::CreateImageShader(*input, Drawing::TileMode::CLAMP,
277         Drawing::TileMode::CLAMP, linear, blurMatrix);
278     if (isGreyX_) {
279         tmpShader = ApplyGreyAdjustmentFilter(canvas, input, tmpShader, scaledInfo, linear);
280         if (!tmpShader) {
281             return nullptr;
282         }
283     }
284     blurBuilder.SetChild("imageInput", tmpShader);
285     blurBuilder.SetUniform("in_blurOffset", blur.offsets[0], blur.offsets[1]);
286     return blurBuilder.MakeImage(canvas.GetGPUContext().get(), nullptr, scaledInfo, false);
287 }
288 
DownSampling4X(Drawing::Canvas & canvas,const std::shared_ptr<Drawing::Image> & input,const Drawing::Rect & src,const Drawing::ImageInfo & scaledInfo,const Drawing::SamplingOptions & linear,const NewBlurParams & blur) const289 std::shared_ptr<Drawing::Image> GEMESABlurShaderFilter::DownSampling4X(Drawing::Canvas& canvas,
290     const std::shared_ptr<Drawing::Image>& input, const Drawing::Rect& src, const Drawing::ImageInfo& scaledInfo,
291     const Drawing::SamplingOptions& linear, const NewBlurParams& blur) const
292 {
293     Drawing::RuntimeShaderBuilder blurBuilder(g_blurEffect);
294     const auto& blurMatrix = BuildMatrix(src, scaledInfo, input);
295     blurBuilder.SetChild("imageInput", Drawing::ShaderEffect::CreateImageShader(*input,
296         Drawing::TileMode::CLAMP, Drawing::TileMode::CLAMP, linear, blurMatrix));
297     blurBuilder.SetUniform("in_blurOffset", BLUR_SCALE_1, BLUR_SCALE_1);
298     if (isGreyX_) {
299         auto tmpShader = blurBuilder.MakeShader(nullptr, input->IsOpaque());
300         tmpShader = ApplyGreyAdjustmentFilter(canvas, input, tmpShader, scaledInfo, linear);
301         if (!tmpShader) {
302             return nullptr;
303         }
304         blurBuilder.SetChild("imageInput", tmpShader);
305         blurBuilder.SetUniform("in_blurOffset", blur.offsets[0], blur.offsets[1]);
306     }
307     return blurBuilder.MakeImage(canvas.GetGPUContext().get(), nullptr, scaledInfo, false);
308 }
309 
DownSampling8X(Drawing::Canvas & canvas,const std::shared_ptr<Drawing::Image> & input,const Drawing::Rect & src,const Drawing::ImageInfo & scaledInfo,const Drawing::ImageInfo & middleInfo,const Drawing::SamplingOptions & linear,const NewBlurParams & blur) const310 std::shared_ptr<Drawing::Image> GEMESABlurShaderFilter::DownSampling8X(Drawing::Canvas& canvas,
311     const std::shared_ptr<Drawing::Image>& input, const Drawing::Rect& src,
312     const Drawing::ImageInfo& scaledInfo, const Drawing::ImageInfo& middleInfo,
313     const Drawing::SamplingOptions& linear, const NewBlurParams& blur) const
314 {
315     Drawing::RuntimeShaderBuilder blurBuilder(g_blurEffect);
316     Drawing::Matrix blurMatrix = BuildMiddleMatrix(middleInfo, input->GetImageInfo());
317     Drawing::Matrix inputMatrix = BuildStretchMatrix(src, input->GetWidth(), input->GetHeight());
318     inputMatrix.PostConcat(blurMatrix);
319     blurBuilder.SetChild("imageInput", Drawing::ShaderEffect::CreateImageShader(*input,
320         tileMode_, tileMode_, linear, inputMatrix));
321     blurBuilder.SetUniform("in_blurOffset", BLUR_SCALE_1, BLUR_SCALE_1);
322     std::shared_ptr<Drawing::Image> tmpBlur_pre(blurBuilder.MakeImage(canvas.GetGPUContext().get(),
323         nullptr, middleInfo, false));
324     if (!tmpBlur_pre) {
325         return nullptr;
326     }
327     Drawing::Matrix blurMatrixA = BuildMiddleMatrix(scaledInfo, middleInfo);
328     Drawing::RuntimeShaderBuilder simpleBlurBuilder(g_simpleFilter);
329     simpleBlurBuilder.SetChild("imageInput", Drawing::ShaderEffect::CreateImageShader(*tmpBlur_pre,
330         Drawing::TileMode::CLAMP, Drawing::TileMode::CLAMP, linear, blurMatrixA));
331     if (isGreyX_) {
332         auto tmpShader = simpleBlurBuilder.MakeShader(nullptr, input->IsOpaque());
333         tmpShader = ApplyGreyAdjustmentFilter(canvas, input, tmpShader, scaledInfo, linear);
334         if (!tmpShader) {
335             return nullptr;
336         }
337         blurBuilder.SetChild("imageInput", tmpShader);
338         blurBuilder.SetUniform("in_blurOffset", blur.offsets[0], blur.offsets[1]);
339     } else {
340         tmpBlur_pre = simpleBlurBuilder.MakeImage(canvas.GetGPUContext().get(), nullptr, scaledInfo, false);
341         if (!tmpBlur_pre) {
342             return nullptr;
343         }
344         blurBuilder.SetChild("imageInput", Drawing::ShaderEffect::CreateImageShader(*tmpBlur_pre,
345             Drawing::TileMode::CLAMP, Drawing::TileMode::CLAMP, linear, Drawing::Matrix()));
346         blurBuilder.SetUniform("in_blurOffset", blur.offsets[0], blur.offsets[1]);
347     }
348     return blurBuilder.MakeImage(canvas.GetGPUContext().get(), nullptr, scaledInfo, false);
349 }
350 
DownSamplingMoreX(Drawing::Canvas & canvas,const std::shared_ptr<Drawing::Image> & input,const Drawing::Rect & src,const Drawing::ImageInfo & scaledInfo,const Drawing::ImageInfo & middleInfo,const Drawing::ImageInfo & middleInfo2,const Drawing::SamplingOptions & linear,const NewBlurParams & blur) const351 std::shared_ptr<Drawing::Image> GEMESABlurShaderFilter::DownSamplingMoreX(Drawing::Canvas& canvas,
352     const std::shared_ptr<Drawing::Image>& input, const Drawing::Rect& src,
353     const Drawing::ImageInfo& scaledInfo, const Drawing::ImageInfo& middleInfo, const Drawing::ImageInfo& middleInfo2,
354     const Drawing::SamplingOptions& linear, const NewBlurParams& blur) const
355 {
356     Drawing::RuntimeShaderBuilder blurBuilder(g_blurEffect);
357     Drawing::Matrix blurMatrix = BuildMiddleMatrix(middleInfo, input->GetImageInfo());
358     Drawing::Matrix inputMatrix = BuildStretchMatrix(src, input->GetWidth(), input->GetHeight());
359     inputMatrix.PostConcat(blurMatrix);
360     blurBuilder.SetChild("imageInput", Drawing::ShaderEffect::CreateImageShader(*input,
361         tileMode_, tileMode_, linear, inputMatrix));
362     blurBuilder.SetUniform("in_blurOffset", BLUR_SCALE_1, BLUR_SCALE_1);
363     std::shared_ptr<Drawing::Image> tmpBlur_pre(blurBuilder.MakeImage(canvas.GetGPUContext().get(),
364         nullptr, middleInfo, false));
365     if (!tmpBlur_pre) {
366         return nullptr;
367     }
368     Drawing::Matrix blurMatrixA = BuildMiddleMatrix(middleInfo2, middleInfo);
369     blurBuilder.SetChild("imageInput", Drawing::ShaderEffect::CreateImageShader(*tmpBlur_pre,
370         Drawing::TileMode::CLAMP, Drawing::TileMode::CLAMP, linear, blurMatrixA));
371     blurBuilder.SetUniform("in_blurOffset", BLUR_SCALE_1, BLUR_SCALE_1);
372     if (isGreyX_) {
373         Drawing::RuntimeShaderBuilder builder(g_greyAdjustEffect);
374         builder.SetChild("imageShader", blurBuilder.MakeShader(nullptr, input->IsOpaque()));
375         builder.SetUniform("coefficient1", greyCoef1_);
376         builder.SetUniform("coefficient2", greyCoef2_);
377         tmpBlur_pre = builder.MakeImage(canvas.GetGPUContext().get(), nullptr, middleInfo2, false);
378     } else {
379         tmpBlur_pre = blurBuilder.MakeImage(canvas.GetGPUContext().get(), nullptr, middleInfo2, false);
380     }
381     Drawing::Matrix blurMatrix2 = BuildMiddleMatrix(scaledInfo, middleInfo2);
382     blurBuilder.SetChild("imageInput", Drawing::ShaderEffect::CreateImageShader(*tmpBlur_pre,
383         Drawing::TileMode::CLAMP, Drawing::TileMode::CLAMP, linear, blurMatrix2));
384     blurBuilder.SetUniform("in_blurOffset", blur.offsets[0], blur.offsets[1]);
385     return blurBuilder.MakeImage(canvas.GetGPUContext().get(), nullptr, scaledInfo, false);
386 }
387 
DownSampling(Drawing::Canvas & canvas,const std::shared_ptr<Drawing::Image> & input,const Drawing::Rect & src,const Drawing::ImageInfo & scaledInfo,int & width,int & height,const Drawing::SamplingOptions & linear,const NewBlurParams & blur) const388 std::shared_ptr<Drawing::Image> GEMESABlurShaderFilter::DownSampling(Drawing::Canvas& canvas,
389     const std::shared_ptr<Drawing::Image>& input, const Drawing::Rect& src,
390     const Drawing::ImageInfo& scaledInfo, int& width, int& height,
391     const Drawing::SamplingOptions& linear, const NewBlurParams& blur) const
392 {
393     auto originImageInfo = input->GetImageInfo();
394     auto middleInfo = Drawing::ImageInfo(std::ceil(width * BLUR_SCALE_1), std::ceil(height * BLUR_SCALE_1),
395         originImageInfo.GetColorType(), originImageInfo.GetAlphaType(), originImageInfo.GetColorSpace());
396     if (blurScale_ > BLUR_SCALE_1 + 1e-4) {
397         return DownSampling2X(canvas, input, src, scaledInfo, linear, blur);
398     } else if (blurScale_ > BLUR_SCALE_2 + 1e-4) {
399         return DownSampling4X(canvas, input, src, scaledInfo, linear, blur);
400     } else if (blurScale_ > BLUR_SCALE_3 + 1e-4) {
401         return DownSampling8X(canvas, input, src, scaledInfo, middleInfo, linear, blur);
402     } else {
403         auto middleInfo2 = Drawing::ImageInfo(std::ceil(width * BLUR_SCALE_3), std::ceil(height * BLUR_SCALE_3),
404             originImageInfo.GetColorType(), originImageInfo.GetAlphaType(), originImageInfo.GetColorSpace());
405         return DownSamplingMoreX(canvas, input, src, scaledInfo, middleInfo, middleInfo2, linear, blur);
406     }
407 }
408 
ProcessImage(Drawing::Canvas & canvas,const std::shared_ptr<Drawing::Image> image,const Drawing::Rect & src,const Drawing::Rect & dst)409 std::shared_ptr<Drawing::Image> GEMESABlurShaderFilter::ProcessImage(Drawing::Canvas& canvas,
410     const std::shared_ptr<Drawing::Image> image, const Drawing::Rect& src, const Drawing::Rect& dst)
411 {
412     if (!IsInputValid(canvas, image, src, dst)) {
413         return image;
414     }
415     CalculatePixelStretch(image->GetWidth(), image->GetHeight());
416 
417     // Even so there is no blur, we may need to make greyadjustment and pixel stretch.
418     if (radius_ <= 0 || radius_ >= 8000 || GetKawaseOriginalEnabled()) {  // 8000 experienced value
419         return OutputImageWithoutBlur(canvas, image, src, dst);
420     }
421 
422     auto input = image;
423     CheckInputImage(canvas, image, input, src);
424     ComputeRadiusAndScale(radius_);
425     NewBlurParams blur;
426     SetBlurParams(blur);
427     auto originImageInfo = input->GetImageInfo();
428     auto width = std::max(static_cast<int>(std::ceil(dst.GetWidth())), input->GetWidth());
429     auto height = std::max(static_cast<int>(std::ceil(dst.GetHeight())), input->GetHeight());
430     auto scaledInfo = Drawing::ImageInfo(std::ceil(width * blurScale_), std::ceil(height * blurScale_),
431         originImageInfo.GetColorType(), originImageInfo.GetAlphaType(), originImageInfo.GetColorSpace());
432     Drawing::RuntimeShaderBuilder blurBuilder(g_blurEffect);
433     Drawing::SamplingOptions linear(Drawing::FilterMode::LINEAR, Drawing::MipmapMode::NONE);
434     LOGD("GEMESABlurShaderFilter:: sigma = %{public}f, numberOfPasses = %{public}d",
435         blurRadius_, blur.numberOfPasses);
436 
437     std::shared_ptr<Drawing::Image> tmpBlur = DownSampling(canvas, input, src, scaledInfo,
438         width, height, linear, blur);
439     if (!tmpBlur) {
440         LOGE("GEMESABlurShaderFilter::ProcessImage make image error when downsampling");
441         return image;
442     }
443 
444     int stride = 2;     // 2: stride
445     int i_start = 1;
446     if (fabs(blurScale_ - BLUR_SCALE_1) < 1e-4 && (!isGreyX_)) {
447         // 0: staring from zero when blurScale_ = 0.25 and isGreyX_ = false
448         i_start = 0;
449     }
450     for (auto i = i_start; i < blur.numberOfPasses; i++) {
451         blurBuilder.SetChild("imageInput", Drawing::ShaderEffect::CreateImageShader(*tmpBlur,
452             Drawing::TileMode::CLAMP, Drawing::TileMode::CLAMP, linear, Drawing::Matrix()));
453         blurBuilder.SetUniform("in_blurOffset", blur.offsets[stride * i], blur.offsets[stride * i + 1]);
454         tmpBlur = blurBuilder.MakeImage(canvas.GetGPUContext().get(), nullptr, scaledInfo, false);
455     }
456 
457     auto output = ScaleAndAddRandomColor(canvas, input, tmpBlur, src, dst, width, height);
458     return output;
459 }
460 
IsInputValid(Drawing::Canvas & canvas,const std::shared_ptr<Drawing::Image> & image,const Drawing::Rect & src,const Drawing::Rect & dst)461 bool GEMESABlurShaderFilter::IsInputValid(Drawing::Canvas& canvas, const std::shared_ptr<Drawing::Image>& image,
462     const Drawing::Rect& src, const Drawing::Rect& dst)
463 {
464     if (!g_blurEffect || !g_mixEffect || !image || !g_simpleFilter || (isGreyX_ && !g_greyAdjustEffect)) {
465         LOGE("GEMESABlurShaderFilter::IsInputValid invalid shader or image");
466         return false;
467     }
468     return true;
469 }
470 
BuildMatrix(const Drawing::Rect & src,const Drawing::ImageInfo & scaledInfo,const std::shared_ptr<Drawing::Image> & input) const471 Drawing::Matrix GEMESABlurShaderFilter::BuildMatrix(
472     const Drawing::Rect& src, const Drawing::ImageInfo& scaledInfo, const std::shared_ptr<Drawing::Image>& input) const
473 {
474     Drawing::Matrix blurMatrix;
475     blurMatrix.Translate(-src.GetLeft(), -src.GetTop());
476     int scaleWidth = scaledInfo.GetWidth();
477     int width = input->GetWidth();
478     float scaleW = static_cast<float>(scaleWidth) / (width > 0 ? width : 1);
479 
480     int scaleHeight = scaledInfo.GetHeight();
481     int height = input->GetHeight();
482     float scaleH = static_cast<float>(scaleHeight) / (height > 0 ? height : 1);
483     blurMatrix.PostScale(scaleW, scaleH);
484     return blurMatrix;
485 }
486 
BuildMiddleMatrix(const Drawing::ImageInfo & scaledInfo,const Drawing::ImageInfo & middleInfo) const487 Drawing::Matrix GEMESABlurShaderFilter::BuildMiddleMatrix(
488     const Drawing::ImageInfo& scaledInfo, const Drawing::ImageInfo& middleInfo) const
489 {
490     Drawing::Matrix blurMatrixA;
491     int width = middleInfo.GetWidth();
492     auto scaleW = static_cast<float>(scaledInfo.GetWidth()) / (width > 0 ? width : 1);
493     int height = middleInfo.GetHeight();
494     auto scaleH = static_cast<float>(scaledInfo.GetHeight()) / (height > 0 ? height : 1);
495     blurMatrixA.SetScale(scaleW, scaleH);
496     return blurMatrixA;
497 }
498 
BuildStretchMatrixFull(const Drawing::Rect & src,const Drawing::Rect & dst,int imageWidth,int imageHeight) const499 Drawing::Matrix GEMESABlurShaderFilter::BuildStretchMatrixFull(const Drawing::Rect& src,
500     const Drawing::Rect& dst, int imageWidth, int imageHeight) const
501 {
502     Drawing::Matrix matrix;
503     float scaleW = static_cast<float>((dst.GetWidth() - offsetX_ - offsetZ_)) / (imageWidth > 0 ? imageWidth : 1);
504     float scaleH = static_cast<float>((dst.GetHeight() - offsetY_ - offsetW_)) / (imageHeight > 0 ? imageHeight : 1);
505     matrix.Translate(-src.GetLeft(), -src.GetTop());
506     matrix.PostScale(scaleW, scaleH);
507 
508     Drawing::Matrix translateMatrix;
509     translateMatrix.Translate(dst.GetLeft() + offsetX_, dst.GetTop() + offsetY_);
510     matrix.PostConcat(translateMatrix);
511 
512     return matrix;
513 }
514 
BuildStretchMatrix(const Drawing::Rect & src,int imageWidth,int imageHeight) const515 Drawing::Matrix GEMESABlurShaderFilter::BuildStretchMatrix(const Drawing::Rect& src,
516     int imageWidth, int imageHeight) const
517 {
518     Drawing::Matrix matrix;
519     float scaleW = static_cast<float>((imageWidth - offsetX_ - offsetZ_)) / (imageWidth > 0 ? imageWidth : 1);
520     float scaleH = static_cast<float>((imageHeight - offsetY_ - offsetW_)) / (imageHeight > 0 ? imageHeight : 1);
521     matrix.Translate(-src.GetLeft(), -src.GetTop());
522     matrix.PostScale(scaleW, scaleH);
523 
524     Drawing::Matrix translateMatrix;
525     translateMatrix.Translate(offsetX_, offsetY_);
526     matrix.PostConcat(translateMatrix);
527 
528     return matrix;
529 }
530 
InitBlurEffect()531 bool GEMESABlurShaderFilter::InitBlurEffect()
532 {
533     if (g_blurEffect != nullptr) {
534         return true;
535     }
536 
537     static const std::string blurStringMESA(R"(
538         uniform shader imageInput;
539         uniform float2 in_blurOffset;
540 
541         half4 main(float2 xy) {
542             half4 c = imageInput.eval(float2(in_blurOffset.x + xy.x, in_blurOffset.y + xy.y));
543             c += imageInput.eval(float2(-in_blurOffset.y + xy.x, in_blurOffset.x + xy.y));
544             c += imageInput.eval(float2(-in_blurOffset.x + xy.x, -in_blurOffset.y + xy.y));
545             c += imageInput.eval(float2(in_blurOffset.y + xy.x, -in_blurOffset.x + xy.y));
546             return half4(c.rgba * 0.25);
547         }
548     )");
549 
550     g_blurEffect = Drawing::RuntimeEffect::CreateForShader(blurStringMESA);
551     if (g_blurEffect == nullptr) {
552         LOGE("GEMESABlurShaderFilter::RuntimeShader blurEffect create failed");
553         return false;
554     }
555 
556     return true;
557 }
558 
InitMixEffect()559 bool GEMESABlurShaderFilter::InitMixEffect()
560 {
561     if (g_mixEffect != nullptr) {
562         return true;
563     }
564 
565     static const std::string mixStringMESA(R"(
566         uniform shader blurredInput;
567         uniform float inColorFactor;
568 
569         highp float random(float2 xy) {
570             float t = dot(xy, float2(78.233, 12.9898));
571             return fract(sin(t) * 43758.5453);
572         }
573 
574         half4 main(float2 xy) {
575             highp float noiseGranularity = inColorFactor / 255.0;
576             half4 finalColor = blurredInput.eval(xy);
577             float noise  = mix(-noiseGranularity, noiseGranularity, random(xy));
578             finalColor.rgb += noise;
579             return finalColor;
580         }
581     )");
582 
583     g_mixEffect = Drawing::RuntimeEffect::CreateForShader(mixStringMESA);
584     if (g_mixEffect == nullptr) {
585         LOGE("GEMESABlurShaderFilter::RuntimeShader mixEffect create failed");
586         return false;
587     }
588 
589     return true;
590 }
591 
InitSimpleFilter()592 bool GEMESABlurShaderFilter::InitSimpleFilter()
593 {
594     if (g_simpleFilter != nullptr) {
595         return true;
596     }
597 
598     static const std::string simpleShader(R"(
599         uniform shader imageInput;
600         half4 main(float2 xy) {
601             return imageInput.eval(xy);
602         }
603     )");
604     g_simpleFilter = Drawing::RuntimeEffect::CreateForShader(simpleShader);
605     if (g_simpleFilter == nullptr) {
606         LOGE("GEMESABlurShaderFilter::RuntimeShader simpleFilter create failed");
607         return false;
608     }
609 
610     return true;
611 }
612 
InitGreyAdjustmentEffect()613 bool GEMESABlurShaderFilter::InitGreyAdjustmentEffect()
614 {
615     static const std::string greyXShader(R"(
616         uniform shader imageShader;
617         uniform float coefficient1;
618         uniform float coefficient2;
619 
620         float calculateT_y(float rgb) {
621             if (rgb > 127.5) { rgb = 255 - rgb; }
622             float A = 106.5;    // 3 * b - 3 * c + d;
623             float B = -93;      // 3 * (c - 2 * b);
624             float p = 0.816240163988;                   // (3 * A * C - pow(B, 2)) / (3 * pow(A, 2));
625             float s1 = rgb / 213.0 - 0.5 * 0.262253485943;    // -rgb/A - B*C/(3*pow(A,2)) + 2*pow(B,3)/(27*pow(A,3))
626             float s2 = sqrt(pow(s1, 2) + pow(p / 3, 3));
627             return pow((s1 + s2), 1.0 / 3) - pow((s2 - s1), 1.0 / 3) - (B / (3 * A));
628         }
629 
630         float calculateGreyAdjustY(float rgb) {
631             float t_r = calculateT_y(rgb);
632             return (rgb < 127.5) ? (coefficient1 * pow((1 - t_r), 3)) : (-coefficient2 * pow((1 - t_r), 3));
633         }
634 
635         vec4 main(vec2 drawing_coord) {
636             vec3 color = imageShader.eval(drawing_coord).rgb;
637             float Y = (0.299 * color.r + 0.587 * color.g + 0.114 * color.b) * 255;
638             float dY = calculateGreyAdjustY(Y) / 255.0;
639             return vec4(color+dY, 1.0);
640         }
641     )");
642     if (g_greyAdjustEffect == nullptr) {
643         g_greyAdjustEffect = Drawing::RuntimeEffect::CreateForShader(greyXShader);
644         if (g_greyAdjustEffect == nullptr) {
645             LOGE("GEMESABlurShaderFilter::RuntimeShader greyAdjustEffect create failed");
646             return false;
647         }
648     }
649     return true;
650 }
651 
CheckInputImage(Drawing::Canvas & canvas,const std::shared_ptr<Drawing::Image> & image,std::shared_ptr<Drawing::Image> & checkedImage,const Drawing::Rect & src) const652 void GEMESABlurShaderFilter::CheckInputImage(Drawing::Canvas& canvas, const std::shared_ptr<Drawing::Image>& image,
653     std::shared_ptr<Drawing::Image>& checkedImage, const Drawing::Rect& src) const
654 {
655     auto srcRect = Drawing::RectI(src.GetLeft(), src.GetTop(), src.GetRight(), src.GetBottom());
656     if (image->GetImageInfo().GetBound() != srcRect) {
657         auto resizedImage = std::make_shared<Drawing::Image>();
658         auto gpuCtx = canvas.GetGPUContext();
659         if (gpuCtx == nullptr || !(image->IsValid(gpuCtx.get()))) {
660             LOGE("GEMESABlurShaderFilter::CheckInputImage invalid image");
661             return;
662         }
663         if (resizedImage->BuildSubset(image, srcRect, *gpuCtx)) {
664             checkedImage = resizedImage;
665             LOGD("GEMESABlurShaderFilter::resize image success");
666         } else {
667             LOGD("GEMESABlurShaderFilter::resize image failed, use original image");
668         }
669     }
670 }
671 
OutputImageWithoutBlur(Drawing::Canvas & canvas,const std::shared_ptr<Drawing::Image> & image,const Drawing::Rect & src,const Drawing::Rect & dst) const672 std::shared_ptr<Drawing::Image> GEMESABlurShaderFilter::OutputImageWithoutBlur(Drawing::Canvas& canvas,
673     const std::shared_ptr<Drawing::Image>& image, const Drawing::Rect& src, const Drawing::Rect& dst) const
674 {
675     auto width = image->GetWidth();
676     auto height = image->GetHeight();
677     if (width == 0 || height == 0) {
678         return image;
679     }
680 
681     auto width_output = std::max(static_cast<int>(std::ceil(dst.GetWidth())), width);
682     auto height_output = std::max(static_cast<int>(std::ceil(dst.GetHeight())), height);
683     Drawing::Matrix inputMatrix = BuildStretchMatrixFull(src, dst, width, height);
684 
685     Drawing::SamplingOptions linear(Drawing::FilterMode::LINEAR, Drawing::MipmapMode::NONE);
686     auto imageInfo = Drawing::ImageInfo(width_output, height_output, image->GetImageInfo().GetColorType(),
687         image->GetImageInfo().GetAlphaType(), image->GetImageInfo().GetColorSpace());
688 
689     std::shared_ptr<Drawing::Image> output;
690     if (isGreyX_) {
691         Drawing::RuntimeShaderBuilder builder(g_greyAdjustEffect);
692         auto inputShader = Drawing::ShaderEffect::CreateImageShader(*image, tileMode_, tileMode_, linear, inputMatrix);
693         builder.SetChild("imageShader", inputShader);
694         builder.SetUniform("coefficient1", greyCoef1_);
695         builder.SetUniform("coefficient2", greyCoef2_);
696         output = builder.MakeImage(canvas.GetGPUContext().get(), nullptr, imageInfo, false);
697     } else {
698         Drawing::RuntimeShaderBuilder builder(g_simpleFilter);
699         auto inputShader = Drawing::ShaderEffect::CreateImageShader(*image, tileMode_, tileMode_, linear, inputMatrix);
700         builder.SetChild("imageInput", inputShader);
701         output = builder.MakeImage(canvas.GetGPUContext().get(), nullptr, imageInfo, false);
702     }
703     if (!output) {
704         LOGE("GEMESABlurShaderFilter::OutputImageWithoutBlur make image error");
705         return image;
706     } else {
707         return output;
708     }
709 }
710 
ScaleAndAddRandomColor(Drawing::Canvas & canvas,const std::shared_ptr<Drawing::Image> & image,const std::shared_ptr<Drawing::Image> & blurImage,const Drawing::Rect & src,const Drawing::Rect & dst,int & width,int & height) const711 std::shared_ptr<Drawing::Image> GEMESABlurShaderFilter::ScaleAndAddRandomColor(Drawing::Canvas& canvas,
712     const std::shared_ptr<Drawing::Image>& image, const std::shared_ptr<Drawing::Image>& blurImage,
713     const Drawing::Rect& src, const Drawing::Rect& dst, int& width, int& height) const
714 {
715     if (fabs(blurScale_) < 1e-6 || blurImage->GetWidth() < 1e-6 || blurImage->GetHeight() < 1e-6 ||
716         image->GetWidth() < 1e-6 || image->GetHeight() < 1e-6) {
717         LOGE("GEMESABlurShaderFilter::ScaleAndAddRandomColor invalid blurScale is zero.");
718         return blurImage;
719     }
720 
721     Drawing::RuntimeShaderBuilder mixBuilder(g_mixEffect);
722     Drawing::SamplingOptions linear(Drawing::FilterMode::LINEAR, Drawing::MipmapMode::NONE);
723     auto scaledInfo = Drawing::ImageInfo(width, height, blurImage->GetImageInfo().GetColorType(),
724         blurImage->GetImageInfo().GetAlphaType(), blurImage->GetImageInfo().GetColorSpace());
725     if (blurRadius_ >= BLUR_RADIUS_1P5) {
726         Drawing::Matrix scaleMatrix;
727         // blurImage->GetWidth() and blurImage->GetHeight() are larger than zero, checked before
728         float scaleW = static_cast<float>(dst.GetWidth()) / blurImage->GetWidth();
729         float scaleH = static_cast<float>(dst.GetHeight()) / blurImage->GetHeight();
730         scaleMatrix.SetScale(scaleW, scaleH);
731         Drawing::Matrix translateMatrix;
732         translateMatrix.Translate(dst.GetLeft(), dst.GetTop());
733         scaleMatrix.PostConcat(translateMatrix);
734         auto tmpShader = Drawing::ShaderEffect::CreateImageShader(*blurImage, Drawing::TileMode::CLAMP,
735             Drawing::TileMode::CLAMP, linear, scaleMatrix);
736         mixBuilder.SetChild("blurredInput", tmpShader);
737     } else {
738         Drawing::Rect srcRect(0.0f, 0.0f, static_cast<float>(blurImage->GetWidth()),
739             static_cast<float>(blurImage->GetHeight()));
740         const auto scaleMatrix = BuildStretchMatrixFull(srcRect, dst, blurImage->GetWidth(), blurImage->GetHeight());
741         auto tmpShader = Drawing::ShaderEffect::CreateImageShader(*blurImage, tileMode_,
742             tileMode_, linear, scaleMatrix);
743         mixBuilder.SetChild("blurredInput", tmpShader);
744     }
745     static auto factor = 1.75; // 1.75 from experience
746 
747     mixBuilder.SetUniform("inColorFactor", factor);
748     auto output = mixBuilder.MakeImage(canvas.GetGPUContext().get(), nullptr, scaledInfo, false);
749     return output;
750 }
751 
ComputeRadiusAndScale(int radius)752 void GEMESABlurShaderFilter::ComputeRadiusAndScale(int radius)
753 {
754     blurRadius_ = static_cast<float>(radius);
755     AdjustRadiusAndScale();
756     return;
757 }
758 
AdjustRadiusAndScale()759 void GEMESABlurShaderFilter::AdjustRadiusAndScale()
760 {
761     auto radius = static_cast<int>(blurRadius_);
762     if (radius < BLUR_RADIUS_1) {
763         blurScale_ = BASE_BLUR_SCALE;
764     } else if (radius < BLUR_RADIUS_1P5) {
765         blurScale_ = BLUR_SCALE_1;
766     } else if (radius < BLUR_RADIUS_2) {
767         blurScale_ = BLUR_SCALE_2;
768     } else if (radius < BLUR_RADIUS_3) {
769         blurScale_ = BLUR_SCALE_3;
770     } else {
771         blurScale_ = BLUR_SCALE_4;
772     }
773 }
774 
GetDescription() const775 std::string GEMESABlurShaderFilter::GetDescription() const
776 {
777     return "blur radius is " + std::to_string(blurRadius_);
778 }
779 
CalculatePixelStretch(int width,int height)780 void GEMESABlurShaderFilter::CalculatePixelStretch(int width, int height)
781 {
782     if (width_ > 0) {
783         offsetX_ = width * (stretchOffsetX_ / width_);
784         offsetZ_ = width * (stretchOffsetZ_ / width_);
785     } else {
786         offsetX_ = stretchOffsetX_;
787         offsetZ_ = stretchOffsetZ_;
788     }
789     if (height_ > 0) {
790         offsetY_ = height * (stretchOffsetY_ / height_);
791         offsetW_ = height * (stretchOffsetW_ / height_);
792     } else {
793         offsetY_ = stretchOffsetY_;
794         offsetW_ = stretchOffsetW_;
795     }
796 }
797 } // namespace Rosen
798 } // namespace OHOS
799