• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "ge_log.h"
17 #include "ge_particle_circular_halo_shader.h"
18 #include "ge_visual_effect_impl.h"
19 
20 namespace OHOS {
21 namespace Rosen {
22 
23 namespace {
24     constexpr float DOWN_SAMPLE_SCALE = 4.0;
25     static constexpr char GLOW_HALO_PROG[] = R"(
26         uniform half2 iResolution;
27         uniform half2 rotationCenter;  // center of the halo rings, normalized coords (0. - 10.)
28         uniform float DOWN_SAMPLE_SCALE;
29 
30         vec4 fragColor;
31         const float PI = 3.14159;
32         const float PI2 = 6.28318;
33 
34         const half2 RAND2_A = half2(127.1, 1.7);
35         const half2 RAND2_B = half2(269.5, 183.3);
36         const float RAND2_MUL = 4258.5453123;
37         // ****************************** Sub-functions ******************************
38         half2 Random2D(half2 st)
39         {
40             st = half2(dot(st, RAND2_A), dot(st, RAND2_B));
41             return -1.0 + 2.0 * fract(sin(st) * RAND2_MUL);
42         }
43 
44         float Noise2D(half2 st)
45         {
46             half2 i = floor(st);
47             half2 f = fract(st);
48             half2 u = smoothstep(0.0, 1.0, f);
49             return mix(mix(dot(Random2D(i + half2(0.0, 0.0)), f - half2(0.0, 0.0)),
50                            dot(Random2D(i + half2(1.0, 0.0)), f - half2(1.0, 0.0)), u.x),
51                        mix(dot(Random2D(i + half2(0.0, 1.0)), f - half2(0.0, 1.0)),
52                            dot(Random2D(i + half2(1.0, 1.0)), f - half2(1.0, 1.0)), u.x),
53                        u.y);
54         }
55 
56         // Simple 1D pseudo-random hash value
57         float Random1D(float x)
58         {
59             return fract(sin(x) * RAND2_MUL);
60         }
61 
62         float Noise1D(float t)
63         {
64             float f = fract(t);
65             // Cubic Hermite Curve. Same as SmoothStep()
66             float u = smoothstep(0.0, 1.0, f);
67             return mix(Random1D(floor(t)), Random1D(floor(t) + 1.0), u);
68         }
69 
70         // Create animated, noisy halo border lines
71         float CentralAmbienceHaloBorder(half2 uv, half2 polarCoords, float radius, float animationTime,
72             float rotationTimeScale, float noiseScale, float noiseDisplacement)
73         {
74             polarCoords.x = fract((polarCoords.x - animationTime * 4.0) - (polarCoords.y * 0.8));
75             float screenNoise = Noise2D(half2(uv + (animationTime * rotationTimeScale)) * noiseScale);
76             float angularRandomVal = Noise1D((polarCoords.x) * 10.0);
77             angularRandomVal = mix(angularRandomVal, 0.2, smoothstep(0.5, 0.8, abs(polarCoords.x * 2.0 - 1.0)));
78             radius += (screenNoise * noiseDisplacement);
79             polarCoords.y -= radius;
80             float thickness = mix(0.01, 0.035, angularRandomVal);
81             float circleBorder = smoothstep(thickness * radius, 0.0, abs(polarCoords.y));
82             circleBorder *= smoothstep(0.1, 1.0, angularRandomVal);
83             circleBorder *= screenNoise * 0.5 + 0.5;
84             return circleBorder;
85         }
86 
87         // Create soft glow inside halo
88         float CentralAmbienceHaloGlow(half2 polarCoords, float animationTime, float radius, float glowRadius,
89             float glowExposure)
90         {
91             polarCoords.x = fract(((polarCoords.x - animationTime * 4.0) - (polarCoords.y * 0.8)));
92             float angularRandomVal = Noise1D(polarCoords.x * 10.);
93             angularRandomVal = mix(angularRandomVal, 0.2, smoothstep(0.5, 0.8, abs(polarCoords.x * 2.0 - 1.0)));
94             polarCoords.y -= radius;
95             float circleBorderGlow = smoothstep(glowRadius, 0.0, abs(polarCoords.y)) * glowExposure;
96             return circleBorderGlow;
97         }
98 
99         float BlendScreen(float a, float b)
100         {
101             return a + b - a * b;
102         }
103 
104         half4 BlendScreen(half4 a, half4 b)
105         {
106             return a + b - a * b;
107         }
108 
109         // ****************************** Main Functions ******************************
110         vec4 main(vec2 fragCoord)
111         {
112             float globalRadius = 0.5 / DOWN_SAMPLE_SCALE;
113             float radius = globalRadius * 2.0;
114             float innerRadiusEdge = 0.5 * radius;
115             float outerRadiusEdge = 1.35 * radius;
116             float glowColor = 0.;
117             float solidColor = 0.0;
118 
119             half2 uv = fragCoord.xy / iResolution.xy;
120             uv -= rotationCenter;
121             float screenRatio = iResolution.x / iResolution.y;
122             half2 centeredUVs = uv * 2.0 ;
123             centeredUVs.x *= screenRatio;
124             half2 directionVector = centeredUVs;
125             float lenDirVec = length(directionVector);
126             half2 polarCoords =
127                 half2((atan(directionVector.y, directionVector.x) + PI) / (2.0 * PI), lenDirVec);
128 
129             if (lenDirVec > 0.5) {
130                 fragColor = vec4(0.);
131             } else {
132                 float ambienceHaloBorder = CentralAmbienceHaloBorder(directionVector, polarCoords, radius * 0.82,
133                     globalRadius * 0.1, 1.0, 1.25, 0.05) * 2.5;
134                 ambienceHaloBorder = clamp(ambienceHaloBorder, 0., 1.);
135                 solidColor = BlendScreen(solidColor, ambienceHaloBorder);
136                 ambienceHaloBorder = CentralAmbienceHaloBorder(directionVector, polarCoords, radius * 0.95,
137                     globalRadius * 0.25, 1.0, 0.25, 0.05);
138                 solidColor = BlendScreen(solidColor, ambienceHaloBorder);
139                 ambienceHaloBorder = CentralAmbienceHaloBorder(directionVector, polarCoords, radius,
140                     globalRadius * 0.35, 1.0, 0.25, 0.05);
141                 solidColor = BlendScreen(solidColor, ambienceHaloBorder);
142                 float ambienceHaloGlow = CentralAmbienceHaloGlow(polarCoords, 1.0, radius * 0.83, 0.2 * radius,
143                     0.4);
144                 float glow = clamp(ambienceHaloGlow, 0., 1.);
145                 glowColor = BlendScreen(glowColor, glow);  // Add a glow circle
146             }
147 
148             fragColor = BlendScreen(half4(half3(solidColor), 0.0), half4(glowColor));
149             return fragColor;
150         }
151     )";
152 
153     static constexpr char PARTICLE_HALO_PROG[] = R"(
154         uniform half2 iResolution;
155         uniform half randomNoise;
156         uniform half2 rotationCenter; // center of the halo rings, normalized coords (0. - 1.)
157         uniform half DOWN_SAMPLE_SCALE;
158 
159         half4 fragColor;
160         // ****************************** Constants ******************************
161         const half WIDTH = 0.055;             // Width of the halo rings
162         const half PI = 3.14159;
163         const half PI2 = 6.28318;
164 
165         const half2 RAND2_A = half2(127.1, 1.7);
166         const half2 RAND2_B = half2(269.5, 183.3);
167         const half RAND2_MUL = 2358.5453123;
168 
169         // ****************************** Sub-functions ******************************
170         half2 Random2D(half2 st)
171         {
172             st = half2(dot(st, RAND2_A), dot(st, RAND2_B));
173             return -1.0 + 2.0 * fract(sin(st) * RAND2_MUL);
174         }
175 
176         half Noise2D(half2 st)
177         {
178             half2 i = floor(st);
179             half2 f = fract(st);
180             half2 u = smoothstep(0.0, 1.0, f);
181             return mix(mix(dot(Random2D(i + half2(0.0, 0.0)), f - half2(0.0, 0.0)),
182                            dot(Random2D(i + half2(1.0, 0.0)), f - half2(1.0, 0.0)), u.x),
183                        mix(dot(Random2D(i + half2(0.0, 1.0)), f - half2(0.0, 1.0)),
184                            dot(Random2D(i + half2(1.0, 1.0)), f - half2(1.0, 1.0)), u.x),
185                        u.y);
186         }
187 
188         half Random1D(half x)
189         {
190             return fract(sin(x) * RAND2_MUL);
191         }
192 
193         half Noise1D(half t)
194         {
195             half f = fract(t);
196             half u = smoothstep(0.0, 1.0, f);
197             return mix(Random1D(floor(t)), Random1D(floor(t) + 1.0), u);
198         }
199 
200         half2 ShapePerturbation(half2 uv, half noiseVariation, half noiseScale, half noiseStrength)
201         {
202             half screenNoise = Noise2D(half2(uv.x - noiseVariation, uv.y + noiseVariation) * noiseScale);
203             return uv + screenNoise * noiseStrength;
204         }
205 
206         half SimpleHaloRingShape(half2 uv,  half radius, half noiseVariation, half haloThickness)
207         {
208             uv = ShapePerturbation(uv, noiseVariation, 1.0, radius * 0.6);
209             half2 polarCoords = half2((atan(uv.y, uv.x) + PI) / (2.0 * PI), length(uv));
210             polarCoords.y -= radius;
211             half angularRandomVal = Noise1D((polarCoords.x) * 10.0);
212             // ensure that the radial noise is seamless
213             angularRandomVal = mix(angularRandomVal, 0.5, smoothstep(0.9, 1.0, abs(polarCoords.x * 2.0 - 1.0)));
214             half thickness = mix(haloThickness, haloThickness * 4.0, angularRandomVal);
215             half circleBorder = smoothstep(thickness, thickness * 0.2, abs(polarCoords.y));
216             return clamp(circleBorder, 0.0, 1.0);
217         }
218         // ****************************** Main Functions ******************************
219         half4 main(vec2 fragCoord)
220         {
221           	half globalRadius = 0.5 / DOWN_SAMPLE_SCALE;
222             half radius = globalRadius * 2.;
223             half innerRadiusEdge = 0.5 * radius;
224             half outerRadiusEdge = 1.35 * radius;
225             half haloColor = 0.;
226 
227             half2 uv = fragCoord.xy / iResolution.xy;
228             half screenRatio = iResolution.x / iResolution.y;
229             uv -= rotationCenter;
230             half2 centeredUVs = uv * 2.0;
231             centeredUVs.x *= screenRatio;
232             half2 directionVector = centeredUVs;
233             half lenDir = length(directionVector);
234             if (lenDir > 0.5) {
235                 fragColor = vec4(0.);
236             } else {
237                 half2 polarCoords =
238                 half2((atan(directionVector.y, directionVector.x) + PI) / (2.0 * PI), lenDir);
239 
240                 if (lenDir >= innerRadiusEdge && lenDir <= outerRadiusEdge) {
241                     half particleHaloColor = 0.;
242                     half haloRing = SimpleHaloRingShape(directionVector, radius, randomNoise, WIDTH * radius);
243                     particleHaloColor += haloRing;
244                     haloColor = haloColor + particleHaloColor - haloColor * particleHaloColor;
245                 }
246                 fragColor = half4(haloColor);
247             }
248             return fragColor;
249         }
250     )";
251 
252     static constexpr char MAIN_SHADER_PROG[] = R"(
253         uniform half2 iResolution;
254         uniform float globalRadius;   // global radius controlling the overall size of halos
255         uniform half2 rotationCenter;  // center of the halo rings, normalized coords (0. - 1.)
256         uniform shader particleHalo;
257         uniform shader glowHalo;
258         uniform float DOWN_SAMPLE_SCALE;
259 
260         vec4 fragColor;
261         const int NUMBER_OF_SAMPLES = 11;  // Number of samples used for blur effects
262         const float NUMBER_OF_SAMPLES_F = 11.;
263         const float PI = 3.14159;
264         const float PI2 = 6.28318;
265 
266         // Color stops for the color bar
267         const float STOP_POS0 = 0.00;
268         const float STOP_POS1 = 0.22;
269         const float STOP_POS2 = 0.75;
270         const float STOP_POS3 = 0.90;
271         const float STOP_POS4 = 1.00;
272 
273         const half3 STOP_COLOR0 = half3(255.0, 133.0, 127.0) / 255.0;  // FF717F
274         const half3 STOP_COLOR1 = half3(86.0, 146.0, 255.0) / 255.0;   // 5692FF
275         const half3 STOP_COLOR2 = half3(137.0, 240.0, 255.0) / 255.0;  // 89F0FF
276         const half3 STOP_COLOR3 = half3(255.0, 200.0, 161.0) / 255.0;  // FFC8A1
277         const half3 STOP_COLOR4 = half3(253.0, 216.0, 98.0) / 255.0;   // FDD862
278 
279         const half2 RAND2_A = half2(127.1, 1.7);
280         const half2 RAND2_B = half2(269.5, 183.3);
281         const float RAND2_MUL = 4258.5453123;
282 
283         const float COS_ROT1 = 0.993482;
284         const float COS_ROT2 = 0.974012;
285         const float COS_ROT3 = 0.941844;
286         const float COS_ROT4 = 0.897398;
287         const float COS_ROT5 = 0.841254;
288         const float COS_ROT6 = 0.774142;
289         const float COS_ROT7 = 0.696938;
290         const float COS_ROT8 = 0.610648;
291         const float COS_ROT9 = 0.516397;
292         const float COS_ROT10 = 0.415415;
293 
294         const float SIN_ROT1 = 0.113991;
295         const float SIN_ROT2 = 0.226497;
296         const float SIN_ROT3 = 0.336049;
297         const float SIN_ROT4 = 0.441221;
298         const float SIN_ROT5 = 0.540641;
299         const float SIN_ROT6 = 0.633012;
300         const float SIN_ROT7 = 0.717132;
301         const float SIN_ROT8 = 0.791902;
302         const float SIN_ROT9 = 0.856349;
303         const float SIN_ROT10 = 0.909632;
304 
305         // ****************************** Sub-functions ******************************
306         half2 Random2D(half2 st)
307         {
308             st = half2(dot(st, RAND2_A), dot(st, RAND2_B));
309             return -1.0 + 2.0 * fract(sin(st) * RAND2_MUL);
310         }
311 
312         float Noise2D(half2 st)
313         {
314             half2 i = floor(st);
315             half2 f = fract(st);
316             half2 u = smoothstep(0.0, 1.0, f);
317             return mix(mix(dot(Random2D(i + half2(0.0, 0.0)), f - half2(0.0, 0.0)),
318                            dot(Random2D(i + half2(1.0, 0.0)), f - half2(1.0, 0.0)), u.x),
319                        mix(dot(Random2D(i + half2(0.0, 1.0)), f - half2(0.0, 1.0)),
320                            dot(Random2D(i + half2(1.0, 1.0)), f - half2(1.0, 1.0)), u.x),
321                        u.y);
322         }
323 
324         // Simple 1D pseudo-random hash value
325         float Random1(float x)
326         {
327             return fract(sin(x) * RAND2_MUL);
328         }
329 
330         float Noise1D(float t)
331         {
332             float f = fract(t);
333             float u = smoothstep(0.0, 1.0, f);
334             return mix(Random1(floor(t)), Random1(floor(t) + 1.0), u);
335         }
336 
337         // Create animated, noisy halo border lines
338         float CentralAmbienceHaloBorder(half2 uv, half2 polarCoords, float radius, float animationTime,
339             float rotationTimeScale, float noiseScale, float noiseDisplacement)
340         {
341             polarCoords.x = fract((polarCoords.x - animationTime * 4.0) - (polarCoords.y * 0.8));
342             float screenNoise = Noise2D(half2(uv + (animationTime * rotationTimeScale)) * noiseScale);
343             float angularRandomVal = Noise1D((polarCoords.x) * 10.0);
344             angularRandomVal = mix(angularRandomVal, 0.2, smoothstep(0.5, 0.8, abs(polarCoords.x * 2.0 - 1.0)));
345             radius += (screenNoise * noiseDisplacement);
346             polarCoords.y -= radius;
347             float thickness = mix(0.01, 0.035, angularRandomVal);
348             float circleBorder = smoothstep(thickness * radius, 0.0, abs(polarCoords.y));
349             circleBorder *= smoothstep(0.1, 1.0, angularRandomVal);
350             circleBorder *= screenNoise * 0.5 + 0.5;
351             return circleBorder;
352         }
353 
354         // Create soft glow inside halo
355         float CentralAmbienceHaloGlow(half2 polarCoords, float animationTime, float radius, float glowRadius,
356             float glowExposure)
357         {
358             polarCoords.x = fract(((polarCoords.x - animationTime * 4.0) - (polarCoords.y * 0.8)));
359             float angularRandomVal = Noise1D(polarCoords.x * 10.);
360             angularRandomVal = mix(angularRandomVal, 0.2, smoothstep(0.5, 0.8, abs(polarCoords.x * 2.0 - 1.0)));
361             polarCoords.y -= radius;
362             float circleBorderGlow = smoothstep(glowRadius, 0.0, abs(polarCoords.y)) * glowExposure;
363             return circleBorderGlow;
364         }
365 
366         half3 GetColorFromColorbar(half2 pt, float radius)
367         {
368             // Map pt.x to the range [0.0, 1.0] for color interpolation
369             float t = (pt.x + radius) / (2.0 * radius);
370             half3 colorValue =
371                 step(pt.x, -radius) * STOP_COLOR0 +
372                 // Return the first color when pt.x is to the left of the halo
373                 step(STOP_POS0, t) * step(t, STOP_POS1) *
374                 mix(STOP_COLOR0, STOP_COLOR1,
375                     (t - STOP_POS0) / (STOP_POS1 - STOP_POS0)) +  // 0.00 < t <= 0.22
376                 step(STOP_POS1, t) * step(t, STOP_POS2) *
377                 mix(STOP_COLOR1, STOP_COLOR2,
378                     (t - STOP_POS1) / (STOP_POS2 - STOP_POS1)) +  // 0.22 < t <= 0.75
379                 step(STOP_POS2, t) * step(t, STOP_POS3) *
380                 mix(STOP_COLOR2, STOP_COLOR3,
381                     (t - STOP_POS2) / (STOP_POS3 - STOP_POS2)) +  // 0.75 < t <= 0.90
382                 step(STOP_POS3, t) * step(t, STOP_POS4) *
383                 mix(STOP_COLOR3, STOP_COLOR4,
384                     (t - STOP_POS3) / (STOP_POS4 - STOP_POS3)) +  // 0.90 < t <= 1.00
385                 step(STOP_POS4, t) * STOP_COLOR4;                           // t > 1.00
386             return colorValue;
387         }
388 
389         float BlendScreen(float a, float b)
390         {
391             return a + b - a * b;
392         }
393 
394         half2 UV2PixelParticle(half2 rotatedDir, float DOWN_SAMPLE_SCALE, float screenRatio, half radius,
395             half2 rotationCenter, half2 iRes)
396         {
397             half lenRotDir = length(rotatedDir);
398             half cosBeta = rotatedDir.x / lenRotDir;
399             half sinBeta = rotatedDir.y / lenRotDir;
400             rotatedDir = half2(cosBeta, sinBeta) * lenRotDir / radius / DOWN_SAMPLE_SCALE;
401 
402             rotatedDir /= 2.0;
403             rotatedDir.x /= screenRatio;
404             rotatedDir += rotationCenter;
405             return  rotatedDir * iRes;
406         }
407 
408         // ****************************** Main Functions ******************************
409         vec4 main(vec2 fragCoord)
410         {
411             float radius = globalRadius * 2.0;
412             float innerRadiusEdge = 0.5 * radius;
413             float outerRadiusEdge = 1.35 * radius;
414             float haloColor = 0.;
415             half4 glowColor = half4(0.0);
416 
417             half2 uv = fragCoord.xy / iResolution.xy;
418             uv -= rotationCenter;
419             float screenRatio = iResolution.x / iResolution.y;
420             half2 centeredUVs = uv * 2.0 ;
421             centeredUVs.x *= screenRatio;
422             half2 directionVector = centeredUVs;
423             float lenDirVec = length(directionVector);
424             half2 polarCoords =
425                 half2((atan(directionVector.y, directionVector.x) + PI) / (2.0 * PI), lenDirVec);
426 
427             float colorRotScale = radius * PI * 0.5;  // Make the ring colors rotated.
428             mat2 rotColorRing =
429                 mat2(cos(colorRotScale), -sin(colorRotScale), sin(colorRotScale), cos(colorRotScale));
430             half3 colorRing = GetColorFromColorbar(directionVector * rotColorRing, radius);
431 
432             if (lenDirVec >= innerRadiusEdge && lenDirVec <= outerRadiusEdge) {
433                 float particleHaloColor = 0.;
434 
435                 half2 rotatedDir = directionVector;
436               	half2 samplePixel = UV2PixelParticle(rotatedDir, DOWN_SAMPLE_SCALE, screenRatio, radius,
437                     rotationCenter, iResolution.xy);
438                 half4 samplerColor = particleHalo.eval(samplePixel);
439                 particleHaloColor += samplerColor.x;
440 
441               	rotatedDir = mat2(COS_ROT1, SIN_ROT1, -SIN_ROT1, COS_ROT1) * directionVector;
442                 samplePixel = UV2PixelParticle(rotatedDir, DOWN_SAMPLE_SCALE, screenRatio, radius,
443                     rotationCenter, iResolution.xy);
444                 samplerColor = particleHalo.eval(samplePixel);
445                 particleHaloColor += samplerColor.x;
446 
447               	rotatedDir = mat2(COS_ROT2, SIN_ROT2, -SIN_ROT2, COS_ROT2) * directionVector;
448                 samplePixel = UV2PixelParticle(rotatedDir, DOWN_SAMPLE_SCALE, screenRatio, radius,
449                     rotationCenter, iResolution.xy);
450                 samplerColor = particleHalo.eval(samplePixel);
451                 particleHaloColor += samplerColor.x;
452 
453               	rotatedDir = mat2(COS_ROT3, SIN_ROT3, -SIN_ROT3, COS_ROT3) * directionVector;
454                 samplePixel = UV2PixelParticle(rotatedDir, DOWN_SAMPLE_SCALE, screenRatio, radius,
455                     rotationCenter, iResolution.xy);
456                 samplerColor = particleHalo.eval(samplePixel);
457                 particleHaloColor += samplerColor.x;
458 
459               	rotatedDir = mat2(COS_ROT4, SIN_ROT4, -SIN_ROT4, COS_ROT4) * directionVector;
460                 samplePixel = UV2PixelParticle(rotatedDir, DOWN_SAMPLE_SCALE, screenRatio, radius,
461                     rotationCenter, iResolution.xy);
462                 samplerColor = particleHalo.eval(samplePixel);
463                 particleHaloColor += samplerColor.x;
464 
465               	rotatedDir = mat2(COS_ROT5, SIN_ROT5, -SIN_ROT5, COS_ROT5) * directionVector;
466                 samplePixel = UV2PixelParticle(rotatedDir, DOWN_SAMPLE_SCALE, screenRatio, radius,
467                     rotationCenter, iResolution.xy);
468                 samplerColor = particleHalo.eval(samplePixel);
469                 particleHaloColor += samplerColor.x;
470 
471               	rotatedDir = mat2(COS_ROT6, SIN_ROT6, -SIN_ROT6, COS_ROT6) * directionVector;
472                 samplePixel = UV2PixelParticle(rotatedDir, DOWN_SAMPLE_SCALE, screenRatio, radius,
473                     rotationCenter, iResolution.xy);
474                 samplerColor = particleHalo.eval(samplePixel);
475                 particleHaloColor += samplerColor.x;
476 
477               	rotatedDir = mat2(COS_ROT7, SIN_ROT7, -SIN_ROT7, COS_ROT7) * directionVector;
478                 samplePixel = UV2PixelParticle(rotatedDir, DOWN_SAMPLE_SCALE, screenRatio, radius,
479                     rotationCenter, iResolution.xy);
480                 samplerColor = particleHalo.eval(samplePixel);
481                 particleHaloColor += samplerColor.x;
482 
483               	rotatedDir = mat2(COS_ROT8, SIN_ROT8, -SIN_ROT8, COS_ROT8) * directionVector;
484                 samplePixel = UV2PixelParticle(rotatedDir, DOWN_SAMPLE_SCALE, screenRatio, radius,
485                     rotationCenter, iResolution.xy);
486                 samplerColor = particleHalo.eval(samplePixel);
487                 particleHaloColor += samplerColor.x;
488 
489               	rotatedDir = mat2(COS_ROT9, SIN_ROT9, -SIN_ROT9, COS_ROT9) * directionVector;
490                 samplePixel = UV2PixelParticle(rotatedDir, DOWN_SAMPLE_SCALE, screenRatio, radius,
491                     rotationCenter, iResolution.xy);
492                 samplerColor = particleHalo.eval(samplePixel);
493                 particleHaloColor += samplerColor.x;
494 
495               	rotatedDir = mat2(COS_ROT10, SIN_ROT10, -SIN_ROT10, COS_ROT10) * directionVector;
496                 samplePixel = UV2PixelParticle(rotatedDir, DOWN_SAMPLE_SCALE, screenRatio, radius,
497                     rotationCenter, iResolution.xy);
498                 samplerColor = particleHalo.eval(samplePixel);
499                 particleHaloColor += samplerColor.x;
500 
501                 particleHaloColor /= NUMBER_OF_SAMPLES_F;
502 
503                 haloColor = BlendScreen(haloColor, particleHaloColor);
504 
505               	half glowRadiusScale = 1.05;
506                 float angle = radius * PI;
507                 half2 rotatedUV = mat2(cos(angle), -sin(angle), sin(angle), cos(angle)) * centeredUVs;
508                 float lenRotUV = length(rotatedUV);
509                 float cosAlpha = rotatedUV.x / lenRotUV;
510                 float sinAlpha = rotatedUV.y / lenRotUV;
511                 rotatedUV = half2(cosAlpha, sinAlpha) * lenRotUV / radius / DOWN_SAMPLE_SCALE / glowRadiusScale;
512                 rotatedUV /= 2.0;
513                 rotatedUV.x /= screenRatio;
514                 rotatedUV += rotationCenter;
515 
516                 glowColor = glowHalo.eval(rotatedUV * iResolution.xy);
517                 glowColor.rgb *= colorRing;
518             }
519             fragColor = half4(clamp(haloColor, 0.0, 1.0));
520             fragColor.xyz *= colorRing;
521             fragColor = fragColor + glowColor - fragColor * glowColor;
522             return fragColor;
523         }
524     )";
525 }
526 
527 using CacheDataType = struct CacheData {
528     std::shared_ptr<Drawing::Image> cacheImg = nullptr;
529 };
530 
GEParticleCircularHaloShader()531 GEParticleCircularHaloShader::GEParticleCircularHaloShader() {}
532 
GEParticleCircularHaloShader(Drawing::GEParticleCircularHaloShaderParams & params)533 GEParticleCircularHaloShader::GEParticleCircularHaloShader(Drawing::GEParticleCircularHaloShaderParams& params)
534 {
535     particleCircularHaloParams_ = params;
536 }
537 
MakeDrawingShader(const Drawing::Rect & rect,float progress)538 void GEParticleCircularHaloShader::MakeDrawingShader(const Drawing::Rect& rect, float progress)
539 {
540     drShader_ = MakeParticleCircularHaloShader(rect);
541 }
542 
CreateParticleCircularHaloShader(Drawing::GEParticleCircularHaloShaderParams & params)543 std::shared_ptr<GEParticleCircularHaloShader> GEParticleCircularHaloShader::CreateParticleCircularHaloShader(
544     Drawing::GEParticleCircularHaloShaderParams& params)
545 {
546     std::shared_ptr<GEParticleCircularHaloShader> particleCircularHaloShader =
547         std::make_shared<GEParticleCircularHaloShader>(params);
548     return particleCircularHaloShader;
549 }
550 
Preprocess(Drawing::Canvas & canvas,const Drawing::Rect & rect)551 void GEParticleCircularHaloShader::Preprocess(Drawing::Canvas& canvas, const Drawing::Rect& rect)
552 {
553     Drawing::ImageInfo particleHaloImg(rect.GetWidth(), rect.GetHeight(),
554         Drawing::ColorType::COLORTYPE_RGBA_8888, Drawing::AlphaType::ALPHATYPE_OPAQUE);
555     particleHaloImg_ = MakeParticleHaloShader(canvas, particleHaloImg);
556 
557     float currentCenterX = particleCircularHaloParams_.center_.first;
558     float currentCenterY = particleCircularHaloParams_.center_.second;
559     if (cacheAnyPtr_ == nullptr || lastCenterX_ != currentCenterX || lastCenterY_ != currentCenterY) {
560         CacheDataType cacheData;
561         Drawing::ImageInfo cacheImgInf(rect.GetWidth(), rect.GetHeight(),
562             Drawing::ColorType::COLORTYPE_RGBA_8888, Drawing::AlphaType::ALPHATYPE_OPAQUE);
563         auto cacheImg = MakeGlowHaloShader(canvas, cacheImgInf);
564         if (cacheImg) {
565             cacheData.cacheImg = cacheImg;
566             cacheAnyPtr_ = std::make_shared<std::any>(std::make_any<CacheDataType>(cacheData));
567         }
568         lastCenterX_ = currentCenterX;
569         lastCenterY_ = currentCenterY;
570     }
571 }
572 
GetGlowHaloBuilder()573 std::shared_ptr<Drawing::RuntimeShaderBuilder> GEParticleCircularHaloShader::GetGlowHaloBuilder()
574 {
575     thread_local std::shared_ptr<Drawing::RuntimeEffect> preCalculatedGlowHaloEffect_ = nullptr;
576 
577     if (preCalculatedGlowHaloEffect_ == nullptr) {
578         preCalculatedGlowHaloEffect_ = Drawing::RuntimeEffect::CreateForShader(GLOW_HALO_PROG);
579     }
580     if (preCalculatedGlowHaloEffect_ == nullptr) {
581         GE_LOGE("GEParticleCircularHaloShader preCalculatedGlowHaloEffect_ is nullptr.");
582         return nullptr;
583     }
584     return std::make_shared<Drawing::RuntimeShaderBuilder>(preCalculatedGlowHaloEffect_);
585 }
586 
GetParticleHaloBuilder()587 std::shared_ptr<Drawing::RuntimeShaderBuilder> GEParticleCircularHaloShader::GetParticleHaloBuilder()
588 {
589     thread_local std::shared_ptr<Drawing::RuntimeEffect> particleHaloEffect_ = nullptr;
590 
591     if (particleHaloEffect_ == nullptr) {
592         particleHaloEffect_ = Drawing::RuntimeEffect::CreateForShader(PARTICLE_HALO_PROG);
593     }
594     if (particleHaloEffect_ == nullptr) {
595         GE_LOGE("GEParticleCircularHaloShader::GetParticleHaloBuilder particleHaloEffect_ is "
596                 "nullptr.");
597         return nullptr;
598     }
599     return std::make_shared<Drawing::RuntimeShaderBuilder>(particleHaloEffect_);
600 }
601 
GetParticleCircularHaloBuilder()602 std::shared_ptr<Drawing::RuntimeShaderBuilder> GEParticleCircularHaloShader::GetParticleCircularHaloBuilder()
603 {
604     thread_local std::shared_ptr<Drawing::RuntimeEffect> glowHaloEffect_ = nullptr;
605 
606     if (glowHaloEffect_ == nullptr) {
607         glowHaloEffect_ = Drawing::RuntimeEffect::CreateForShader(MAIN_SHADER_PROG);
608     }
609 
610     if (glowHaloEffect_ == nullptr) {
611         GE_LOGE("GEParticleCircularHaloShader:: GetParticleCircularHaloBuilder ShaderEffect_ is nullptr.");
612         return nullptr;
613     }
614     return std::make_shared<Drawing::RuntimeShaderBuilder>(glowHaloEffect_);
615 }
616 
MakeGlowHaloShader(Drawing::Canvas & canvas,const Drawing::ImageInfo & imageInfo)617 std::shared_ptr<Drawing::Image> GEParticleCircularHaloShader::MakeGlowHaloShader(
618     Drawing::Canvas& canvas, const Drawing::ImageInfo& imageInfo)
619 {
620     float width = imageInfo.GetWidth();
621     float height = imageInfo.GetHeight();
622     auto glowHaloBuilder_ = GetGlowHaloBuilder();
623     if (glowHaloBuilder_ == nullptr) {
624         GE_LOGE("GEParticleCircularHaloShader MakeGlowHaloShader preCalculatedBuilder_ is nullptr.");
625         return nullptr;
626     }
627     glowHaloBuilder_->SetUniform("iResolution", width, height);
628     glowHaloBuilder_->SetUniform("rotationCenter", particleCircularHaloParams_.center_.first,
629         particleCircularHaloParams_.center_.second);
630     glowHaloBuilder_->SetUniform("DOWN_SAMPLE_SCALE", DOWN_SAMPLE_SCALE);
631     auto preCalculatedGlowHaloShader = glowHaloBuilder_->MakeImage(canvas.GetGPUContext().get(),
632         nullptr, imageInfo, false);
633     if (preCalculatedGlowHaloShader == nullptr) {
634         GE_LOGE("GEParticleCircularHaloShader preCalculatedGlowHaloShader is nullptr.");
635         return nullptr;
636     }
637     return preCalculatedGlowHaloShader;
638 }
639 
MakeParticleHaloShader(Drawing::Canvas & canvas,const Drawing::ImageInfo & imageInfo)640 std::shared_ptr<Drawing::Image> GEParticleCircularHaloShader::MakeParticleHaloShader(
641     Drawing::Canvas& canvas, const Drawing::ImageInfo& imageInfo)
642 {
643     float width = imageInfo.GetWidth();
644     float height = imageInfo.GetHeight();
645 
646     particleHaloBuilder_ = GetParticleHaloBuilder();
647     if (particleHaloBuilder_ == nullptr) {
648         GE_LOGE("GEParticleCircularHaloShader MakeParticleHaloShader particleTextureBuilder_ is nullptr.");
649         return nullptr;
650     }
651     particleHaloBuilder_->SetUniform("iResolution", width, height);
652     particleHaloBuilder_->SetUniform("rotationCenter", particleCircularHaloParams_.center_.first,
653         particleCircularHaloParams_.center_.second);
654     particleHaloBuilder_->SetUniform("randomNoise", particleCircularHaloParams_.noise_);
655     particleHaloBuilder_->SetUniform("DOWN_SAMPLE_SCALE", DOWN_SAMPLE_SCALE);
656     auto particleHaloShader =
657         particleHaloBuilder_->MakeImage(canvas.GetGPUContext().get(), nullptr, imageInfo, false);
658     if (particleHaloShader == nullptr) {
659         GE_LOGE("GEParticleCircularHaloShader MakeParticleHaloShader is nullptr.");
660         return nullptr;
661     }
662     return particleHaloShader;
663 }
664 
MakeParticleCircularHaloShader(const Drawing::Rect & rect)665 std::shared_ptr<Drawing::ShaderEffect> GEParticleCircularHaloShader::MakeParticleCircularHaloShader(
666     const Drawing::Rect& rect)
667 {
668     if (particleHaloImg_ == nullptr) {
669         GE_LOGE("GEParticleCircularHaloShader MakeParticleCircularHaloShader particleHaloImg_ is nullptr.");
670         return nullptr;
671     }
672 
673     if (cacheAnyPtr_ == nullptr) {
674         GE_LOGE("GEParticleCircularHaloShader MakeParticleCircularHaloShader cacheAnyPtr_ is nullptr.");
675         return nullptr;
676     }
677 
678     auto width = rect.GetWidth();
679     auto height = rect.GetHeight();
680 
681     Drawing::Matrix matrix;
682     auto preCalcImg_ = std::any_cast<CacheDataType>(*cacheAnyPtr_).cacheImg;
683     if (preCalcImg_ == nullptr) {
684         GE_LOGE("GEParticleCircularHaloShader MakeParticleCircularHaloShader preCalcImg_ is nullptr.");
685         return nullptr;
686     }
687     auto preCalcImgShader = Drawing::ShaderEffect::CreateImageShader(*preCalcImg_, Drawing::TileMode::CLAMP,
688         Drawing::TileMode::CLAMP, Drawing::SamplingOptions(Drawing::FilterMode::LINEAR), matrix);
689 
690     auto particleHaloShader = Drawing::ShaderEffect::CreateImageShader(*particleHaloImg_,
691         Drawing::TileMode::CLAMP, Drawing::TileMode::CLAMP,
692         Drawing::SamplingOptions(Drawing::FilterMode::LINEAR), matrix);
693 
694     builder_ = GetParticleCircularHaloBuilder();
695     if (builder_ == nullptr) {
696         GE_LOGE("GEParticleCircularHaloShader::MakeParticleCircularHaloShader builder_ is nullptr.");
697         return nullptr;
698     }
699     builder_->SetChild("glowHalo", preCalcImgShader);
700     builder_->SetChild("particleHalo", particleHaloShader);
701     builder_->SetUniform("iResolution", width, height);
702     builder_->SetUniform("globalRadius", particleCircularHaloParams_.radius_);
703     builder_->SetUniform("rotationCenter", particleCircularHaloParams_.center_.first,
704         particleCircularHaloParams_.center_.second);
705     builder_->SetUniform("DOWN_SAMPLE_SCALE", DOWN_SAMPLE_SCALE);
706     auto particleCircularHaloShader = builder_->MakeShader(nullptr, false);
707     if (particleCircularHaloShader == nullptr) {
708         GE_LOGE(
709             "GEParticleCircularHaloShader::MakeParticleCircularHaloShader particleCircularHaloShader is nullptr.");
710         return nullptr;
711     }
712     return particleCircularHaloShader;
713 }
714 
715 }  // namespace Rosen
716 }  // namespace OHOS