diff --git a/BUILD.gn b/BUILD.gn index 744c4e33cedd3543797c863128ae3c5d59bfd669..9b63cbbaeb28e3b78957a086d4878fc01354a08e 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -63,6 +63,7 @@ ohos_source_set("graphics_effect_src") { "src/ge_magnifier_shader_filter.cpp", "src/ge_mesa_blur_shader_filter.cpp", "src/ge_render.cpp", + "src/ge_sound_wave_filter.cpp", "src/ge_system_properties.cpp", "src/ge_visual_effect.cpp", "src/ge_visual_effect_container.cpp", diff --git a/include/ge_shader_filter_params.h b/include/ge_shader_filter_params.h index 43ef6c9f08289e31642f7e13f15d9c302ee9cce9..c25c850d1b51d1a6d695613b0023b86c11b9bb60 100644 --- a/include/ge_shader_filter_params.h +++ b/include/ge_shader_filter_params.h @@ -20,6 +20,7 @@ #include #include "utils/matrix.h" +#include "common/rs_color.h" namespace OHOS { namespace Rosen { @@ -53,6 +54,33 @@ struct GEWaterRippleFilterParams { uint32_t rippleMode = 1; }; +constexpr char GE_FILTER_SOUND_WAVE[] = "SOUND_WAVE"; +constexpr char GE_FILTER_SOUND_WAVE_COLOR_A[] = "COLORA"; +constexpr char GE_FILTER_SOUND_WAVE_COLOR_B[] = "COLORB"; +constexpr char GE_FILTER_SOUND_WAVE_COLOR_C[] = "COLORC"; +constexpr char GE_FILTER_SOUND_WAVE_COLORPROGRESS[] = "COLORPROGRESS"; +constexpr char GE_FILTER_SOUND_WAVE_CENTERBRIGHTNESS[] = "CENTERBRIGHTNESS"; +constexpr char GE_FILTER_SOUND_WAVE_SOUNDINTENSITY[] = "SOUNDINTENSITY"; +constexpr char GE_FILTER_SOUND_WAVE_SHOCKWAVEALPHA_A[] = "SHOCKWAVEALPHAA"; +constexpr char GE_FILTER_SOUND_WAVE_SHOCKWAVEALPHA_B[] = "SHOCKWAVEALPHAB"; +constexpr char GE_FILTER_SOUND_WAVE_SHOCKWAVEPROGRESS_A[] = "SHOCKWAVEPROGRESSA"; +constexpr char GE_FILTER_SOUND_WAVE_SHOCKWAVEPROGRESS_B[] = "SHOCKWAVEPROGRESSB"; +struct GESoundWaveFilterParams { + //sound wave + uint32_t colorA = 0xFFFFFFFF; + uint32_t colorB = 0xFFFFFFFF; + uint32_t colorC = 0xFFFFFFFF; + float colorProgress = 0.0f; + float centerBrightness = 1.0f; + float soundIntensity = 0.0f; + + //shock wave + float shockWaveAlphaA = 1.0f; + float shockWaveAlphaB = 1.0f; + float shockWaveProgressA = 0.0f; + float shockWaveProgressB = 0.0f; +}; + constexpr char GE_FILTER_GREY[] = "GREY"; constexpr char GE_FILTER_GREY_COEF_1[] = "GREY_COEF_1"; constexpr char GE_FILTER_GREY_COEF_2[] = "GREY_COEF_2"; diff --git a/include/ge_sound_wave_filter.h b/include/ge_sound_wave_filter.h new file mode 100644 index 0000000000000000000000000000000000000000..584da0445151ea154fe3a857195bf03767fb1ad1 --- /dev/null +++ b/include/ge_sound_wave_filter.h @@ -0,0 +1,188 @@ +/* + * 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. + */ +#ifndef GRAPHICS_EFFECT_GE_SOUND_WAVE_FILTER_H +#define GRAPHICS_EFFECT_GE_SOUND_WAVE_FILTER_H + +#include + +#include "ge_shader_filter.h" +#include "ge_visual_effect.h" + +#include "draw/canvas.h" +#include "effect/color_filter.h" +#include "effect/runtime_effect.h" +#include "effect/runtime_shader_builder.h" +#include "image/image.h" +#include "utils/matrix.h" +#include "utils/rect.h" + +namespace OHOS { +namespace Rosen { +class GESoundWaveFilter : public GEShaderFilter { +public: + GESoundWaveFilter(const Drawing::GESoundWaveFilterParams& params); + ~GESoundWaveFilter() override = default; + + std::shared_ptr ProcessImage(Drawing::Canvas& canvas, const std::shared_ptr image, + const Drawing::Rect& src, const Drawing::Rect& dst) override; + +private: + std::shared_ptr GetSoundWaveEffect(); + // sound wave + Drawing::Color colorA_ = Drawing::Color::COLOR_WHITE; + Drawing::Color colorB_ = Drawing::Color::COLOR_WHITE; + Drawing::Color colorC_ = Drawing::Color::COLOR_WHITE; + float colorProgress_ = 0.0f; + float centerBrightness_ = 1.0f; + float soundIntensity_ = 0.0f; + + // shock wave + float shockWaveAlphaA_ = 1.0f; + float shockWaveAlphaB_ = 1.0f; + float shockWaveProgressA_ = 0.0f; + float shockWaveProgressB_ = 0.0f; + + inline static const std::string shaderStringSoundWave = R"( + uniform shader image; + uniform half2 iResolution; + uniform vec3 colorA; + uniform vec3 colorB; + uniform vec3 colorC; + uniform half colorProgress; + uniform half centerBrightness; + uniform half soundIntensity; + uniform half shockWaveAlphaA; + uniform half shockWaveAlphaB; + uniform half shockWaveProgressA; + uniform half shockWaveProgressB; + + float smin(float a, float b, float k) + { + k *= 6.0; + float h = max(k-abs(a-b), 0.0)/k; + return min(a, b) - h*h*h*k*(1.0/6.0); + } + + // Create a smooth color gradient effect based on the threshold over X or Y or user defined + // Ideal for movement, but not for rotation + vec3 colorGradient(vec3 colorA, vec3 colorB, float startPos, float endPos, float threshold) + { + float stepValue = (threshold >= startPos && threshold <= endPos) ? 1.0 : 0.0; + vec3 returnValue = mix(colorA, colorB, smoothstep(startPos, endPos, threshold)) * stepValue; + return returnValue; + } + + vec3 colorWheel(vec2 uv, vec2 circleCenter, float circleRadius, float animationTime) + { + float mask = length(uv+circleCenter)/circleRadius; + float distanceFromCenter = fract(mask-animationTime); + + vec3 color = colorGradient(colorA, colorB, 0.0, 0.2, distanceFromCenter) + + colorGradient(colorB, colorC, 0.2, 0.6, distanceFromCenter) + + colorGradient(colorC, colorA, 0.6, 1.0, distanceFromCenter); + color *= (1.0 - step(1.0, mask)); + return color; + } + + vec4 soundWaveDistortionEffects(vec2 screenUVs, vec2 centeredUVs, float animationTime) + { + vec2 lightPulseUVs = centeredUVs + vec2(0.0, 1.12); // uv minus pulse center position + float frequency = fract(animationTime); // frequency of distortion waves + float radius = mix(0.17, 0.68, frequency); + float lightPulseDistance = length(lightPulseUVs) - radius; + + float lightPulseThickness = 0.12; + float lightPulse = smoothstep(lightPulseThickness, -0.025, abs(lightPulseDistance)); + if (lightPulse > 0.0) + { + float animationMask = smoothstep(1.0, 0.4, frequency); + + vec2 directionVector = normalize(lightPulseUVs); + vec2 normal = directionVector * lightPulseDistance * lightPulse*animationMask; + vec2 refractedUVs = clamp(mix(screenUVs, screenUVs - normal * 0.25, 0.3), 0.001, 0.999); + return vec4(refractedUVs, max(normal.y,0.0), lightPulse); + } + return vec4(screenUVs, 0.0, 0.0); + } + + vec3 soundWaveLightEffects(vec2 uv, vec2 centeredUVs, vec3 currentColor, vec3 centerColor) + { + float circleRadius = 0.125; + + // Control the height of the circle + float circleHeight = mix(-0.2, 0.03, soundIntensity); + float spreadX = pow(100.0, -soundIntensity) + 1.0;// Control the spread of the mask across X + + float smoothUnionThreshold = mix(0.0657, 0.09, soundIntensity); + float horizonOffset = -0.02; + vec2 circlePosition = vec2(0.0, circleHeight); + centeredUVs.y += 1.0; + centeredUVs.y += mix(0.09, 0.0, soundIntensity); + float circleSDF = length(centeredUVs-circlePosition)-circleRadius ; + circleSDF += smoothUnionThreshold; + float smoothUnionDistance = smin(circleSDF, centeredUVs.y-horizonOffset, smoothUnionThreshold); + float horizontalGradient = smoothstep(0.9 * spreadX, 0.0, abs(uv.x*2.0-1.0)); + float smoothGap = mix(0.08, 0.1085, horizontalGradient); + float smoothUnion = smoothstep(smoothGap, -0.035, mix(0.0, 0.66, smoothUnionDistance)); + + // Control the spread of the mask across X + smoothUnion *= mix(0.65, 1.0, horizontalGradient); + + float brightnessValue = centerBrightness *smoothstep(5.0, 0.0, circleSDF); + + return currentColor+centerColor*smoothUnion* brightnessValue; + } + + half4 main(float2 fragCoord) { + vec2 uv = fragCoord.xy/iResolution.xy; + uv.y = 1.0 - uv.y; + if (uv.y>0.3) { + return vec4(0.0); + } + float screenRatio = iResolution.x/iResolution.y; + vec2 centeredUVs = uv*2.0 - 1.0; + centeredUVs.x *= screenRatio; + vec2 screenUVs = uv; + + vec3 finalColor = vec3(0.); + vec3 centerColor = colorWheel(centeredUVs, vec2(0.0, 1.0), 2.125, colorProgress); + + // Shock wave distort + vec4 soundWaveDistortionA = vec4(0.0); + vec4 soundWaveDistortionB = vec4(0.0); + soundWaveDistortionA = soundWaveDistortionEffects(uv, centeredUVs, shockWaveProgressA); + soundWaveDistortionB = soundWaveDistortionEffects(soundWaveDistortionA.xy, centeredUVs, shockWaveProgressB); + uv = soundWaveDistortionB.xy; + + finalColor.rgb = image.eval(vec2(uv.x, 1.0 - uv.y) * iResolution.xy).rgb; + // Sound wave Effect + finalColor.rgb = soundWaveLightEffects(screenUVs, centeredUVs, finalColor.rgb, centerColor); + + // Shock wave Effect: Add sutil light from the refraction distortion + finalColor.rgb = finalColor.rgb + centerColor * vec3(soundWaveDistortionA.z) * shockWaveAlphaA; + finalColor.rgb = finalColor.rgb + centerColor * vec3(pow(soundWaveDistortionA.w, 6.0))*0.3 * shockWaveAlphaA; + + finalColor.rgb = finalColor.rgb + centerColor * vec3(soundWaveDistortionB.z) * shockWaveAlphaB; + finalColor.rgb = finalColor.rgb + centerColor * vec3(pow(soundWaveDistortionB.w, 6.0))*0.3 * shockWaveAlphaB; + + return vec4(finalColor, 1.0); + } + )"; +}; + +} // namespace Rosen +} // namespace OHOS + +#endif // GRAPHICS_EFFECT_GE_SOUND_WAVE_FILTER_H \ No newline at end of file diff --git a/include/ge_visual_effect_impl.h b/include/ge_visual_effect_impl.h index 66f87485767b6c78053d53e6eefb1776afe4092d..71e35f6d4fbfb71b8701fd20f155469816f02457 100644 --- a/include/ge_visual_effect_impl.h +++ b/include/ge_visual_effect_impl.h @@ -43,6 +43,7 @@ public: FLOW_LIGHT_SWEEP, COMPLEX_SHADER, EDGE_LIGHT, + SOUND_WAVE, MAX }; @@ -153,6 +154,16 @@ public: return edgeLightParams_; } + void MakeSoundWaveParams() + { + soundWaveParams_ = std::make_shared(); + } + + const std::shared_ptr& GetSoundWaveParams() const + { + return soundWaveParams_; + } + private: static std::map> g_initialMap; @@ -166,6 +177,8 @@ private: void SetWaterRippleParams(const std::string& tag, float param); void SetEdgeLightParams(const std::string& tag, float param); + void SetSoundWaveParamsUint32(const std::string& tag, uint32_t param); + void SetSoundWaveParamsFloat(const std::string& tag, float param); FilterType filterType_ = GEVisualEffectImpl::FilterType::NONE; @@ -178,6 +191,7 @@ private: std::shared_ptr magnifierParams_ = nullptr; std::shared_ptr waterRippleParams_ = nullptr; std::shared_ptr edgeLightParams_ = nullptr; + std::shared_ptr soundWaveParams_ = nullptr; }; diff --git a/src/ge_render.cpp b/src/ge_render.cpp index 684776efa4e121b946a65df62c2acd6b61db3138..920a675020d2c992a47c49108f41a0a5bf7545c0 100644 --- a/src/ge_render.cpp +++ b/src/ge_render.cpp @@ -24,6 +24,7 @@ #include "ge_visual_effect_impl.h" #include "ge_water_ripple_filter.h" #include "ge_edge_light_shader_filter.h" +#include "ge_sound_wave_filter.h" #include "ge_external_dynamic_loader.h" namespace OHOS { @@ -162,6 +163,11 @@ std::vector> GERender::GenerateShaderFilter( shaderFilter = GenerateExtShaderFilter(ve); break; } + case Drawing::GEVisualEffectImpl::FilterType::SOUND_WAVE: { + const auto& soundWaveParams = ve->GetSoundWaveParams(); + shaderFilter = std::make_shared(*soundWaveParams); + break; + } default: break; } diff --git a/src/ge_sound_wave_filter.cpp b/src/ge_sound_wave_filter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f0c3a6f1ec2878ea9b13e14a33359e19790fe126 --- /dev/null +++ b/src/ge_sound_wave_filter.cpp @@ -0,0 +1,100 @@ +/* + * 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 + +#include "ge_log.h" +#include "ge_sound_wave_filter.h" + +namespace OHOS { +namespace Rosen { + +namespace { +constexpr static uint8_t COLOR_CHANNEL = 3; // 3 len of rgb +} // namespace + +GESoundWaveFilter::GESoundWaveFilter(const Drawing::GESoundWaveFilterParams& params) + :colorProgress_(params.colorProgress), centerBrightness_(params.centerBrightness), + soundIntensity_(params.soundIntensity), shockWaveAlphaA_(params.shockWaveAlphaA), + shockWaveAlphaB_(params.shockWaveAlphaB), shockWaveProgressA_(params.shockWaveProgressA), + shockWaveProgressB_(params.shockWaveProgressB) +{ + colorA_ = Drawing::Color(params.colorA); + colorB_ = Drawing::Color(params.colorB); + colorC_ = Drawing::Color(params.colorC); +} + + +std::shared_ptr GESoundWaveFilter::ProcessImage(Drawing::Canvas& canvas, + const std::shared_ptr image, const Drawing::Rect& src, const Drawing::Rect& dst) +{ + if (image == nullptr) { + LOGE("GESoundWaveFilter::ProcessImage input is invalid"); + return nullptr; + } + + Drawing::Matrix matrix; + auto shader = Drawing::ShaderEffect::CreateImageShader(*image, Drawing::TileMode::CLAMP, + Drawing::TileMode::CLAMP, Drawing::SamplingOptions(Drawing::FilterMode::LINEAR), matrix); + auto imageInfo = image->GetImageInfo(); + float height = imageInfo.GetHeight(); + float width = imageInfo.GetWidth(); + if (height < 1e-6 || width < 1e-6) { + return nullptr; + } + auto soundWaveShader = GetSoundWaveEffect(); + if (soundWaveShader == nullptr) { + LOGE("GESoundWaveFilter::ProcessImage g_SoundWaveEffect init failed"); + return nullptr; + } + + float colorA[COLOR_CHANNEL] = {colorA_.GetRedF(), colorA_.GetGreenF(), colorA_.GetBlueF()}; + float colorB[COLOR_CHANNEL] = {colorB_.GetRedF(), colorB_.GetGreenF(), colorB_.GetBlueF()}; + float colorC[COLOR_CHANNEL] = {colorC_.GetRedF(), colorC_.GetGreenF(), colorC_.GetBlueF()}; + + Drawing::RuntimeShaderBuilder builder(soundWaveShader); + builder.SetChild("image", shader); + builder.SetUniform("iResolution", width, height); + builder.SetUniform("colorA", colorA, COLOR_CHANNEL); + builder.SetUniform("colorB", colorB, COLOR_CHANNEL); + builder.SetUniform("colorC", colorC, COLOR_CHANNEL); + builder.SetUniform("colorProgress", colorProgress_); + builder.SetUniform("centerBrightness", centerBrightness_); + builder.SetUniform("soundIntensity", soundIntensity_); + builder.SetUniform("shockWaveAlphaA", shockWaveAlphaA_); + builder.SetUniform("shockWaveAlphaB", shockWaveAlphaB_); + builder.SetUniform("shockWaveProgressA", shockWaveProgressA_); + builder.SetUniform("shockWaveProgressB", shockWaveProgressB_); + + auto invertedImage = builder.MakeImage(canvas.GetGPUContext().get(), nullptr, imageInfo, false); + if (invertedImage == nullptr) { + LOGE("GESoundWaveFilter::ProcessImage make image failed"); + return nullptr; + } + return invertedImage; +} + +std::shared_ptr GESoundWaveFilter::GetSoundWaveEffect() +{ + static std::shared_ptr g_soundWaveShader = nullptr; + if (g_soundWaveShader == nullptr) { + g_soundWaveShader = Drawing::RuntimeEffect::CreateForShader(shaderStringSoundWave); + } + return g_soundWaveShader; +} + + +} // namespace Rosen +} // namespace OHOS \ No newline at end of file diff --git a/src/ge_visual_effect_impl.cpp b/src/ge_visual_effect_impl.cpp index cce0f0fda41ba19b1ced99ed1e56be2b67942776..8bf887992374940deb59595f481a0168975e84cf 100644 --- a/src/ge_visual_effect_impl.cpp +++ b/src/ge_visual_effect_impl.cpp @@ -70,6 +70,12 @@ std::map> GEVisualEf impl->SetFilterType(GEVisualEffectImpl::FilterType::EDGE_LIGHT); impl->MakeEdgeLightParams(); } + }, + { GE_FILTER_SOUND_WAVE, + [](GEVisualEffectImpl* impl) { + impl->SetFilterType(GEVisualEffectImpl::FilterType::SOUND_WAVE); + impl->MakeSoundWaveParams(); + } } }; @@ -201,6 +207,10 @@ void GEVisualEffectImpl::SetParam(const std::string& tag, float param) SetEdgeLightParams(tag, param); break; } + case FilterType::SOUND_WAVE: { + SetSoundWaveParamsFloat(tag, param); + break; + } default: break; } @@ -276,6 +286,10 @@ void GEVisualEffectImpl::SetParam(const std::string& tag, const uint32_t param) } break; } + case FilterType::SOUND_WAVE: { + SetSoundWaveParamsUint32(tag, param); + break; + } default: break; } @@ -492,6 +506,58 @@ void GEVisualEffectImpl::SetEdgeLightParams(const std::string& tag, float param) } } +void GEVisualEffectImpl::SetSoundWaveParamsUint32(const std::string& tag, uint32_t param) +{ + if (soundWaveParams_ == nullptr) { + return; + } + + static std::unordered_map> actions = { + + { GE_FILTER_SOUND_WAVE_COLOR_A, + [](GEVisualEffectImpl* obj, uint32_t p) { obj->soundWaveParams_->colorA = p; } }, + { GE_FILTER_SOUND_WAVE_COLOR_B, + [](GEVisualEffectImpl* obj, uint32_t p) { obj->soundWaveParams_->colorB = p; } }, + { GE_FILTER_SOUND_WAVE_COLOR_C, + [](GEVisualEffectImpl* obj, uint32_t p) { obj->soundWaveParams_->colorC = p; } }, + }; + + auto it = actions.find(tag); + if (it != actions.end()) { + it->second(this, param); + } +} + +void GEVisualEffectImpl::SetSoundWaveParamsFloat(const std::string& tag, float param) +{ + if (soundWaveParams_ == nullptr) { + return; + } + + static std::unordered_map> actions = { + + { GE_FILTER_SOUND_WAVE_COLORPROGRESS, + [](GEVisualEffectImpl* obj, float p) { obj->soundWaveParams_->colorProgress = p; } }, + { GE_FILTER_SOUND_WAVE_CENTERBRIGHTNESS, + [](GEVisualEffectImpl* obj, float p) { obj->soundWaveParams_->centerBrightness = p; } }, + { GE_FILTER_SOUND_WAVE_SOUNDINTENSITY, + [](GEVisualEffectImpl* obj, float p) { obj->soundWaveParams_->soundIntensity = p; } }, + { GE_FILTER_SOUND_WAVE_SHOCKWAVEALPHA_A, + [](GEVisualEffectImpl* obj, float p) { obj->soundWaveParams_->shockWaveAlphaA = p; } }, + { GE_FILTER_SOUND_WAVE_SHOCKWAVEALPHA_B, + [](GEVisualEffectImpl* obj, float p) { obj->soundWaveParams_->shockWaveAlphaB = p; } }, + { GE_FILTER_SOUND_WAVE_SHOCKWAVEPROGRESS_A, + [](GEVisualEffectImpl* obj, float p) { obj->soundWaveParams_->shockWaveProgressA = p; } }, + { GE_FILTER_SOUND_WAVE_SHOCKWAVEPROGRESS_B, + [](GEVisualEffectImpl* obj, float p) { obj->soundWaveParams_->shockWaveProgressB = p; } }, + }; + + auto it = actions.find(tag); + if (it != actions.end()) { + it->second(this, param); + } +} + } // namespace Drawing } // namespace Rosen } // namespace OHOS