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_magnifier_shader_filter.h"
16 
17 #include "ge_log.h"
18 
19 namespace OHOS {
20 namespace Rosen {
21 
22 namespace {
23 constexpr static uint8_t COLOR_CHANNEL = 4; // 4 len of rgba
24 } // namespace
25 
26 std::shared_ptr<Drawing::RuntimeEffect> GEMagnifierShaderFilter::g_magnifierShaderEffect = nullptr;
27 
GEMagnifierShaderFilter(const Drawing::GEMagnifierShaderFilterParams & params)28 GEMagnifierShaderFilter::GEMagnifierShaderFilter(const Drawing::GEMagnifierShaderFilterParams& params)
29 {
30     magnifierPara_ = std::make_shared<GEMagnifierParams>();
31     if (!magnifierPara_) {
32         return;
33     }
34     magnifierPara_->factor_ = params.factor;
35     magnifierPara_->width_ = params.width;
36     magnifierPara_->height_ = params.height;
37     magnifierPara_->cornerRadius_ = params.cornerRadius;
38     magnifierPara_->borderWidth_ = params.borderWidth;
39     magnifierPara_->shadowOffsetX_ = params.shadowOffsetX;
40     magnifierPara_->shadowOffsetY_ = params.shadowOffsetY;
41     magnifierPara_->shadowSize_ = params.shadowSize;
42     magnifierPara_->shadowStrength_ = params.shadowStrength;
43     magnifierPara_->gradientMaskColor1_ = params.gradientMaskColor1;
44     magnifierPara_->gradientMaskColor2_ = params.gradientMaskColor2;
45     magnifierPara_->outerContourColor1_ = params.outerContourColor1;
46     magnifierPara_->outerContourColor2_ = params.outerContourColor2;
47     magnifierPara_->rotateDegree_ = params.rotateDegree;
48 }
49 
ProcessImage(Drawing::Canvas & canvas,const std::shared_ptr<Drawing::Image> image,const Drawing::Rect & src,const Drawing::Rect & dst)50 std::shared_ptr<Drawing::Image> GEMagnifierShaderFilter::ProcessImage(Drawing::Canvas& canvas,
51     const std::shared_ptr<Drawing::Image> image, const Drawing::Rect& src, const Drawing::Rect& dst)
52 {
53     if (image == nullptr || magnifierPara_ == nullptr) {
54         LOGE("GEMagnifierShaderFilter::ProcessImage image or para is null");
55         return image;
56     }
57 
58     Drawing::Matrix matrix;
59     matrix.Rotate(magnifierPara_->rotateDegree_, src.GetLeft() + src.GetWidth() / 2.0f,
60         src.GetTop() + src.GetHeight() / 2.0f); // 2.0 center of rect
61     auto imageShader = Drawing::ShaderEffect::CreateImageShader(*image, Drawing::TileMode::CLAMP,
62         Drawing::TileMode::CLAMP, Drawing::SamplingOptions(Drawing::FilterMode::LINEAR), matrix);
63     float imageWidth = image->GetWidth();
64     float imageHeight = image->GetHeight();
65     auto builder = MakeMagnifierShader(imageShader, imageWidth, imageHeight);
66     if (builder == nullptr) {
67         LOGE("GEMagnifierShaderFilter::ProcessImage builder is null");
68         return image;
69     }
70 
71     Drawing::Matrix invMatrix;
72     invMatrix.Rotate(-magnifierPara_->rotateDegree_, src.GetLeft() + src.GetWidth() / 2.0f,
73         src.GetTop() + src.GetHeight() / 2.0f); // 2.0 center of rect
74     auto resultImage = builder->MakeImage(canvas.GetGPUContext().get(), &invMatrix, image->GetImageInfo(), false);
75     if (resultImage == nullptr) {
76         LOGE("GEMagnifierShaderFilter::ProcessImage resultImage is null");
77         return image;
78     }
79 
80     return resultImage;
81 }
82 
MakeMagnifierShader(std::shared_ptr<Drawing::ShaderEffect> imageShader,float imageWidth,float imageHeight)83 std::shared_ptr<Drawing::RuntimeShaderBuilder> GEMagnifierShaderFilter::MakeMagnifierShader(
84     std::shared_ptr<Drawing::ShaderEffect> imageShader, float imageWidth, float imageHeight)
85 {
86     if (g_magnifierShaderEffect == nullptr) {
87         if (!InitMagnifierEffect()) {
88             LOGE("GEMagnifierShaderFilter::failed when initializing MagnifierEffect.");
89             return nullptr;
90         }
91     }
92 
93     if (magnifierPara_ == nullptr) {
94         return nullptr;
95     }
96     std::shared_ptr<Drawing::RuntimeShaderBuilder> builder =
97         std::make_shared<Drawing::RuntimeShaderBuilder>(g_magnifierShaderEffect);
98     builder->SetChild("imageShader", imageShader);
99     builder->SetUniform("iResolution", imageWidth, imageHeight);
100 
101     builder->SetUniform("factor", magnifierPara_->factor_);
102     builder->SetUniform("size", magnifierPara_->width_, magnifierPara_->height_);
103     builder->SetUniform("cornerRadius", magnifierPara_->cornerRadius_);
104     builder->SetUniform("borderWidth", magnifierPara_->borderWidth_);
105 
106     builder->SetUniform("shadowOffset", magnifierPara_->shadowOffsetX_, magnifierPara_->shadowOffsetY_);
107     builder->SetUniform("shadowSize", magnifierPara_->shadowSize_);
108     builder->SetUniform("shadowStrength", magnifierPara_->shadowStrength_);
109 
110     float maskColor1[COLOR_CHANNEL] = { 0.0f };
111     float maskColor2[COLOR_CHANNEL] = { 0.0f };
112     float outColor1[COLOR_CHANNEL] = { 0.0f };
113     float outColor2[COLOR_CHANNEL] = { 0.0f };
114     ConvertToRgba(magnifierPara_->gradientMaskColor1_, maskColor1, COLOR_CHANNEL);
115     ConvertToRgba(magnifierPara_->gradientMaskColor2_, maskColor2, COLOR_CHANNEL);
116     ConvertToRgba(magnifierPara_->outerContourColor1_, outColor1, COLOR_CHANNEL);
117     ConvertToRgba(magnifierPara_->outerContourColor2_, outColor2, COLOR_CHANNEL);
118     builder->SetUniform("gradientMaskColor1", maskColor1, COLOR_CHANNEL);
119     builder->SetUniform("gradientMaskColor2", maskColor2, COLOR_CHANNEL);
120     builder->SetUniform("outerContourColor1", outColor1, COLOR_CHANNEL);
121     builder->SetUniform("outerContourColor2", outColor2, COLOR_CHANNEL);
122 
123     return builder;
124 }
125 
InitMagnifierEffect()126 bool GEMagnifierShaderFilter::InitMagnifierEffect()
127 {
128     if (g_magnifierShaderEffect == nullptr) {
129         static constexpr char prog[] = R"(
130             uniform shader imageShader;
131             uniform float2 iResolution;
132 
133             uniform float factor;
134             uniform float borderWidth;
135             uniform float cornerRadius;
136             uniform float2 size;
137 
138             uniform float2 shadowOffset;
139             uniform float shadowSize;
140             uniform float shadowStrength;
141 
142             uniform vec4 gradientMaskColor1;
143             uniform vec4 gradientMaskColor2;
144             uniform vec4 outerContourColor1;
145             uniform vec4 outerContourColor2;
146 
147             // refraction
148             const float refractionStrength = 0.02;           // 0.02 refraction strength
149             const float epsilon = 1e-4;
150 
151             vec4 sdfRect(vec2 position, vec2 R1, float R2, float curvature, out float isInBorder)
152             {
153                 // calculate normal
154                 vec2 d = max(abs(position) - R1, 0.0);
155                 float dist = length(d) / R2;
156                 vec2 dir = normalize(sign(position) * d);
157                 float borderHeightRatio = min(size.x, size.y) / (borderWidth * 2.8); // 2.8 borderWidth
158                 float posInBorder = mix(1.0 - borderHeightRatio, 1.0, dist);
159                 float weight = max(posInBorder, 0.0);
160                 vec3 normal = normalize(mix(vec3(0.0, 0.0, 1.0), vec3(dir, 0.0), weight));
161                 isInBorder = smoothstep(0.0, 0.3, posInBorder); // 0.3 alpha threshold
162 
163                 // calculate shadow
164                 position -= shadowOffset / iResolution.x;
165                 float R2Shadow = R2 + 0.5 * shadowSize / iResolution.x; // 0.5 half of shader size
166                 float distShadow = length(max(abs(position) - R1, 0.)) / R2Shadow;
167                 float shadowSizeHeightRatio = min(size.x, size.y) / (shadowSize / (curvature + epsilon) * 2.0);
168                 float weightShadow = max(mix(1.0 - shadowSizeHeightRatio, 1.0, distShadow), 0.0);
169                 float shadow = mix(1.0 - shadowStrength, 1.0, min(abs(weightShadow - 0.5) * 2.0, 1.0)); // 0.5 2.0 num
170 
171                 return vec4(normal, shadow);
172             }
173 
174             vec4 main(float2 fragCoord)
175             {
176                 vec2 uv = fragCoord.xy / iResolution.x;
177                 vec2 boxPosition = iResolution / 2.0 / iResolution.x; // 2.0 center of rect
178                 vec2 halfBoxSize = size / iResolution.x / 2.0; // 2.0 half of resolution
179                 float curvature = cornerRadius / min(size.x, size.y) * 2.0; // 2.0 double of radius
180                 float mn = min(halfBoxSize.x, halfBoxSize.y) * (curvature + epsilon);
181 
182                 float isInBorder = 0;
183                 vec4 magnifyingGlass = sdfRect(uv - boxPosition, halfBoxSize - vec2(mn), mn, curvature, isInBorder);
184                 vec4 finalColor = vec4(outerContourColor1.xyz, 1.0);
185 
186                 // add refraction
187                 float red = magnifyingGlass.x;
188                 float green = magnifyingGlass.y;
189                 float offsetX = refractionStrength * sign(red) * red * red;
190                 float offsetY = -refractionStrength * sign(green) * green * green;
191                 vec2 sampleUV = (uv - boxPosition) / factor + boxPosition;
192                 vec4 refraction = imageShader.eval((sampleUV + vec2(offsetX, offsetY)) * iResolution.x);
193 
194                 // add gradient mask
195                 float yDistToCenter = (uv.y - boxPosition.y) / halfBoxSize.y;
196                 float yValue = (yDistToCenter + 1.0) / 2.0; // 2.0 half of height
197                 vec4 gradientMask = mix(gradientMaskColor1, gradientMaskColor2, yValue);
198                 refraction.xyz = mix(refraction.xyz, gradientMask.xyz, gradientMask.w);
199 
200                 // only apply refraction if z-value is not zero
201                 float mask = smoothstep(0.0, 0.3, magnifyingGlass.z); // 0.3 alpha threshold
202                 finalColor = mix(finalColor, refraction, mask);
203 
204                 // add outer_contour color
205                 float xValue = (uv.x - boxPosition.x) / halfBoxSize.x;
206                 vec4 gradientContour = mix(outerContourColor1, outerContourColor2, abs(xValue));
207                 finalColor.xyz = mix(finalColor.xyz, gradientContour.xyz, gradientContour.w * isInBorder * mask);
208 
209                 // add shadow
210                 finalColor.xyz *= magnifyingGlass.w;
211                 vec4 shadowColor = vec4(0.0, 0.0, 0.0, 1.0) * (1.0 - magnifyingGlass.w);
212                 finalColor = mix(shadowColor, finalColor, mask);
213 
214                 return finalColor;
215             }
216         )";
217 
218         g_magnifierShaderEffect = Drawing::RuntimeEffect::CreateForShader(prog);
219         if (g_magnifierShaderEffect == nullptr) {
220             LOGE("MakeMagnifierShader::RuntimeShader effect error\n");
221             return false;
222         }
223     }
224     return true;
225 }
226 
ConvertToRgba(uint32_t rgba,float * color,int tupleSize)227 void GEMagnifierShaderFilter::ConvertToRgba(uint32_t rgba, float* color, int tupleSize)
228 {
229     if (!color || tupleSize < 4) { // 4 len of rgba
230         return;
231     }
232     int16_t alpha = static_cast<int16_t>(rgba & 0xFF);               // 0xff byte mask
233     int16_t red = static_cast<int16_t>((rgba & 0xFF000000) >> 24);   // 0xff000000 red mask, 24 red shift
234     int16_t green = static_cast<int16_t>((rgba & 0x00FF0000) >> 16); // 0x00ff0000 green mask, 16 green shift
235     int16_t blue = static_cast<int16_t>((rgba & 0x0000FF00) >> 8);   // 0x0000ff00 blue mask, 8 blue shift
236 
237     color[0] = red * 1.0f / 255.0f;     // 255.0f is the max value, 0 red
238     color[1] = green * 1.0f / 255.0f;   // 255.0f is the max value, 1 green
239     color[2] = blue * 1.0f / 255.0f;    // 255.0f is the max value, 2 blue
240     color[3] = alpha * 1.0f / 255.0f;   // 255.0f is the max value, 3 alpha
241 }
242 
GetDescription() const243 const std::string GEMagnifierShaderFilter::GetDescription() const
244 {
245     return "GEMagnifierShaderFilter";
246 }
247 
248 } // namespace Rosen
249 } // namespace OHOS
250