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
16 #include "render/rs_hps_blur.h"
17
18 #include <mutex>
19
20 #include "common/rs_common_def.h"
21 #include "common/rs_optional_trace.h"
22 #include "draw/surface.h"
23 #include "effect/runtime_shader_builder.h"
24 #include "platform/common/rs_log.h"
25 #include "platform/common/rs_system_properties.h"
26 #include "rs_profiler.h"
27
28 namespace OHOS {
29 namespace Rosen {
30
31 static constexpr uint32_t MAX_SURFACE_SIZE = 10000;
32 constexpr float MAX_ALPHA = 255.0f;
33 static std::shared_ptr<Drawing::RuntimeEffect> g_mixEffect;
34 std::mutex g_mixEffectMutex;
35
GetShaderTransform(const Drawing::Rect & blurRect,float scaleW,float scaleH)36 Drawing::Matrix HpsBlurFilter::GetShaderTransform(const Drawing::Rect& blurRect, float scaleW, float scaleH)
37 {
38 Drawing::Matrix matrix;
39 matrix.SetScale(scaleW, scaleH);
40 Drawing::Matrix translateMatrix;
41 translateMatrix.Translate(blurRect.GetLeft(), blurRect.GetTop());
42 matrix.PostConcat(translateMatrix);
43 return matrix;
44 }
45
GetMixEffect() const46 std::shared_ptr<Drawing::RuntimeEffect> HpsBlurFilter::GetMixEffect() const
47 {
48 static const std::string mixString(R"(
49 uniform shader blurredInput;
50 uniform float inColorFactor;
51
52 highp float random(float2 xy) {
53 float t = dot(xy, float2(78.233, 12.9898));
54 return fract(sin(t) * 43758.5453);
55 }
56 half4 main(float2 xy) {
57 highp float noiseGranularity = inColorFactor / 255.0;
58 half4 finalColor = blurredInput.eval(xy);
59 float noise = mix(-noiseGranularity, noiseGranularity, random(xy));
60 finalColor.rgb += noise;
61 return finalColor;
62 }
63 )");
64
65 if (g_mixEffect == nullptr) {
66 std::unique_lock<std::mutex> lock(g_mixEffectMutex);
67 if (g_mixEffect == nullptr) {
68 g_mixEffect = Drawing::RuntimeEffect::CreateForShader(mixString);
69 if (g_mixEffect == nullptr) {
70 return nullptr;
71 }
72 }
73 }
74 return g_mixEffect;
75 }
76
ApplyMaskColorFilter(Drawing::Canvas & offscreenCanvas,float alpha,const RSColor & maskColor) const77 float HpsBlurFilter::ApplyMaskColorFilter(Drawing::Canvas& offscreenCanvas, float alpha,
78 const RSColor& maskColor) const
79 {
80 float newAlpha = alpha;
81 if (maskColor != RgbPalette::Transparent()) {
82 float maskColorAlpha = static_cast<float>(maskColor.GetAlpha()) / MAX_ALPHA;
83 newAlpha += maskColorAlpha - alpha * maskColorAlpha;
84 Drawing::Brush maskBrush;
85 maskBrush.SetColor(maskColor.AsArgbInt());
86 if (ROSEN_EQ(newAlpha, 0.f)) {
87 maskBrush.SetAlphaF(0.f);
88 } else {
89 maskBrush.SetAlphaF(maskColorAlpha / newAlpha);
90 }
91 ROSEN_LOGD("HpsBlurFilter::ApplyHpsBlur newMaskColor %{public}#x, alpha = %{public}f",
92 maskColor.AsArgbInt(), alpha);
93 offscreenCanvas.DrawBackground(maskBrush);
94 }
95 return newAlpha;
96 }
97
ApplyHpsBlur(Drawing::Canvas & canvas,const std::shared_ptr<Drawing::Image> & image,const Drawing::HpsBlurParameter & param,float alpha,std::shared_ptr<Drawing::ColorFilter> colorFilter,const RSColor & maskColor) const98 bool HpsBlurFilter::ApplyHpsBlur(Drawing::Canvas& canvas, const std::shared_ptr<Drawing::Image>& image,
99 const Drawing::HpsBlurParameter& param, float alpha, std::shared_ptr<Drawing::ColorFilter> colorFilter,
100 const RSColor& maskColor) const
101 {
102 auto surface = canvas.GetSurface();
103 if (surface == nullptr || image == nullptr) {
104 return false;
105 }
106 std::array<int, 2> dimension = canvas.CalcHpsBluredImageDimension(param); // There are 2 variables
107 if (dimension[0] <= 0 || dimension[1] <= 0 || dimension[0] >= static_cast<int>(MAX_SURFACE_SIZE)
108 || dimension[1] >= static_cast<int>(MAX_SURFACE_SIZE)) {
109 ROSEN_LOGD("HpsBlurFilter::ApplyHpsBlur CalcHpsBluredImageDimension error");
110 return false;
111 }
112 std::shared_ptr<Drawing::Surface> offscreenSurface = surface->MakeSurface(dimension[0], dimension[1]);
113 if (offscreenSurface == nullptr) {
114 return false;
115 }
116
117 std::shared_ptr<Drawing::Canvas> offscreenCanvas = offscreenSurface->GetCanvas();
118 if (offscreenCanvas == nullptr) {
119 return false;
120 }
121 Drawing::Rect dimensionRect = {0, 0, dimension[0], dimension[1]};
122 auto offscreenHpsParam = Drawing::HpsBlurParameter(param.src, dimensionRect, param.sigma,
123 param.saturation, param.brightness);
124 if (!offscreenCanvas->DrawBlurImage(*image, offscreenHpsParam)) {
125 return false;
126 }
127 RS_PROFILER_ADD_HPS_BLUR_METRICS(static_cast<uint32_t>(image->GetWidth() * image->GetHeight()));
128
129 alpha = ApplyMaskColorFilter(*offscreenCanvas, alpha, maskColor);
130
131 auto imageCache = offscreenSurface->GetImageSnapshot();
132 if (imageCache == nullptr) {
133 return false;
134 }
135 auto dst = param.dst;
136 Drawing::Brush brush;
137 Drawing::SamplingOptions linear(Drawing::FilterMode::LINEAR, Drawing::MipmapMode::NONE);
138 const auto blurMatrix = GetShaderTransform(dst, dst.GetWidth() / imageCache->GetWidth(),
139 dst.GetHeight() / imageCache->GetHeight());
140 const auto blurShader = Drawing::ShaderEffect::CreateImageShader(*imageCache, Drawing::TileMode::CLAMP,
141 Drawing::TileMode::CLAMP, linear, blurMatrix);
142 if (!SetShaderEffect(brush, blurShader, imageCache)) { return false; }
143 if (colorFilter != nullptr) {
144 Drawing::Filter filter;
145 filter.SetColorFilter(colorFilter);
146 brush.SetFilter(filter);
147 }
148 brush.SetAlphaF(alpha);
149 canvas.AttachBrush(brush);
150 canvas.DrawRect(dst);
151 canvas.DetachBrush();
152 return true;
153 }
154
SetShaderEffect(Drawing::Brush & brush,std::shared_ptr<Drawing::ShaderEffect> blurShader,std::shared_ptr<Drawing::Image> imageCache) const155 bool HpsBlurFilter::SetShaderEffect(Drawing::Brush& brush, std::shared_ptr<Drawing::ShaderEffect> blurShader,
156 std::shared_ptr<Drawing::Image> imageCache) const
157 {
158 if (blurShader == nullptr || imageCache == nullptr) {
159 return false;
160 }
161 static auto factor = RSSystemProperties::GetHpsBlurNoiseFactor();
162 ROSEN_LOGD("HpsBlurFilter::ApplyHpsBlur HpsBlurNoise %{public}f", factor);
163 static constexpr float epsilon = 0.1f;
164 if (!ROSEN_LE(factor, epsilon)) {
165 auto mixEffect = GetMixEffect();
166 if (mixEffect == nullptr) {
167 return false;
168 }
169 Drawing::RuntimeShaderBuilder mixBuilder(mixEffect);
170 mixBuilder.SetChild("blurredInput", blurShader);
171 ROSEN_LOGD("HpsBlurFilter::HpsBlurNoise factor : %{public}f", factor);
172 mixBuilder.SetUniform("inColorFactor", factor);
173 brush.SetShaderEffect(mixBuilder.MakeShader(nullptr, imageCache->IsOpaque()));
174 } else {
175 brush.SetShaderEffect(blurShader);
176 }
177 return true;
178 }
179
180 } // namespace Rosen
181 } // namespace OHOS