* Copyright (c) 2025 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "ge_circle_flowlight_effect.h"
#include "ge_log.h"
#include "ge_shader_diagnostics.h"
#include "ge_visual_effect_impl.h"
#include <cmath>
namespace OHOS {
namespace Rosen {
namespace {
thread_local std::shared_ptr<Drawing::RuntimeEffect> g_circleFlowlightEffect = nullptr;
thread_local std::shared_ptr<Drawing::RuntimeEffect> g_circleFlowlightEffectWithMask = nullptr;
const std::string CIRCLE_FLOWLIGHT_SHADER(
R"(
uniform half2 iResolution;
uniform half4 color[4];
uniform half4 rotationFrequency;
uniform half4 rotationAmplitude;
uniform half4 rotationSeed;
uniform half4 gradientX;
uniform half4 gradientY;
uniform half strength[4];
uniform half distortStrength;
uniform half blendGradient;
uniform half progress;
float randWave(float frequency, float amplitude, float time, float seed)
{
// 78.233 is a random seed
return amplitude * sin(dot(vec2(time, seed), vec2(frequency, 78.233)));
}
vec4 colorGradient(vec2 fragPos, float radius, float timeValue)
{
vec4 colorOne = color[0];
vec4 colorTwo = color[1];
vec4 colorThree = color[2];
vec4 colorFour = color[3];
if (radius >= 1.0) {
return vec4(0.0);
}
float freqTime = -2.0 * timeValue;
mat2 rotationMatrix = mat2(cos(freqTime), sin(freqTime), -sin(freqTime), cos(freqTime));
vec2 gradientPos[4];
float wave = randWave(rotationFrequency.x, rotationAmplitude.x, timeValue, rotationSeed.x);
vec2 tempPos = rotationMatrix * (vec2(gradientX.x, gradientY.x) * 2.0 - 1.0);
gradientPos[0] = (tempPos + wave * normalize(tempPos)) * 0.5 + 0.5;
wave = randWave(rotationFrequency.y, rotationAmplitude.y, timeValue, rotationSeed.y);
tempPos = rotationMatrix * (vec2(gradientX.y, gradientY.y) * 2.0 - 1.0);
gradientPos[1] = (tempPos + wave * normalize(tempPos)) * 0.5 + 0.5;
wave = randWave(rotationFrequency.z, rotationAmplitude.z, timeValue, rotationSeed.z);
tempPos = rotationMatrix * (vec2(gradientX.z, gradientY.z) * 2.0 - 1.0);
gradientPos[2] = (tempPos + wave * normalize(tempPos)) * 0.5 + 0.5;
wave = randWave(rotationFrequency.w, rotationAmplitude.w, timeValue, rotationSeed.w);
tempPos = rotationMatrix * (vec2(gradientX.w, gradientY.w) * 2.0 - 1.0);
gradientPos[3] = (tempPos + wave * normalize(tempPos)) * 0.5 + 0.5;
float distorted_radius = pow(radius, distortStrength);
vec2 fragPosNew = fragPos / radius * distorted_radius;
vec2 gradientUV = fragPosNew * 0.5 + 0.5;
float gradientTotalWeight = 0.0;
vec4 gradientInterpColor = vec4(0.0);
float distance0 = max(0.01, length(gradientUV - gradientPos[0]));
float weight0 = strength[0] / pow(distance0, blendGradient);
gradientInterpColor += weight0 * colorOne;
gradientTotalWeight += weight0;
float distance1 = max(0.01, length(gradientUV - gradientPos[1]));
float weight1 = strength[1] / pow(distance1, blendGradient);
gradientInterpColor += weight1 * colorTwo;
gradientTotalWeight += weight1;
float distance2 = max(0.01, length(gradientUV - gradientPos[2]));
float weight2 = strength[2] / pow(distance2, blendGradient);
gradientInterpColor += weight2 * colorThree;
gradientTotalWeight += weight2;
float distance3 = max(0.01, length(gradientUV - gradientPos[3]));
float weight3 = strength[3] / pow(distance3, blendGradient);
gradientInterpColor += weight3 * colorFour;
gradientTotalWeight += weight3;
gradientInterpColor = pow(gradientInterpColor / gradientTotalWeight, vec4(1.0 / 2.2));
return gradientInterpColor;
}
float sdf_circle(vec2 uv, vec2 centerPos, float radius)
{
return length(uv - centerPos) - radius;
}
half4 main(vec2 fragCoord) {
vec2 fragPos = fragCoord.xy / iResolution.xy * 2.0 - 1.0;
float radius = length(fragPos);
vec4 gradient_color = colorGradient(fragPos, radius, progress);
return gradient_color;
}
)");
const std::string CIRCLE_FLOWLIGHT_SHADER_WITH_MASK(
R"(
uniform shader maskImageShader;
uniform half2 iResolution;
uniform half4 color[4];
uniform half4 rotationFrequency;
uniform half4 rotationAmplitude;
uniform half4 rotationSeed;
uniform half4 gradientX;
uniform half4 gradientY;
uniform half progress;
uniform half strength[4];
uniform half distortStrength;
uniform half blendGradient;
float randWave(float frequency, float amplitude, float time, float seed)
{
// 78.233 is a random seed
return amplitude * sin(dot(vec2(time, seed), vec2(frequency, 78.233)));
}
vec4 colorGradient(vec2 fragPos, float radius, float timeValue)
{
vec4 colorOne = color[0];
vec4 colorTwo = color[1];
vec4 colorThree = color[2];
vec4 colorFour = color[3];
if (radius >= 1.0) {
return vec4(0.0);
}
float freqTime = -2.0 * timeValue;
mat2 rotationMatrix = mat2(cos(freqTime), sin(freqTime), -sin(freqTime), cos(freqTime));
vec2 gradientPos[4];
float wave = randWave(rotationFrequency.x, rotationAmplitude.x, timeValue, rotationSeed.x);
vec2 tempPos = rotationMatrix * (vec2(gradientX.x, gradientY.x) * 2.0 - 1.0);
gradientPos[0] = (tempPos + wave * normalize(tempPos)) * 0.5 + 0.5;
wave = randWave(rotationFrequency.y, rotationAmplitude.y, timeValue, rotationSeed.y);
tempPos = rotationMatrix * (vec2(gradientX.y, gradientY.y) * 2.0 - 1.0);
gradientPos[1] = (tempPos + wave * normalize(tempPos)) * 0.5 + 0.5;
wave = randWave(rotationFrequency.z, rotationAmplitude.z, timeValue, rotationSeed.z);
tempPos = rotationMatrix * (vec2(gradientX.z, gradientY.z) * 2.0 - 1.0);
gradientPos[2] = (tempPos + wave * normalize(tempPos)) * 0.5 + 0.5;
wave = randWave(rotationFrequency.w, rotationAmplitude.w, timeValue, rotationSeed.w);
tempPos = rotationMatrix * (vec2(gradientX.w, gradientY.w) * 2.0 - 1.0);
gradientPos[3] = (tempPos + wave * normalize(tempPos)) * 0.5 + 0.5;
float distorted_radius = pow(radius, distortStrength);
vec2 fragPosNew = fragPos / radius * distorted_radius;
vec2 gradientUV = fragPosNew * 0.5 + 0.5;
float gradientTotalWeight = 0.0;
vec4 gradientInterpColor = vec4(0.0);
float distance0 = max(0.01, length(gradientUV - gradientPos[0]));
float weight0 = strength[0] / pow(distance0, blendGradient);
gradientInterpColor += weight0 * colorOne;
gradientTotalWeight += weight0;
float distance1 = max(0.01, length(gradientUV - gradientPos[1]));
float weight1 = strength[1] / pow(distance1, blendGradient);
gradientInterpColor += weight1 * colorTwo;
gradientTotalWeight += weight1;
float distance2 = max(0.01, length(gradientUV - gradientPos[2]));
float weight2 = strength[2] / pow(distance2, blendGradient);
gradientInterpColor += weight2 * colorThree;
gradientTotalWeight += weight2;
float distance3 = max(0.01, length(gradientUV - gradientPos[3]));
float weight3 = strength[3] / pow(distance3, blendGradient);
gradientInterpColor += weight3 * colorFour;
gradientTotalWeight += weight3;
gradientInterpColor = pow(gradientInterpColor / gradientTotalWeight, vec4(1.0 / 2.2));
return gradientInterpColor;
}
float sdf_circle(vec2 uv, vec2 centerPos, float radius)
{
return length(uv - centerPos) - radius;
}
half4 main(vec2 fragCoord) {
half maskValue = maskImageShader.eval(fragCoord).a;
if (maskValue < 0.0) {
return half4(0.0);
}
vec2 fragPos = fragCoord.xy / iResolution.xy * 2.0 - 1.0;
float radius = length(fragPos);
vec4 gradient_color = colorGradient(fragPos, radius, progress);
gradient_color *= maskValue;
return gradient_color;
}
)");
}
GECircleFlowlightEffect::GECircleFlowlightEffect(Drawing::GECircleFlowlightEffectParams& param)
{
for (int i = 0; i < ARRAY_NUM; i++) {
colors_[i * COLOR_CHANNEL] = param.colors[i].x_;
colors_[i * COLOR_CHANNEL + 1] = param.colors[i].y_;
colors_[i * COLOR_CHANNEL + 2] = param.colors[i].z_;
colors_[i * COLOR_CHANNEL + 3] = param.colors[i].w_;
}
rotationFrequency_ = param.rotationFrequency;
rotationAmplitude_ = param.rotationAmplitude;
rotationSeed_ = param.rotationSeed;
gradientX_ = param.gradientX;
gradientY_ = param.gradientY;
progress_ = param.progress;
mask_ = param.mask;
strength_ = param.strength;
distortStrength_ = param.distortStrength;
blendGradient_ = param.blendGradient;
}
void GECircleFlowlightEffect::MakeCircleFlowlightEffect()
{
if (g_circleFlowlightEffect != nullptr) {
return;
}
g_circleFlowlightEffect = GECreateRuntimeEffectForShader(CIRCLE_FLOWLIGHT_SHADER);
if (g_circleFlowlightEffect == nullptr) {
LOGE("GECircleFlowlightEffect::RuntimeShader circleFlowlightEffect create failed");
}
}
void GECircleFlowlightEffect::MakeCircleFlowlightEffectWithMask()
{
if (g_circleFlowlightEffectWithMask != nullptr) {
return;
}
g_circleFlowlightEffectWithMask = GECreateRuntimeEffectForShader(CIRCLE_FLOWLIGHT_SHADER_WITH_MASK);
if (g_circleFlowlightEffectWithMask == nullptr) {
LOGE("GECircleFlowlightEffect::RuntimeShader circleFlowlightEffectWithMask create failed");
}
}
bool GECircleFlowlightEffect::IsValidParam(float width, float height)
{
if (width < 1e-6 || height < 1e-6) {
LOGE("GECircleFlowlightEffect::MakeDrawingShader width or height less than 1e-6");
return false;
}
return true;
}
void GECircleFlowlightEffect::SetUniform(float width, float height)
{
if (mask_) {
builder_ = std::make_shared<Drawing::RuntimeShaderBuilder>(g_circleFlowlightEffectWithMask);
} else {
builder_ = std::make_shared<Drawing::RuntimeShaderBuilder>(g_circleFlowlightEffect);
}
builder_->SetUniform("iResolution", width, height);
builder_->SetUniform("color", colors_.data(), COLOR_CHANNEL * ARRAY_NUM);
builder_->SetUniform("rotationFrequency", rotationFrequency_.GetData(), ARRAY_NUM);
builder_->SetUniform("rotationAmplitude", rotationAmplitude_.GetData(), ARRAY_NUM);
builder_->SetUniform("rotationSeed", rotationSeed_.GetData(), ARRAY_NUM);
builder_->SetUniform("gradientX", gradientX_.GetData(), ARRAY_NUM);
builder_->SetUniform("gradientY", gradientY_.GetData(), ARRAY_NUM);
builder_->SetUniform("progress", progress_);
builder_->SetUniform("strength", strength_.GetData(), ARRAY_NUM);
builder_->SetUniform("distortStrength", distortStrength_);
builder_->SetUniform("blendGradient", blendGradient_);
}
void GECircleFlowlightEffect::MakeDrawingShader(const Drawing::Rect& rect, float progress)
{
drShader_ = nullptr;
auto width = rect.GetWidth();
auto height = rect.GetHeight();
if (!IsValidParam(width, height)) {
return;
}
if (mask_) {
MakeCircleFlowlightEffectWithMask();
if (!g_circleFlowlightEffectWithMask) {
return;
}
} else {
MakeCircleFlowlightEffect();
if (!g_circleFlowlightEffect) {
return;
}
}
SetUniform(width, height);
if (mask_) {
auto maskImageShader = mask_->GenerateDrawingShader(width, height);
if (!maskImageShader) {
LOGE("GECircleFlowlightEffect::MakeDrawingShader maskImageShader_ is null");
return;
}
builder_->SetChild("maskImageShader", maskImageShader);
}
auto circleFlowlightShader = builder_->MakeShader(nullptr, false);
if (circleFlowlightShader == nullptr) {
LOGE("GECircleFlowlightEffect::MakeDrawingShader circleFlowlightShader is nullptr.");
return;
}
drShader_ = circleFlowlightShader;
}
}
}