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