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 "effect_image_chain.h"
17
18 #include "effect_utils.h"
19 #include "ge_mesa_blur_shader_filter.h"
20 #include "rs_trace.h"
21
22 #include "pipeline/rs_paint_filter_canvas.h"
23 #include "platform/common/rs_system_properties.h"
24 #include "render/rs_hps_blur.h"
25 #include "render/rs_pixel_map_util.h"
26
27 #ifdef RS_ENABLE_VK
28 #include "effect_vulkan_context.h"
29 #endif
30
31 namespace OHOS::Rosen {
PixelFormatToDrawingColorType(const Media::PixelFormat & pixelFormat)32 Drawing::ColorType ImageUtil::PixelFormatToDrawingColorType(const Media::PixelFormat& pixelFormat)
33 {
34 switch (pixelFormat) {
35 case Media::PixelFormat::RGB_565: {
36 return Drawing::ColorType::COLORTYPE_RGB_565;
37 }
38 case Media::PixelFormat::RGBA_8888: {
39 return Drawing::ColorType::COLORTYPE_RGBA_8888;
40 }
41 case Media::PixelFormat::BGRA_8888: {
42 return Drawing::ColorType::COLORTYPE_BGRA_8888;
43 }
44 case Media::PixelFormat::ALPHA_8: {
45 return Drawing::ColorType::COLORTYPE_ALPHA_8;
46 }
47 case Media::PixelFormat::RGBA_F16: {
48 return Drawing::ColorType::COLORTYPE_RGBA_F16;
49 }
50 case Media::PixelFormat::RGBA_1010102: {
51 return Drawing::ColorType::COLORTYPE_RGBA_1010102;
52 }
53 default: {
54 return Drawing::ColorType::COLORTYPE_UNKNOWN;
55 }
56 }
57 }
58
AlphaTypeToDrawingAlphaType(const Media::AlphaType & alphaType)59 Drawing::AlphaType ImageUtil::AlphaTypeToDrawingAlphaType(const Media::AlphaType& alphaType)
60 {
61 switch (alphaType) {
62 case Media::AlphaType::IMAGE_ALPHA_TYPE_OPAQUE: {
63 return Drawing::AlphaType::ALPHATYPE_OPAQUE;
64 }
65 case Media::AlphaType::IMAGE_ALPHA_TYPE_PREMUL: {
66 return Drawing::AlphaType::ALPHATYPE_PREMUL;
67 }
68 case Media::AlphaType::IMAGE_ALPHA_TYPE_UNPREMUL: {
69 return Drawing::AlphaType::ALPHATYPE_UNPREMUL;
70 }
71 default: {
72 return Drawing::AlphaType::ALPHATYPE_UNKNOWN;
73 }
74 }
75 }
76
Prepare(const std::shared_ptr<Media::PixelMap> & srcPixelMap,bool forceCPU)77 DrawingError EffectImageChain::Prepare(const std::shared_ptr<Media::PixelMap>& srcPixelMap, bool forceCPU)
78 {
79 std::lock_guard<std::mutex> lock(apiMutex_);
80 if (!CheckPixelMap(srcPixelMap)) {
81 return DrawingError::ERR_ILLEGAL_INPUT;
82 }
83 srcPixelMap_ = srcPixelMap;
84
85 if (InitWithoutCanvas(srcPixelMap_) != DrawingError::ERR_OK) {
86 EFFECT_LOG_E("EffectImageChain::Prepare: Failed to init.");
87 return DrawingError::ERR_IMAGE_NULL;
88 }
89
90 forceCPU_ = forceCPU;
91 surface_ = CreateSurface(forceCPU_);
92 if (surface_ == nullptr) {
93 EFFECT_LOG_E("EffectImageChain::Prepare: Failed to create surface %{public}d.", forceCPU_);
94 return DrawingError::ERR_SURFACE;
95 }
96 canvas_ = surface_->GetCanvas();
97
98 prepared_ = true;
99 return DrawingError::ERR_OK;
100 }
101
ApplyDrawingFilter(const std::shared_ptr<Drawing::ImageFilter> & filter)102 DrawingError EffectImageChain::ApplyDrawingFilter(const std::shared_ptr<Drawing::ImageFilter>& filter)
103 {
104 if (filter == nullptr) {
105 EFFECT_LOG_E("EffectImageChain::ApplyDrawingFilter: The filter is null.");
106 return DrawingError::ERR_ILLEGAL_INPUT;
107 }
108
109 std::lock_guard<std::mutex> lock(apiMutex_);
110 filters_ = (filters_ == nullptr) ? filter : Drawing::ImageFilter::CreateComposeImageFilter(filter, filters_);
111 return DrawingError::ERR_OK;
112 }
113
ApplyBlur(float radius,const Drawing::TileMode & tileMode)114 DrawingError EffectImageChain::ApplyBlur(float radius, const Drawing::TileMode& tileMode)
115 {
116 if (radius < 0.0f) { // invalid radius
117 return DrawingError::ERR_ILLEGAL_INPUT;
118 }
119
120 std::lock_guard<std::mutex> lock(apiMutex_);
121 if (!prepared_) { // blur need prepare first
122 EFFECT_LOG_E("EffectImageChain::ApplyBlur: Not ready, need prepare first.");
123 return DrawingError::ERR_NOT_PREPARED;
124 }
125
126 if (forceCPU_) {
127 auto filter = Drawing::ImageFilter::CreateBlurImageFilter(radius, radius, tileMode, nullptr);
128 filters_ = (filters_ == nullptr) ? filter : Drawing::ImageFilter::CreateComposeImageFilter(filter, filters_);
129 return DrawingError::ERR_OK;
130 }
131
132 if (filters_ != nullptr) {
133 DrawOnFilter(); // need draw first to ensure cascading
134 image_ = surface_->GetImageSnapshot();
135 filters_ = nullptr; // clear filters_ to avoid apply again
136 }
137
138 auto isHpsApplied = (RSSystemProperties::GetHpsBlurEnabled() && tileMode == Drawing::TileMode::CLAMP &&
139 (ApplyHpsBlur(radius) == DrawingError::ERR_OK));
140 if (isHpsApplied) { // apply hps blur success
141 return DrawingError::ERR_OK;
142 }
143
144 return ApplyMesaBlur(radius, tileMode);
145 }
146
ApplyMesaBlur(float radius,const Drawing::TileMode & tileMode)147 DrawingError EffectImageChain::ApplyMesaBlur(float radius, const Drawing::TileMode& tileMode)
148 {
149 ROSEN_TRACE_BEGIN(HITRACE_TAG_GRAPHIC_AGP, "EffectImageChain::ApplyMesaBlur");
150 Drawing::GEMESABlurShaderFilterParams params { radius, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, static_cast<int>(tileMode),
151 0.f, 0.f };
152 auto mesaBlurFilter = std::make_shared<GEMESABlurShaderFilter>(params);
153 image_ = mesaBlurFilter->ProcessImage(*canvas_, image_,
154 Drawing::Rect(0, 0, srcPixelMap_->GetWidth(), srcPixelMap_->GetHeight()),
155 Drawing::Rect(0, 0, srcPixelMap_->GetWidth(), srcPixelMap_->GetHeight()));
156 ROSEN_TRACE_END(HITRACE_TAG_GRAPHIC_AGP);
157 return DrawingError::ERR_OK;
158 }
159
ApplyHpsBlur(float radius)160 DrawingError EffectImageChain::ApplyHpsBlur(float radius)
161 {
162 ROSEN_TRACE_BEGIN(HITRACE_TAG_GRAPHIC_AGP, "EffectImageChain::ApplyHpsBlur");
163 auto ret = DrawingError::ERR_OK;
164 do {
165 if (std::ceil(radius) == 0) { // exception when applying hps blur on 0, need handle separately
166 break;
167 }
168
169 RSPaintFilterCanvas canvas(surface_.get());
170 Drawing::AutoCanvasRestore autoRestore(canvas, true);
171 auto hpsParam = Drawing::HpsBlurParameter(Drawing::Rect(0, 0, image_->GetWidth(), image_->GetHeight()),
172 Drawing::Rect(0, 0, image_->GetWidth(), image_->GetHeight()), radius, 1.f, 1.f);
173 if (!HpsBlurFilter::GetHpsBlurFilter().ApplyHpsBlur(canvas, image_, hpsParam, 1.f)) {
174 EFFECT_LOG_E("EffectImageChain::ApplyHpsBlur: Failed to apply hps blur");
175 ret = DrawingError::ERR_FAST_BLUR;
176 break;
177 }
178 image_ = surface_->GetImageSnapshot();
179 } while (false);
180
181 ROSEN_TRACE_END(HITRACE_TAG_GRAPHIC_AGP);
182 return ret;
183 }
184
DrawOnFilter()185 void EffectImageChain::DrawOnFilter()
186 {
187 ROSEN_TRACE_BEGIN(HITRACE_TAG_GRAPHIC_AGP, "EffectImageChain::DrawOnFilter");
188 // Step1: Setup Paint
189 Drawing::Paint paint;
190 paint.SetStyle(Drawing::Paint::PaintStyle::PAINT_FILL);
191 paint.SetAntiAlias(true);
192 paint.SetBlendMode(Drawing::BlendMode::SRC);
193 if (filters_ != nullptr) {
194 Drawing::Filter filter;
195 filter.SetImageFilter(filters_);
196 paint.SetFilter(filter);
197 }
198
199 // Step2: Draw image
200 canvas_->Save();
201 canvas_->ResetMatrix();
202 canvas_->AttachPaint(paint);
203 canvas_->DrawImage(*image_, 0, 0, Drawing::SamplingOptions());
204 canvas_->DetachPaint();
205 canvas_->Restore();
206 ROSEN_TRACE_END(HITRACE_TAG_GRAPHIC_AGP);
207 }
208
Draw()209 DrawingError EffectImageChain::Draw()
210 {
211 std::lock_guard<std::mutex> lock(apiMutex_);
212 ROSEN_TRACE_BEGIN(HITRACE_TAG_GRAPHIC_AGP, "EffectImageChain::Draw");
213 auto ret = DrawingError::ERR_OK;
214 do {
215 if (!prepared_) {
216 EFFECT_LOG_E("EffectImageChain::Draw: Not ready, need prepare first.");
217 ret = DrawingError::ERR_NOT_PREPARED;
218 break;
219 }
220
221 DrawOnFilter();
222
223 bool needRead = (!forceCPU_ && dstPixelMap_);
224 if (needRead && !canvas_->ReadPixels(imageInfo_, reinterpret_cast<void*>(dstPixelMap_->GetWritablePixels()),
225 static_cast<uint32_t>(dstPixelMap_->GetRowStride()), 0, 0)) {
226 EFFECT_LOG_E("EffectImageChain::Draw: Failed to readPixels to target Pixmap.");
227 ret = DrawingError::ERR_PIXEL_READ;
228 break;
229 }
230 } while (false);
231
232 ROSEN_TRACE_END(HITRACE_TAG_GRAPHIC_AGP);
233 return ret;
234 }
235
CheckPixelMap(const std::shared_ptr<Media::PixelMap> & pixelMap)236 bool EffectImageChain::CheckPixelMap(const std::shared_ptr<Media::PixelMap>& pixelMap)
237 {
238 if (pixelMap == nullptr) {
239 EFFECT_LOG_E("EffectImageChain::CheckPixelMap: The srcPixelMap is nullptr.");
240 return false;
241 }
242
243 if (pixelMap->GetWidth() <= 0 || pixelMap->GetHeight() <= 0) {
244 EFFECT_LOG_E("EffectImageChain::CheckPixelMap: The width or height is invalid.");
245 return false;
246 }
247
248 if (pixelMap->GetPixelFormat() == Media::PixelFormat::UNKNOWN) {
249 EFFECT_LOG_E("EffectImageChain::CheckPixelMap: The pixel format is invalid.");
250 return false;
251 }
252
253 if (pixelMap->GetAlphaType() == Media::AlphaType::IMAGE_ALPHA_TYPE_UNKNOWN) {
254 EFFECT_LOG_E("EffectImageChain::CheckPixelMap: The alpha type is invalid.");
255 return false;
256 }
257
258 return true;
259 }
260
InitWithoutCanvas(const std::shared_ptr<Media::PixelMap> & srcPixelMap)261 DrawingError EffectImageChain::InitWithoutCanvas(const std::shared_ptr<Media::PixelMap>& srcPixelMap)
262 {
263 imageInfo_ = Drawing::ImageInfo { srcPixelMap_->GetWidth(), srcPixelMap_->GetHeight(),
264 ImageUtil::PixelFormatToDrawingColorType(srcPixelMap_->GetPixelFormat()),
265 ImageUtil::AlphaTypeToDrawingAlphaType(srcPixelMap_->GetAlphaType()),
266 RSPixelMapUtil::GetPixelmapColorSpace(srcPixelMap_) };
267
268 Drawing::Bitmap bitmap;
269 bitmap.InstallPixels(imageInfo_, reinterpret_cast<void*>(srcPixelMap_->GetWritablePixels()),
270 static_cast<uint32_t>(srcPixelMap_->GetRowStride()));
271 image_ = std::make_shared<Drawing::Image>();
272 image_->BuildFromBitmap(bitmap);
273
274 Media::InitializationOptions opts;
275 opts.size = { srcPixelMap_->GetWidth(), srcPixelMap_->GetHeight() };
276 opts.pixelFormat = srcPixelMap_->GetPixelFormat();
277 opts.alphaType = srcPixelMap_->GetAlphaType();
278 opts.editable = true;
279 opts.useDMA = true;
280 auto dstPixelMap = Media::PixelMap::Create(opts);
281 if (dstPixelMap == nullptr) {
282 image_ = nullptr;
283 EFFECT_LOG_E("EffectImageChain::InitWithoutCanvas: Failed to create the dstPixelMap.");
284 return DrawingError::ERR_IMAGE_NULL;
285 }
286 dstPixelMap_ = std::shared_ptr<Media::PixelMap>(dstPixelMap.release());
287
288 EFFECT_LOG_I("Init w %{public}d, h %{public}d, f %{public}d, alpha %{public}d, useDMA 1",
289 opts.size.width, opts.size.height, srcPixelMap_->GetPixelFormat(), srcPixelMap_->GetAlphaType());
290 return DrawingError::ERR_OK;
291 }
292
GetPixelMap()293 std::shared_ptr<Media::PixelMap> EffectImageChain::GetPixelMap()
294 {
295 std::lock_guard<std::mutex> lock(apiMutex_);
296 return dstPixelMap_;
297 }
298
CreateSurface(bool forceCPU)299 std::shared_ptr<Drawing::Surface> EffectImageChain::CreateSurface(bool forceCPU)
300 {
301 if (forceCPU) { // dstPixelMap_ must not null by called
302 auto address = const_cast<uint32_t*>(dstPixelMap_->GetPixel32(0, 0));
303 return Drawing::Surface::MakeRasterDirect(imageInfo_, address, dstPixelMap_->GetRowStride());
304 }
305
306 #ifdef RS_ENABLE_GPU
307 std::shared_ptr<Drawing::GPUContext> context = nullptr;
308 if (RSSystemProperties::GetGpuApiType() == GpuApiType::OPENGL) {
309 renderContext_ = std::make_shared<RenderContext>();
310 renderContext_->InitializeEglContext();
311 renderContext_->SetUpGpuContext();
312 context = renderContext_->GetSharedDrGPUContext();
313 }
314
315 #ifdef RS_ENABLE_VK
316 if (RSSystemProperties::IsUseVulkan()) {
317 context = EffectVulkanContext::GetSingleton().CreateDrawingContext();
318 }
319 #endif
320
321 if (context == nullptr) {
322 EFFECT_LOG_E("EffectImageChain::CreateGPUSurface: create gpuContext failed.");
323 return nullptr;
324 }
325 return Drawing::Surface::MakeRenderTarget(context.get(), false, imageInfo_);
326 #else
327 EFFECT_LOG_W("EffectImageChain::CreateGPUSurface: GPU rendering is not supported.");
328 return nullptr;
329 #endif
330 }
331 } // namespace OHOS::Rosen