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 #include "ge_linear_gradient_blur_shader_filter.h"
16 
17 #include "ge_log.h"
18 #include "ge_system_properties.h"
19 
20 namespace OHOS {
21 namespace Rosen {
22 
23 namespace {
24 constexpr static float FLOAT_ZERO_THRESHOLD = 0.001f;
25 constexpr static uint8_t DIRECTION_NUM = 4;
26 
GetMaskLinearBlurEnabled()27 static bool GetMaskLinearBlurEnabled()
28 {
29 #ifdef GE_OHOS
30     // Determine whether the mask LinearBlur render should be enabled. The default value is 0,
31     // which means that it is unenabled.
32     static bool enabled =
33         std::atoi((system::GetParameter("persist.sys.graphic.maskLinearBlurEnabled", "1")).c_str()) != 0;
34     return enabled;
35 #else
36     return false;
37 #endif
38 }
39 } // namespace
40 
41 std::shared_ptr<Drawing::RuntimeEffect> GELinearGradientBlurShaderFilter::horizontalMeanBlurShaderEffect_ = nullptr;
42 std::shared_ptr<Drawing::RuntimeEffect> GELinearGradientBlurShaderFilter::verticalMeanBlurShaderEffect_ = nullptr;
43 std::shared_ptr<Drawing::RuntimeEffect> GELinearGradientBlurShaderFilter::maskBlurShaderEffect_ = nullptr;
44 
GELinearGradientBlurShaderFilter(const Drawing::GELinearGradientBlurShaderFilterParams & params)45 GELinearGradientBlurShaderFilter::GELinearGradientBlurShaderFilter(
46     const Drawing::GELinearGradientBlurShaderFilterParams& params)
47 {
48     geoWidth_ = params.geoWidth;
49     geoHeight_ = params.geoHeight;
50     auto maskLinearBlur = GetMaskLinearBlurEnabled();
51     linearGradientBlurPara_ = std::make_shared<GELinearGradientBlurPara>(
52         params.blurRadius, params.fractionStops, static_cast<GEGradientDirection>(params.direction), maskLinearBlur);
53     mat_ = params.mat;
54     tranX_ = params.tranX;
55     tranY_ = params.tranY;
56     isOffscreenCanvas_ = params.isOffscreenCanvas;
57 }
58 
ProcessImageDDGR(Drawing::Canvas & canvas,const std::shared_ptr<Drawing::Image> image,uint8_t directionBias)59 std::shared_ptr<Drawing::Image> GELinearGradientBlurShaderFilter::ProcessImageDDGR(
60     Drawing::Canvas& canvas, const std::shared_ptr<Drawing::Image> image, uint8_t directionBias)
61 {
62     auto& para = linearGradientBlurPara_;
63     auto clipIPadding = Drawing::Rect(0, 0, geoWidth_ * imageScale_, geoHeight_ * imageScale_);
64     uint8_t direction = static_cast<uint8_t>(para->direction_);
65     TransformGradientBlurDirection(direction, directionBias);
66     float radius = para->blurRadius_;
67 
68     Drawing::Brush brush;
69     Drawing::Filter imageFilter;
70     Drawing::GradientBlurType blurType;
71     if (GetMaskLinearBlurEnabled() && para->useMaskAlgorithm_) {
72         blurType = Drawing::GradientBlurType::ALPHA_BLEND;
73         radius /= 2; // 2: half radius.
74     } else {
75         radius -= GELinearGradientBlurPara::ORIGINAL_BASE;
76         radius = std::clamp(radius, 0.0f, 60.0f); // 60.0 represents largest blur radius
77         blurType = Drawing::GradientBlurType::RADIUS_GRADIENT;
78     }
79     imageFilter.SetImageFilter(Drawing::ImageFilter::CreateGradientBlurImageFilter(
80         radius, para->fractionStops_, static_cast<Drawing::GradientDir>(direction), blurType, nullptr));
81     brush.SetFilter(imageFilter);
82 
83     canvas.AttachBrush(brush);
84     Drawing::Rect rect = clipIPadding;
85     rect.Offset(-clipIPadding.GetLeft(), -clipIPadding.GetTop());
86     canvas.DrawImageRect(
87         *image, rect, clipIPadding, Drawing::SamplingOptions(), Drawing::SrcRectConstraint::FAST_SRC_RECT_CONSTRAINT);
88     canvas.DetachBrush();
89     return image;
90 }
91 
ProcessImage(Drawing::Canvas & canvas,const std::shared_ptr<Drawing::Image> image,const Drawing::Rect & src,const Drawing::Rect & dst)92 std::shared_ptr<Drawing::Image> GELinearGradientBlurShaderFilter::ProcessImage(Drawing::Canvas& canvas,
93     const std::shared_ptr<Drawing::Image> image, const Drawing::Rect& src, const Drawing::Rect& dst)
94 {
95     auto& para = linearGradientBlurPara_;
96     if (!image || para == nullptr || para->blurRadius_ <= 0) {
97         return image;
98     }
99     LOGD("GELinearGradientBlurShaderFilter::DrawImageRect%{public}f,  %{public}f, %{public}f, %{public}f, %{public}f "
100          "%{public}d", para->blurRadius_, geoWidth_, geoHeight_, tranX_, tranY_, (int)isOffscreenCanvas_);
101 
102     ComputeScale(dst.GetWidth(), dst.GetHeight(), para->useMaskAlgorithm_);
103     auto clipIPadding = Drawing::Rect(0, 0, geoWidth_ * imageScale_, geoHeight_ * imageScale_);
104     uint8_t directionBias = 0;
105     auto alphaGradientShader = MakeAlphaGradientShader(clipIPadding, para, directionBias);
106     if (alphaGradientShader == nullptr) {
107         LOGE("GELinearGradientBlurShaderFilter::DrawImageRect alphaGradientShader null");
108         return image;
109     }
110 
111     if (GetMaskLinearBlurEnabled() && para->useMaskAlgorithm_) {
112         // use faster LinearGradientBlur if valid
113         if (para->linearGradientBlurFilter_ == nullptr) {
114             LOGE("RSPropertiesPainter::DrawLinearGradientBlur blurFilter null");
115             return image;
116         }
117 
118         const auto& RSFilter = para->linearGradientBlurFilter_;
119         auto filter = RSFilter;
120         return DrawMaskLinearGradientBlur(image, canvas, filter, alphaGradientShader, dst);
121     } else {
122         // use original LinearGradientBlur
123         float radius = para->blurRadius_ - GELinearGradientBlurPara::ORIGINAL_BASE;
124         radius = std::clamp(radius, 0.0f, 60.0f); // 60.0 represents largest blur radius
125         radius = radius / 2 * imageScale_;        // 2 half blur radius
126         MakeHorizontalMeanBlurEffect();
127         MakeVerticalMeanBlurEffect();
128         DrawMeanLinearGradientBlur(image, canvas, radius, alphaGradientShader, dst);
129         return image;
130     }
131 }
132 
ComputeScale(float width,float height,bool useMaskAlgorithm)133 void GELinearGradientBlurShaderFilter::ComputeScale(float width, float height, bool useMaskAlgorithm)
134 {
135     if (GetMaskLinearBlurEnabled() && useMaskAlgorithm) {
136         imageScale_ = 1.0f;
137     } else {
138         if (width * height < 10000) { // 10000 for 100 * 100 resolution
139             imageScale_ = 0.7f;       // 0.7 for scale
140         } else {
141             imageScale_ = 0.5f; // 0.5 for scale
142         }
143     }
144 }
145 
CalcDirectionBias(const Drawing::Matrix & mat)146 uint8_t GELinearGradientBlurShaderFilter::CalcDirectionBias(const Drawing::Matrix& mat)
147 {
148     uint8_t directionBias = 0;
149     // 1 and 3 represents rotate matrix's index
150     if ((mat.Get(1) > FLOAT_ZERO_THRESHOLD) && (mat.Get(3) < (0 - FLOAT_ZERO_THRESHOLD))) {
151         directionBias = 1; // 1 represents rotate 90 degree
152         // 0 and 4 represents rotate matrix's index
153     } else if ((mat.Get(0) < (0 - FLOAT_ZERO_THRESHOLD)) && (mat.Get(4) < (0 - FLOAT_ZERO_THRESHOLD))) {
154         directionBias = 2; // 2 represents rotate 180 degree
155         // 1 and 3 represents rotate matrix's index
156     } else if ((mat.Get(1) < (0 - FLOAT_ZERO_THRESHOLD)) && (mat.Get(3) > FLOAT_ZERO_THRESHOLD)) {
157         directionBias = 3; // 3 represents rotate 270 degree
158     }
159     return directionBias;
160 }
161 
TransformGradientBlurDirection(uint8_t & direction,const uint8_t directionBias)162 void GELinearGradientBlurShaderFilter::TransformGradientBlurDirection(uint8_t& direction, const uint8_t directionBias)
163 {
164     if (direction == static_cast<uint8_t>(GEGradientDirection::LEFT_BOTTOM)) {
165         direction += 2; // 2 is used to transtorm diagnal direction.
166     } else if (direction == static_cast<uint8_t>(GEGradientDirection::RIGHT_TOP) ||
167                direction == static_cast<uint8_t>(GEGradientDirection::RIGHT_BOTTOM)) {
168         direction -= 1; // 1 is used to transtorm diagnal direction.
169     }
170     if (direction <= static_cast<uint8_t>(GEGradientDirection::BOTTOM)) {
171         if (direction < directionBias) {
172             direction += DIRECTION_NUM;
173         }
174         direction -= directionBias;
175     } else {
176         direction -= DIRECTION_NUM;
177         if (direction < directionBias) {
178             direction += DIRECTION_NUM;
179         }
180         direction -= directionBias;
181         direction += DIRECTION_NUM;
182     }
183     if (direction == static_cast<uint8_t>(GEGradientDirection::RIGHT_BOTTOM)) {
184         direction -= 2; // 2 is used to restore diagnal direction.
185     } else if (direction == static_cast<uint8_t>(GEGradientDirection::LEFT_BOTTOM) ||
186                direction == static_cast<uint8_t>(GEGradientDirection::RIGHT_TOP)) {
187         direction += 1; // 1 is used to restore diagnal direction.
188     }
189 }
190 
GetGEGradientDirectionPoints(Drawing::Point (& pts)[2],const Drawing::Rect & clipBounds,GEGradientDirection direction)191 bool GELinearGradientBlurShaderFilter::GetGEGradientDirectionPoints(
192     Drawing::Point (&pts)[2], const Drawing::Rect& clipBounds, GEGradientDirection direction) // 2 size of points
193 {
194     switch (direction) {
195         case GEGradientDirection::BOTTOM: {
196             pts[0].Set(clipBounds.GetWidth() / 2 + clipBounds.GetLeft(), clipBounds.GetTop()); // 2 middle of width;
197             pts[1].Set(clipBounds.GetWidth() / 2 + clipBounds.GetLeft(), clipBounds.GetBottom()); // 2  middle of width;
198             break;
199         }
200         case GEGradientDirection::TOP: {
201             pts[0].Set(clipBounds.GetWidth() / 2 + clipBounds.GetLeft(), clipBounds.GetBottom()); // 2  middle of width;
202             pts[1].Set(clipBounds.GetWidth() / 2 + clipBounds.GetLeft(), clipBounds.GetTop());    // 2  middle of width;
203             break;
204         }
205         case GEGradientDirection::RIGHT: {
206             pts[0].Set(clipBounds.GetLeft(), clipBounds.GetHeight() / 2 + clipBounds.GetTop()); // 2  middle of height;
207             pts[1].Set(clipBounds.GetRight(),
208                 clipBounds.GetHeight() / 2 + clipBounds.GetTop()); // 2  middle of height;
209             break;
210         }
211         case GEGradientDirection::LEFT: {
212             pts[0].Set(clipBounds.GetRight(),
213                 clipBounds.GetHeight() / 2 + clipBounds.GetTop());                              // 2  middle of height;
214             pts[1].Set(clipBounds.GetLeft(), clipBounds.GetHeight() / 2 + clipBounds.GetTop()); // 2  middle of height;
215             break;
216         }
217         default: {
218         }
219     }
220     return ProcessGradientDirectionPoints(pts, clipBounds, direction);
221 }
222 
ProcessGradientDirectionPoints(Drawing::Point (& pts)[2],const Drawing::Rect & clipBounds,GEGradientDirection direction)223 bool GELinearGradientBlurShaderFilter::ProcessGradientDirectionPoints(
224     Drawing::Point (&pts)[2], const Drawing::Rect& clipBounds, GEGradientDirection direction)  // 2 size of points
225 {
226     switch (direction) {
227         case GEGradientDirection::RIGHT_BOTTOM: {
228             pts[0].Set(clipBounds.GetLeft(), clipBounds.GetTop());
229             pts[1].Set(clipBounds.GetRight(), clipBounds.GetBottom());
230             break;
231         }
232         case GEGradientDirection::LEFT_TOP: {
233             pts[0].Set(clipBounds.GetRight(), clipBounds.GetBottom());
234             pts[1].Set(clipBounds.GetLeft(), clipBounds.GetTop());
235             break;
236         }
237         case GEGradientDirection::LEFT_BOTTOM: {
238             pts[0].Set(clipBounds.GetRight(), clipBounds.GetTop());
239             pts[1].Set(clipBounds.GetLeft(), clipBounds.GetBottom());
240             break;
241         }
242         case GEGradientDirection::RIGHT_TOP: {
243             pts[0].Set(clipBounds.GetLeft(), clipBounds.GetBottom());
244             pts[1].Set(clipBounds.GetRight(), clipBounds.GetTop());
245             break;
246         }
247         default: {
248         }
249     }
250     Drawing::Matrix pointsMat = mat_;
251     if (isOffscreenCanvas_) {
252         pointsMat.PostTranslate(-tranX_, -tranY_);
253     }
254     std::vector<Drawing::Point> points(pts, pts + 2); // 2 size of pts
255     pointsMat.MapPoints(points, points, points.size());
256     pts[0].Set(points[0].GetX(), points[0].GetY());
257     pts[1].Set(points[1].GetX(), points[1].GetY());
258     return true;
259 }
260 
MakeAlphaGradientShader(const Drawing::Rect & clipBounds,const std::shared_ptr<GELinearGradientBlurPara> & para,uint8_t directionBias)261 std::shared_ptr<Drawing::ShaderEffect> GELinearGradientBlurShaderFilter::MakeAlphaGradientShader(
262     const Drawing::Rect& clipBounds, const std::shared_ptr<GELinearGradientBlurPara>& para, uint8_t directionBias)
263 {
264     std::vector<Drawing::ColorQuad> c;
265     std::vector<Drawing::scalar> p;
266     Drawing::Point pts[2];  // 2 size of points
267 
268     uint8_t direction = static_cast<uint8_t>(para->direction_);
269     if (directionBias != 0) {
270         TransformGradientBlurDirection(direction, directionBias);
271     }
272     bool result = GetGEGradientDirectionPoints(pts, clipBounds, static_cast<GEGradientDirection>(direction));
273     if (!result) {
274         return nullptr;
275     }
276     uint8_t ColorMax = 255; // 255 max number of color
277     uint8_t ColorMin = 0;
278     if (para->fractionStops_[0].second > 0.01) { // 0.01 represents the fraction bias
279         c.emplace_back(Drawing::Color::ColorQuadSetARGB(ColorMin, ColorMax, ColorMax, ColorMax));
280         p.emplace_back(para->fractionStops_[0].second - 0.01); // 0.01 represents the fraction bias
281     }
282     for (size_t i = 0; i < para->fractionStops_.size(); i++) {
283         c.emplace_back(Drawing::Color::ColorQuadSetARGB(
284             static_cast<uint8_t>(para->fractionStops_[i].first * ColorMax), ColorMax, ColorMax, ColorMax));
285         p.emplace_back(para->fractionStops_[i].second);
286     }
287     // 0.01 represents the fraction bias
288     if (para->fractionStops_[para->fractionStops_.size() - 1].second < (1 - 0.01)) {
289         c.emplace_back(Drawing::Color::ColorQuadSetARGB(ColorMin, ColorMax, ColorMax, ColorMax));
290         // 0.01 represents the fraction bias
291         p.emplace_back(para->fractionStops_[para->fractionStops_.size() - 1].second + 0.01);
292     }
293     return Drawing::ShaderEffect::CreateLinearGradient(pts[0], pts[1], c, p, Drawing::TileMode::CLAMP);
294 }
295 
MakeHorizontalMeanBlurEffect()296 void GELinearGradientBlurShaderFilter::MakeHorizontalMeanBlurEffect()
297 {
298     static const std::string HorizontalBlurString(
299         R"(
300         uniform half r;
301         uniform shader imageShader;
302         uniform shader gradientShader;
303         half4 meanFilter(float2 coord, half radius)
304         {
305             half4 sum = vec4(0.0);
306             half div = 0;
307             for (half x = -30.0; x < 30.0; x += 1.0) {
308                 if (x > radius) {
309                     break;
310                 }
311                 if (abs(x) < radius) {
312                     div += 1;
313                     sum += imageShader.eval(coord + float2(x, 0));
314                 }
315             }
316             return half4(sum.xyz / div, 1.0);
317         }
318         half4 main(float2 coord)
319         {
320             if (abs(gradientShader.eval(coord).a - 0) < 0.001) {
321                 return imageShader.eval(coord);
322             }
323             float val = clamp(r * gradientShader.eval(coord).a, 1.0, r);
324             return meanFilter(coord, val);
325         }
326     )");
327 
328     if (horizontalMeanBlurShaderEffect_ == nullptr) {
329         horizontalMeanBlurShaderEffect_ = Drawing::RuntimeEffect::CreateForShader(HorizontalBlurString);
330     }
331 }
332 
MakeVerticalMeanBlurEffect()333 void GELinearGradientBlurShaderFilter::MakeVerticalMeanBlurEffect()
334 {
335     static const std::string VerticalBlurString(
336         R"(
337         uniform half r;
338         uniform shader imageShader;
339         uniform shader gradientShader;
340         half4 meanFilter(float2 coord, half radius)
341         {
342             half4 sum = vec4(0.0);
343             half div = 0;
344             for (half y = -30.0; y < 30.0; y += 1.0) {
345                 if (y > radius) {
346                     break;
347                 }
348                 if (abs(y) < radius) {
349                     div += 1;
350                     sum += imageShader.eval(coord + float2(0, y));
351                 }
352             }
353             return half4(sum.xyz / div, 1.0);
354         }
355         half4 main(float2 coord)
356         {
357             if (abs(gradientShader.eval(coord).a - 0) < 0.001) {
358                 return imageShader.eval(coord);
359             }
360             float val = clamp(r * gradientShader.eval(coord).a, 1.0, r);
361             return meanFilter(coord, val);
362         }
363     )");
364 
365     if (verticalMeanBlurShaderEffect_ == nullptr) {
366         verticalMeanBlurShaderEffect_ = Drawing::RuntimeEffect::CreateForShader(VerticalBlurString);
367     }
368 }
369 
DrawMeanLinearGradientBlur(const std::shared_ptr<Drawing::Image> & image,Drawing::Canvas & canvas,float radius,std::shared_ptr<Drawing::ShaderEffect> alphaGradientShader,const Drawing::Rect & dst)370 void GELinearGradientBlurShaderFilter::DrawMeanLinearGradientBlur(const std::shared_ptr<Drawing::Image>& image,
371     Drawing::Canvas& canvas, float radius, std::shared_ptr<Drawing::ShaderEffect> alphaGradientShader,
372     const Drawing::Rect& dst)
373 {
374     if (!horizontalMeanBlurShaderEffect_ || !verticalMeanBlurShaderEffect_ || !image) {
375         return;
376     }
377 
378     if (imageScale_ < 1e-6) {
379         return;
380     }
381 
382     Drawing::Matrix m;
383     Drawing::Matrix blurMatrix;
384     blurMatrix.PostScale(imageScale_, imageScale_);
385     blurMatrix.PostTranslate(dst.GetLeft(), dst.GetTop());
386 
387     Drawing::SamplingOptions linear(Drawing::FilterMode::LINEAR, Drawing::MipmapMode::NONE);
388 
389     auto tmpBlur4 = BuildMeanLinearGradientBlur(image, canvas, radius, alphaGradientShader, blurMatrix);
390 
391     float invBlurScale = 1.0f / imageScale_;
392     Drawing::Matrix invBlurMatrix;
393     invBlurMatrix.PostScale(invBlurScale, invBlurScale);
394     auto blurShader = Drawing::ShaderEffect::CreateImageShader(
395         *tmpBlur4, Drawing::TileMode::CLAMP, Drawing::TileMode::CLAMP, linear, invBlurMatrix);
396 
397     Drawing::Brush brush;
398     brush.SetShaderEffect(blurShader);
399     canvas.AttachBrush(brush);
400     canvas.DrawRect(dst);
401     canvas.DetachBrush();
402 }
403 
BuildMeanLinearGradientBlur(const std::shared_ptr<Drawing::Image> & image,Drawing::Canvas & canvas,float radius,std::shared_ptr<Drawing::ShaderEffect> alphaGradientShader,Drawing::Matrix blurMatrix)404 std::shared_ptr<Drawing::Image> GELinearGradientBlurShaderFilter::BuildMeanLinearGradientBlur(
405     const std::shared_ptr<Drawing::Image>& image, Drawing::Canvas& canvas, float radius,
406     std::shared_ptr<Drawing::ShaderEffect> alphaGradientShader, Drawing::Matrix blurMatrix)
407 {
408     auto width = image->GetWidth();
409     auto height = image->GetHeight();
410     auto originImageInfo = image->GetImageInfo();
411     auto scaledInfo = Drawing::ImageInfo(std::ceil(width * imageScale_), std::ceil(height * imageScale_),
412         originImageInfo.GetColorType(), originImageInfo.GetAlphaType(), originImageInfo.GetColorSpace());
413     Drawing::Matrix m;
414     Drawing::SamplingOptions linear(Drawing::FilterMode::LINEAR, Drawing::MipmapMode::NONE);
415     Drawing::RuntimeShaderBuilder hBlurBuilder(horizontalMeanBlurShaderEffect_);
416     hBlurBuilder.SetUniform("r", radius);
417     auto shader1 = Drawing::ShaderEffect::CreateImageShader(
418         *image, Drawing::TileMode::CLAMP, Drawing::TileMode::CLAMP, linear, blurMatrix);
419     hBlurBuilder.SetChild("imageShader", shader1);
420     hBlurBuilder.SetChild("gradientShader", alphaGradientShader);
421     std::shared_ptr<Drawing::Image> tmpBlur(
422         hBlurBuilder.MakeImage(canvas.GetGPUContext().get(), nullptr, scaledInfo, false));
423 
424     Drawing::RuntimeShaderBuilder vBlurBuilder(verticalMeanBlurShaderEffect_);
425     vBlurBuilder.SetUniform("r", radius);
426     auto tmpBlurShader = Drawing::ShaderEffect::CreateImageShader(
427         *tmpBlur, Drawing::TileMode::CLAMP, Drawing::TileMode::CLAMP, linear, m);
428     vBlurBuilder.SetChild("imageShader", tmpBlurShader);
429     vBlurBuilder.SetChild("gradientShader", alphaGradientShader);
430     std::shared_ptr<Drawing::Image> tmpBlur2(
431         vBlurBuilder.MakeImage(canvas.GetGPUContext().get(), nullptr, scaledInfo, false));
432 
433     auto tmpBlur2Shader = Drawing::ShaderEffect::CreateImageShader(
434         *tmpBlur2, Drawing::TileMode::CLAMP, Drawing::TileMode::CLAMP, linear, m);
435     hBlurBuilder.SetChild("imageShader", tmpBlur2Shader);
436     std::shared_ptr<Drawing::Image> tmpBlur3(
437         hBlurBuilder.MakeImage(canvas.GetGPUContext().get(), nullptr, scaledInfo, false));
438 
439     auto tmpBlur3Shader = Drawing::ShaderEffect::CreateImageShader(
440         *tmpBlur3, Drawing::TileMode::CLAMP, Drawing::TileMode::CLAMP, linear, m);
441     vBlurBuilder.SetChild("imageShader", tmpBlur3Shader);
442     std::shared_ptr<Drawing::Image> tmpBlur4(
443         vBlurBuilder.MakeImage(canvas.GetGPUContext().get(), nullptr, scaledInfo, false));
444     return tmpBlur4;
445 }
446 
DrawMaskLinearGradientBlur(const std::shared_ptr<Drawing::Image> & image,Drawing::Canvas & canvas,std::shared_ptr<GEShaderFilter> & blurFilter,std::shared_ptr<Drawing::ShaderEffect> alphaGradientShader,const Drawing::Rect & dst)447 std::shared_ptr<Drawing::Image> GELinearGradientBlurShaderFilter::DrawMaskLinearGradientBlur(
448     const std::shared_ptr<Drawing::Image>& image, Drawing::Canvas& canvas, std::shared_ptr<GEShaderFilter>& blurFilter,
449     std::shared_ptr<Drawing::ShaderEffect> alphaGradientShader, const Drawing::Rect& dst)
450 {
451     if (image == nullptr) {
452         LOGE("GELinearGradientBlurShaderFilter::DrawMaskLinearGradientBlur image is null");
453         return image;
454     }
455 
456     auto imageInfo = image->GetImageInfo();
457     if (imageInfo.GetWidth() < 1e-6 || imageInfo.GetHeight() < 1e-6) {
458         return image;
459     }
460     auto srcRect = Drawing::Rect(0, 0, imageInfo.GetWidth(), imageInfo.GetHeight());
461     auto blurImage = blurFilter->ProcessImage(canvas, image, srcRect, dst);
462 
463     Drawing::Matrix matrix;
464     Drawing::Matrix inputMatrix;
465     inputMatrix.Translate(dst.GetLeft(), dst.GetTop());
466     inputMatrix.PostScale(dst.GetWidth() / imageInfo.GetWidth(), dst.GetHeight() / imageInfo.GetHeight());
467 
468     auto srcImageShader = Drawing::ShaderEffect::CreateImageShader(*image, Drawing::TileMode::CLAMP,
469         Drawing::TileMode::CLAMP, Drawing::SamplingOptions(Drawing::FilterMode::LINEAR), inputMatrix);
470     auto blurImageShader = Drawing::ShaderEffect::CreateImageShader(*blurImage, Drawing::TileMode::CLAMP,
471         Drawing::TileMode::CLAMP, Drawing::SamplingOptions(Drawing::FilterMode::LINEAR), matrix);
472     auto builder = MakeMaskLinearGradientBlurShader(srcImageShader, blurImageShader, alphaGradientShader);
473     auto outImageInfo = Drawing::ImageInfo(dst.GetWidth(), dst.GetHeight(), blurImage->GetImageInfo().GetColorType(),
474         blurImage->GetImageInfo().GetAlphaType(), blurImage->GetImageInfo().GetColorSpace());
475     auto outImage = builder->MakeImage(canvas.GetGPUContext().get(), nullptr, outImageInfo, false);
476 
477     return outImage;
478 }
479 
MakeMaskLinearGradientBlurShader(std::shared_ptr<Drawing::ShaderEffect> srcImageShader,std::shared_ptr<Drawing::ShaderEffect> blurImageShader,std::shared_ptr<Drawing::ShaderEffect> gradientShader)480 std::shared_ptr<Drawing::RuntimeShaderBuilder> GELinearGradientBlurShaderFilter::MakeMaskLinearGradientBlurShader(
481     std::shared_ptr<Drawing::ShaderEffect> srcImageShader, std::shared_ptr<Drawing::ShaderEffect> blurImageShader,
482     std::shared_ptr<Drawing::ShaderEffect> gradientShader)
483 {
484     if (maskBlurShaderEffect_ == nullptr) {
485         static const char* prog = R"(
486             uniform shader srcImageShader;
487             uniform shader blurImageShader;
488             uniform shader gradientShader;
489             half4 meanFilter(float2 coord)
490             {
491                 vec3 srcColor = vec3(srcImageShader.eval(coord).r,
492                     srcImageShader.eval(coord).g, srcImageShader.eval(coord).b);
493                 vec3 blurColor = vec3(blurImageShader.eval(coord).r,
494                     blurImageShader.eval(coord).g, blurImageShader.eval(coord).b);
495                 float gradient = gradientShader.eval(coord).a;
496 
497                 vec3 color = blurColor * gradient + srcColor * (1 - gradient);
498                 return vec4(color, 1.0);
499             }
500             half4 main(float2 coord)
501             {
502                 if (abs(gradientShader.eval(coord).a) < 0.001) {
503                     return srcImageShader.eval(coord);
504                 }
505 
506                 if (abs(gradientShader.eval(coord).a) > 0.999) {
507                     return blurImageShader.eval(coord);
508                 }
509 
510                 return meanFilter(coord);
511             }
512         )";
513         maskBlurShaderEffect_ = Drawing::RuntimeEffect::CreateForShader(prog);
514         if (maskBlurShaderEffect_ == nullptr) {
515             return nullptr;
516         }
517     }
518 
519     auto builder = std::make_shared<Drawing::RuntimeShaderBuilder>(maskBlurShaderEffect_);
520     builder->SetChild("srcImageShader", srcImageShader);
521     builder->SetChild("blurImageShader", blurImageShader);
522     builder->SetChild("gradientShader", gradientShader);
523     return builder;
524 }
525 
GetDescription()526 std::string GELinearGradientBlurShaderFilter::GetDescription()
527 {
528     return "GELinearGradientBlurShaderFilter";
529 }
530 
GetDetailedDescription()531 std::string GELinearGradientBlurShaderFilter::GetDetailedDescription()
532 {
533     if (!linearGradientBlurPara_) {
534         return "GELinearGradientBlurShaderFilterBlur, radius: unavailable";
535     }
536     return "GELinearGradientBlurShaderFilterBlur, radius: " + std::to_string(linearGradientBlurPara_->blurRadius_);
537 }
538 } // namespace Rosen
539 } // namespace OHOS
540