1 /*
2 * Copyright (c) 2025 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 <chrono>
17
18 #include "ge_log.h"
19 #include "ge_radial_gradient_shader_mask.h"
20 #include "utils/ge_trace.h"
21
22 namespace OHOS {
23 namespace Rosen {
24 namespace Drawing {
25
26 namespace {
27 constexpr static uint8_t SIZE_ARRAY = 12; // 12 max length
28 // max radius should equals height of image, 2.0 is a scale from diameter to radius.
29 constexpr static float RADIUS_SCALE = 2.0f;
30 } // namespace
31
GERadialGradientShaderMask(GERadialGradientShaderMaskParams param)32 GERadialGradientShaderMask::GERadialGradientShaderMask(GERadialGradientShaderMaskParams param) : param_(param) {}
33
GenerateDrawingShader(float width,float height) const34 std::shared_ptr<ShaderEffect> GERadialGradientShaderMask::GenerateDrawingShader(float width, float height) const
35 {
36 GE_TRACE_NAME_FMT("GERadialGradientShaderMask::GenerateDrawingShader, Type: %s, Width: %g, Height: %g",
37 Drawing::GE_MASK_RADIAL_GRADIENT, width, height);
38 std::shared_ptr<Drawing::RuntimeShaderBuilder> builder = nullptr;
39 builder = GetRadialGradientShaderMaskBuilder();
40 if (!builder) {
41 LOGE("GERadialGradientShaderMask::GenerateDrawingShaderHas builder error");
42 return nullptr;
43 }
44 auto radialGradientMaskEffectShader = GenerateShaderEffect(width, height, builder);
45
46 return radialGradientMaskEffectShader;
47 }
48
GetRadialGradientShaderMaskBuilder() const49 std::shared_ptr<Drawing::RuntimeShaderBuilder> GERadialGradientShaderMask::GetRadialGradientShaderMaskBuilder() const
50 {
51 thread_local std::shared_ptr<Drawing::RuntimeShaderBuilder> radialGradientShaderMaskBuilder = nullptr;
52 if (radialGradientShaderMaskBuilder) {
53 return radialGradientShaderMaskBuilder;
54 }
55
56 static constexpr char prog[] = R"(
57 const int sizeArray = 12;
58 uniform half2 iResolution;
59 uniform half2 centerPos;
60 uniform half radiusX;
61 uniform half radiusY;
62 uniform half colors[sizeArray];
63 uniform half positions[sizeArray];
64
65 float radialGradientMask(vec2 uv, vec2 centerPosition)
66 {
67 float sdfValue = length(uv - centerPosition) / radiusY;
68 sdfValue = clamp(sdfValue, 0.0, 1.0);
69
70 half color = 0.0;
71 color = (sdfValue >= positions[0] && sdfValue < positions[1])
72 ? mix(colors[0], colors[1], smoothstep(positions[0], positions[1], sdfValue)) : color;
73 color = (sdfValue >= positions[1] && sdfValue < positions[2])
74 ? mix(colors[1], colors[2], smoothstep(positions[1], positions[2], sdfValue)) : color;
75 color = (sdfValue >= positions[2] && sdfValue < positions[3])
76 ? mix(colors[2], colors[3], smoothstep(positions[2], positions[3], sdfValue)) : color;
77 color = (sdfValue >= positions[3] && sdfValue < positions[4])
78 ? mix(colors[3], colors[4], smoothstep(positions[3], positions[4], sdfValue)) : color;
79 color = (sdfValue >= positions[4] && sdfValue < positions[5])
80 ? mix(colors[4], colors[5], smoothstep(positions[4], positions[5], sdfValue)) : color;
81 color = (sdfValue >= positions[5] && sdfValue < positions[6])
82 ? mix(colors[5], colors[6], smoothstep(positions[5], positions[6], sdfValue)) : color;
83 color = (sdfValue >= positions[6] && sdfValue < positions[7])
84 ? mix(colors[6], colors[7], smoothstep(positions[6], positions[7], sdfValue)) : color;
85 color = (sdfValue >= positions[7] && sdfValue < positions[8])
86 ? mix(colors[7], colors[8], smoothstep(positions[7], positions[8], sdfValue)) : color;
87 color = (sdfValue >= positions[8] && sdfValue < positions[9])
88 ? mix(colors[8], colors[9], smoothstep(positions[8], positions[9], sdfValue)) : color;
89 color = (sdfValue >= positions[9] && sdfValue < positions[10])
90 ? mix(colors[9], colors[10], smoothstep(positions[9], positions[10], sdfValue)) : color;
91 color = (sdfValue >= positions[10] && sdfValue < positions[11])
92 ? mix(colors[10], colors[11], smoothstep(positions[10], positions[11], sdfValue)) : color;
93 return color;
94 }
95
96 half4 main(vec2 fragCoord)
97 {
98 vec2 uv = fragCoord.xy / iResolution.xy;
99 float screenRatio = iResolution.x / iResolution.y;
100 vec2 centeredUVs = uv * 2.0 - 1.0;
101 centeredUVs.x *= screenRatio * (radiusY / radiusX);
102 vec2 centerPosition = centerPos * 2 - 1.0 ;
103 centerPosition.x *= screenRatio * (radiusY / radiusX);
104
105 half finalColor = radialGradientMask(centeredUVs, centerPosition);
106 return half4(finalColor);
107 }
108 )";
109
110 auto radialGradientShaderMaskEffect = Drawing::RuntimeEffect::CreateForShader(prog);
111 if (!radialGradientShaderMaskEffect) {
112 LOGE("GERadialGradientShaderMask::GetRadialGradientShaderMaskBuilder effect error");
113 return nullptr;
114 }
115
116 radialGradientShaderMaskBuilder = std::make_shared<Drawing::RuntimeShaderBuilder>(radialGradientShaderMaskEffect);
117 return radialGradientShaderMaskBuilder;
118 }
119
GenerateDrawingShaderHasNormal(float width,float height) const120 std::shared_ptr<ShaderEffect> GERadialGradientShaderMask::GenerateDrawingShaderHasNormal(float width,
121 float height) const
122 {
123 GE_TRACE_NAME_FMT("GERadialGradientShaderMask::GenerateDrawingShaderHasNormal, Type: %s, Width: %g, Height: %g",
124 Drawing::GE_MASK_RADIAL_GRADIENT, width, height);
125 std::shared_ptr<Drawing::RuntimeShaderBuilder> builder = nullptr;
126 builder = GetRadialGradientNormalMaskBuilder();
127 if (!builder) {
128 LOGE("GERadialGradientShaderMask::GenerateDrawingShaderHasNormal builder error");
129 return nullptr;
130 }
131 auto radialGradientMaskEffectShader = GenerateShaderEffect(width, height, builder);
132
133 return radialGradientMaskEffectShader;
134 }
135
GetRadialGradientNormalMaskBuilder() const136 std::shared_ptr<Drawing::RuntimeShaderBuilder> GERadialGradientShaderMask::GetRadialGradientNormalMaskBuilder() const
137 {
138 thread_local std::shared_ptr<Drawing::RuntimeShaderBuilder> radialGradientMaskNormalBuilder = nullptr;
139 if (radialGradientMaskNormalBuilder) {
140 return radialGradientMaskNormalBuilder;
141 }
142
143 static constexpr char prog[] = R"(
144 const int sizeArray = 12;
145 uniform half2 iResolution;
146 uniform half2 centerPos;
147 uniform half radiusX;
148 uniform half radiusY;
149 uniform half colors[sizeArray];
150 uniform half positions[sizeArray];
151
152 float colorGradient(float colorA, float colorB, float startPos, float endPos, float threshold)
153 {
154 if (startPos <= threshold && threshold < endPos) {
155 return mix(colorA, colorB, smoothstep(startPos, endPos, threshold));
156 }
157 return 0.0;
158 }
159
160 vec4 radialGradientMask(vec2 uv, vec2 centerPosition)
161 {
162 vec2 vector = uv - centerPosition;
163 float distance = length(vector);
164 float sdfValue = distance / radiusY;
165 sdfValue = clamp(sdfValue, 0.0, 1.0);
166
167 half color = 0.0;
168 color = (sdfValue >= positions[0] && sdfValue < positions[1])
169 ? mix(colors[0], colors[1], smoothstep(positions[0], positions[1], sdfValue)) : color;
170 color = (sdfValue >= positions[1] && sdfValue < positions[2])
171 ? mix(colors[1], colors[2], smoothstep(positions[1], positions[2], sdfValue)) : color;
172 color = (sdfValue >= positions[2] && sdfValue < positions[3])
173 ? mix(colors[2], colors[3], smoothstep(positions[2], positions[3], sdfValue)) : color;
174 color = (sdfValue >= positions[3] && sdfValue < positions[4])
175 ? mix(colors[3], colors[4], smoothstep(positions[3], positions[4], sdfValue)) : color;
176 color = (sdfValue >= positions[4] && sdfValue < positions[5])
177 ? mix(colors[4], colors[5], smoothstep(positions[4], positions[5], sdfValue)) : color;
178 color = (sdfValue >= positions[5] && sdfValue < positions[6])
179 ? mix(colors[5], colors[6], smoothstep(positions[5], positions[6], sdfValue)) : color;
180 color = (sdfValue >= positions[6] && sdfValue < positions[7])
181 ? mix(colors[6], colors[7], smoothstep(positions[6], positions[7], sdfValue)) : color;
182 color = (sdfValue >= positions[7] && sdfValue < positions[8])
183 ? mix(colors[7], colors[8], smoothstep(positions[7], positions[8], sdfValue)) : color;
184 color = (sdfValue >= positions[8] && sdfValue < positions[9])
185 ? mix(colors[8], colors[9], smoothstep(positions[8], positions[9], sdfValue)) : color;
186 color = (sdfValue >= positions[9] && sdfValue < positions[10])
187 ? mix(colors[9], colors[10], smoothstep(positions[9], positions[10], sdfValue)) : color;
188 color = (sdfValue >= positions[10] && sdfValue < positions[11])
189 ? mix(colors[10], colors[11], smoothstep(positions[10], positions[11], sdfValue)) : color;
190
191 vec2 directionVector = vector / distance * color * 0.5 + 0.5;
192 return vec4(directionVector, 1.0, color);
193 }
194
195 half4 main(vec2 fragCoord)
196 {
197 vec2 uv = fragCoord.xy / iResolution.xy;
198 float screenRatio = iResolution.x / iResolution.y;
199 vec2 centeredUVs = uv * 2.0 - 1.0;
200 centeredUVs.x *= screenRatio * (radiusY / radiusX);
201 vec2 centerPosition = centerPos * 2 - 1.0 ;
202 centerPosition.x *= screenRatio * (radiusY / radiusX);
203
204 half4 finalColor = radialGradientMask(centeredUVs, centerPosition);
205 return half4(finalColor);
206 }
207 )";
208
209 auto radialGradientShaderMaskNormalEffect = Drawing::RuntimeEffect::CreateForShader(prog);
210 if (!radialGradientShaderMaskNormalEffect) {
211 LOGE("GERadialGradientShaderMask::GetRadialGradientNormalMaskBuilder effect error");
212 return nullptr;
213 }
214
215 radialGradientMaskNormalBuilder = std::make_shared<Drawing::RuntimeShaderBuilder>(
216 radialGradientShaderMaskNormalEffect);
217 return radialGradientMaskNormalBuilder;
218 }
219
GenerateShaderEffect(float width,float height,std::shared_ptr<Drawing::RuntimeShaderBuilder> builder) const220 std::shared_ptr<ShaderEffect> GERadialGradientShaderMask::GenerateShaderEffect(float width, float height,
221 std::shared_ptr<Drawing::RuntimeShaderBuilder> builder) const
222 {
223 if (!builder) {
224 LOGE("GERadialGradientShaderMask::GenerateShaderEffect builder error");
225 return nullptr;
226 }
227
228 size_t colorSize = param_.colors_.size();
229 size_t positionSize = param_.positions_.size();
230 // 0.01f is the min value
231 if (colorSize <= 0 || colorSize > SIZE_ARRAY || colorSize != positionSize || width < 0.01f || height < 0.01f) {
232 return nullptr;
233 }
234 // if radius <= 0, no need to draw. 0.001f is the min value
235 if (param_.radiusX_ < 0.001f || param_.radiusY_ < 0.001f) {
236 return nullptr;
237 }
238
239 float color[SIZE_ARRAY] = {0.0f}; // 0.0 default
240 float position[SIZE_ARRAY] = {1.0f}; // 1.0 default
241 for (size_t i = 0; i < SIZE_ARRAY; i++) {
242 if (i < colorSize) {
243 color[i] = std::clamp(param_.colors_[i], 0.0f, 1.0f); // 0.0 1.0 min and max value
244 position[i] = std::clamp(param_.positions_[i], 0.0f, 1.0f); // 0.0 1.0 min and max value
245 }
246 }
247
248 bool success = true;
249 for (size_t i = 1; i < colorSize; i++) {
250 success = success && (position[i] >= position[i - 1]);
251 }
252 if (!success) {
253 return nullptr;
254 }
255
256 if (position[0] < 0.001f) { // 0.001 represents the fraction bias
257 position[0] -= 0.001f;
258 }
259
260 builder->SetUniform("iResolution", width, height);
261 builder->SetUniform("centerPos", param_.center_.first, param_.center_.second);
262 builder->SetUniform("radiusX", param_.radiusX_ * RADIUS_SCALE);
263 builder->SetUniform("radiusY", param_.radiusY_ * RADIUS_SCALE);
264 builder->SetUniform("colors", color, SIZE_ARRAY);
265 builder->SetUniform("positions", position, SIZE_ARRAY);
266 auto radialGradientMaskEffectShader = builder->MakeShader(nullptr, false);
267 if (!radialGradientMaskEffectShader) {
268 LOGE("GERadialGradientShaderMask::GenerateDrawingShaderHas effect error");
269 }
270 return radialGradientMaskEffectShader;
271 }
272
273 } // namespace Drawing
274 } // namespace Rosen
275 } // namespace OHOS