1 /*
2  * Copyright (c) 2024-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 "common/rs_background_thread.h"
17 #include "src/gpu/gl/GrGLDefines.h"
18 #include "rs_trace.h"
19 #include "pipeline/rs_uni_render_processor.h"
20 #include "pipeline/rs_uni_render_util.h"
21 #include "pipeline/sk_resource_manager.h"
22 #include "rs_pointer_render_manager.h"
23 
24 namespace OHOS {
25 namespace Rosen {
26 namespace {
27 static const std::string DISPLAY = "DisplayNode";
28 static const std::string POINTER = "pointer";
29 static const float RGB = 255.0f;
30 static const float HALF = 0.5f;
31 static const int32_t CALCULATE_MIDDLE = 2;
32 } // namespace
33 static std::unique_ptr<RSPointerRenderManager> g_pointerRenderManagerInstance =
34     std::make_unique<RSPointerRenderManager>();
35 
GetInstance()36 RSPointerRenderManager& RSPointerRenderManager::GetInstance()
37 {
38     return *g_pointerRenderManagerInstance;
39 }
40 
41 #if defined (RS_ENABLE_VK)
InitInstance(const std::shared_ptr<RSVkImageManager> & vkImageManager)42 void RSPointerRenderManager::InitInstance(const std::shared_ptr<RSVkImageManager>& vkImageManager)
43 {
44     g_pointerRenderManagerInstance->vkImageManager_ = vkImageManager;
45 }
46 #endif
47 
48 #if defined (RS_ENABLE_GL) && defined (RS_ENABLE_EGLIMAGE)
InitInstance(const std::shared_ptr<RSEglImageManager> & eglImageManager)49 void RSPointerRenderManager::InitInstance(const std::shared_ptr<RSEglImageManager>& eglImageManager)
50 {
51     g_pointerRenderManagerInstance->eglImageManager_ = eglImageManager;
52 }
53 #endif
54 
GetCurrentTime()55 int64_t RSPointerRenderManager::GetCurrentTime()
56 {
57     auto timeNow = std::chrono::time_point_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now());
58     return std::chrono::duration_cast<std::chrono::milliseconds>(timeNow.time_since_epoch()).count();
59 }
60 
SetPointerColorInversionConfig(float darkBuffer,float brightBuffer,int64_t interval,int32_t rangeSize)61 void RSPointerRenderManager::SetPointerColorInversionConfig(float darkBuffer, float brightBuffer,
62     int64_t interval, int32_t rangeSize)
63 {
64     std::lock_guard<std::mutex> lock(cursorInvertMutex_);
65     darkBuffer_ = darkBuffer;
66     brightBuffer_ = brightBuffer;
67     colorSamplingInterval_ = interval;
68     rangeSize_ = rangeSize;
69 }
70 
SetPointerColorInversionEnabled(bool enable)71 void RSPointerRenderManager::SetPointerColorInversionEnabled(bool enable)
72 {
73     std::lock_guard<std::mutex> lock(cursorInvertMutex_);
74     isEnableCursorInversion_ = enable;
75 }
76 
RegisterPointerLuminanceChangeCallback(pid_t pid,sptr<RSIPointerLuminanceChangeCallback> callback)77 void RSPointerRenderManager::RegisterPointerLuminanceChangeCallback(pid_t pid,
78     sptr<RSIPointerLuminanceChangeCallback> callback)
79 {
80     std::lock_guard<std::mutex> lock(cursorInvertMutex_);
81     colorChangeListeners_[pid] = callback;
82 }
83 
UnRegisterPointerLuminanceChangeCallback(pid_t pid)84 void RSPointerRenderManager::UnRegisterPointerLuminanceChangeCallback(pid_t pid)
85 {
86     std::lock_guard<std::mutex> lock(cursorInvertMutex_);
87     colorChangeListeners_.erase(pid);
88 }
89 
ExecutePointerLuminanceChangeCallback(int32_t brightness)90 void RSPointerRenderManager::ExecutePointerLuminanceChangeCallback(int32_t brightness)
91 {
92     std::lock_guard<std::mutex> lock(cursorInvertMutex_);
93     lastColorPickerTime_ = RSPointerRenderManager::GetCurrentTime();
94     for (auto it = colorChangeListeners_.begin(); it != colorChangeListeners_.end(); it++) {
95         if (it->second) {
96             it->second->OnPointerLuminanceChanged(brightness);
97         }
98     }
99 }
100 
CallPointerLuminanceChange(int32_t brightness)101 void RSPointerRenderManager::CallPointerLuminanceChange(int32_t brightness)
102 {
103     RS_LOGD("RSPointerRenderManager::CallPointerLuminanceChange luminance_:%{public}d.", luminance_);
104     if (brightnessMode_ == CursorBrightness::NONE) {
105         brightnessMode_ = brightness < static_cast<int32_t>(RGB * HALF) ?
106             CursorBrightness::DARK : CursorBrightness::BRIGHT;
107         ExecutePointerLuminanceChangeCallback(brightness);
108     } else if (brightnessMode_ == CursorBrightness::DARK) {
109         // Dark cursor to light cursor buffer
110         if (brightness > static_cast<int32_t>(RGB * darkBuffer_)) {
111             brightnessMode_ = CursorBrightness::BRIGHT;
112             ExecutePointerLuminanceChangeCallback(brightness);
113         }
114     } else {
115         // light cursor to Dark cursor buffer
116         if (brightness < static_cast<int32_t>(RGB * brightBuffer_)) {
117             brightnessMode_ = CursorBrightness::DARK;
118             ExecutePointerLuminanceChangeCallback(brightness);
119         }
120     }
121 }
122 
CheckColorPickerEnabled()123 bool RSPointerRenderManager::CheckColorPickerEnabled()
124 {
125     if (!isEnableCursorInversion_ || taskDoing_) {
126         return false;
127     }
128 
129     auto time = RSPointerRenderManager::GetCurrentTime() - lastColorPickerTime_;
130     if (time < colorSamplingInterval_) {
131         return false;
132     }
133 
134     bool exists = false;
135     auto& threadParams = RSUniRenderThread::Instance().GetRSRenderThreadParams();
136     if (threadParams == nullptr) {
137         ROSEN_LOGE("RSPointerRenderManager::CheckColorPickerEnabled threadParams == nullptr");
138         return false;
139     }
140     auto& hardwareDrawables = threadParams->GetHardwareEnabledTypeDrawables();
141 
142     for (const auto& drawable : hardwareDrawables) {
143         auto surfaceDrawable = std::static_pointer_cast<DrawableV2::RSSurfaceRenderNodeDrawable>(drawable);
144         if (surfaceDrawable != nullptr && surfaceDrawable->IsHardwareEnabledTopSurface()) {
145             exists = true;
146             break;
147         }
148     }
149 
150     return exists;
151 }
152 
ProcessColorPicker(std::shared_ptr<RSProcessor> processor,std::shared_ptr<Drawing::GPUContext> gpuContext)153 void RSPointerRenderManager::ProcessColorPicker(std::shared_ptr<RSProcessor> processor,
154     std::shared_ptr<Drawing::GPUContext> gpuContext)
155 {
156     if (!CheckColorPickerEnabled()) {
157         ROSEN_LOGD("RSPointerRenderManager::CheckColorPickerEnabled is false!");
158         return;
159     }
160 
161     RS_TRACE_BEGIN("RSPointerRenderManager ProcessColorPicker");
162     std::lock_guard<std::mutex> locker(mtx_);
163 
164     if (cacheImgForPointer_) {
165         if (!GetIntersectImageBySubset(gpuContext)) {
166             ROSEN_LOGE("RSPointerRenderManager::GetIntersectImageBySubset is false!");
167             return;
168         }
169     } else {
170         if (!CalculateTargetLayer(processor)) { // get the layer intersect with pointer and calculate the rect
171             ROSEN_LOGD("RSPointerRenderManager::CalculateTargetLayer is false!");
172             return;
173         }
174     }
175 
176     // post color picker task to background thread
177     RunColorPickerTask();
178     RS_TRACE_END();
179 }
180 
GetIntersectImageBySubset(std::shared_ptr<Drawing::GPUContext> gpuContext)181 bool RSPointerRenderManager::GetIntersectImageBySubset(std::shared_ptr<Drawing::GPUContext> gpuContext)
182 {
183     auto& hardwareDrawables =
184         RSUniRenderThread::Instance().GetRSRenderThreadParams()->GetHardwareEnabledTypeDrawables();
185     for (const auto& drawable : hardwareDrawables) {
186         auto surfaceDrawable = std::static_pointer_cast<DrawableV2::RSSurfaceRenderNodeDrawable>(drawable);
187         if (surfaceDrawable == nullptr || !surfaceDrawable->IsHardwareEnabledTopSurface() ||
188             !surfaceDrawable->GetRenderParams()) {
189             continue;
190         }
191         image_ = std::make_shared<Drawing::Image>();
192         RectI pointerRect = surfaceDrawable->GetRenderParams()->GetAbsDrawRect();
193         CalculateColorRange(pointerRect);
194         Drawing::RectI drawingPointerRect = Drawing::RectI(pointerRect.GetLeft(), pointerRect.GetTop(),
195             pointerRect.GetRight(), pointerRect.GetBottom());
196         image_->BuildSubset(cacheImgForPointer_, drawingPointerRect, *gpuContext);
197 
198         Drawing::TextureOrigin origin = Drawing::TextureOrigin::BOTTOM_LEFT;
199         backendTexture_ = image_->GetBackendTexture(false, &origin);
200         bitmapFormat_ = Drawing::BitmapFormat{ image_->GetColorType(), image_->GetAlphaType() };
201         return true;
202     }
203     return false;
204 }
205 
CalculateTargetLayer(std::shared_ptr<RSProcessor> processor)206 bool RSPointerRenderManager::CalculateTargetLayer(std::shared_ptr<RSProcessor> processor)
207 {
208     auto uniRenderProcessor = std::static_pointer_cast<RSUniRenderProcessor>(processor);
209     if (uniRenderProcessor == nullptr) {
210         ROSEN_LOGE("RSPointerRenderManager::CalculateTargetLayer uniRenderProcessor is null!");
211         return false;
212     }
213     std::vector<LayerInfoPtr> layers = uniRenderProcessor->GetLayers();
214     forceCPU_ = RSBaseRenderEngine::NeedForceCPU(layers);
215 
216     std::sort(layers.begin(), layers.end(), [](LayerInfoPtr a, LayerInfoPtr b) {
217         return a->GetZorder() < b->GetZorder();
218     });
219     // get pointer and display node layer
220     bool find = false;
221     RectI pRect;
222     int displayNodeIndex = INT_MAX;
223     for (int i = 0; i < layers.size(); ++i) {
224         std::string name = layers[i]->GetSurface()->GetName();
225         if (name.find(DISPLAY) != std::string::npos) {
226             displayNodeIndex = i;
227             continue;
228         }
229         if (name.find(POINTER) != std::string::npos) {
230             GraphicIRect rect = layers[i]->GetLayerSize();
231             pRect.SetAll(rect.x, rect.y, rect.w, rect.h);
232             find = true;
233             continue;
234         }
235     }
236 
237     if (!find || displayNodeIndex == INT_MAX) {
238         ROSEN_LOGD("RSPointerRenderManager::CalculateTargetLayer cannot find pointer or display node.");
239         return false;
240     }
241     CalculateColorRange(pRect);
242     // calculate the max intersection layer and rect
243     GetRectAndTargetLayer(layers, pRect, displayNodeIndex);
244 
245     return true;
246 }
247 
CalculateColorRange(RectI & pRect)248 void RSPointerRenderManager::CalculateColorRange(RectI& pRect)
249 {
250     if (rangeSize_ > 0) {
251         int left = pRect.GetLeft() + (pRect.GetWidth() - rangeSize_) / CALCULATE_MIDDLE;
252         int top = pRect.GetTop() + (pRect.GetHeight() - rangeSize_) / CALCULATE_MIDDLE;
253         pRect.SetAll(left, top, rangeSize_, rangeSize_);
254     }
255 }
256 
GetRectAndTargetLayer(std::vector<LayerInfoPtr> & layers,RectI & pRect,int displayNodeIndex)257 void RSPointerRenderManager::GetRectAndTargetLayer(std::vector<LayerInfoPtr>& layers, RectI& pRect,
258     int displayNodeIndex)
259 {
260     target_ = nullptr;
261     rect_.Clear();
262 
263     for (int i = std::max(0, displayNodeIndex - 1); i >= 0; --i) {
264         if (layers[i]->GetSurface()->GetName().find(POINTER) != std::string::npos) {
265             continue;
266         }
267         GraphicIRect layerSize = layers[i]->GetLayerSize();
268         RectI curRect = RectI(layerSize.x, layerSize.y, layerSize.w, layerSize.h);
269         if (!curRect.Intersect(pRect)) {
270             continue;
271         }
272         RectI dstRect = curRect.IntersectRect(pRect);
273         if (dstRect.width_ * dstRect.height_ >= rect_.width_ * rect_.height_) {
274             dstRect = dstRect.Offset(-curRect.GetLeft(), -curRect.GetTop());
275             rect_ = dstRect;
276             target_ = layers[i];
277         }
278     }
279 
280     // if not intersect with other layers, calculate the displayNode intersection rect
281     if (target_ == nullptr) {
282         target_ = layers[displayNodeIndex];
283         GraphicIRect layerSize = target_->GetLayerSize();
284         RectI curRect = RectI(layerSize.x, layerSize.y, layerSize.w, layerSize.h);
285         RectI dstRect = curRect.IntersectRect(pRect);
286         rect_ = dstRect.Offset(-curRect.GetLeft(), -curRect.GetTop());
287     }
288 }
289 
CalcAverageLuminance(std::shared_ptr<Drawing::Image> image)290 int16_t RSPointerRenderManager::CalcAverageLuminance(std::shared_ptr<Drawing::Image> image)
291 {
292     // create a 1x1 SkPixmap
293     uint32_t pixel[1] = { 0 };
294     Drawing::ImageInfo single_pixel_info(1, 1, Drawing::ColorType::COLORTYPE_RGBA_8888,
295         Drawing::AlphaType::ALPHATYPE_PREMUL);
296     Drawing::Bitmap single_pixel;
297     single_pixel.Build(single_pixel_info, single_pixel_info.GetBytesPerPixel());
298     single_pixel.SetPixels(pixel);
299 
300     // resize image to 1x1 to calculate average color
301     // kMedium_SkFilterQuality will do bilerp + mipmaps for down-scaling, we can easily get average color
302     image->ScalePixels(single_pixel,
303         Drawing::SamplingOptions(Drawing::FilterMode::LINEAR, Drawing::MipmapMode::LINEAR));
304     // convert color format and return average luminance
305     auto color = SkColor4f::FromBytes_RGBA(pixel[0]).toSkColor();
306     return Drawing::Color::ColorQuadGetR(color) * 0.2126f + Drawing::Color::ColorQuadGetG(color) * 0.7152f +
307         Drawing::Color::ColorQuadGetB(color) * 0.0722f;
308 }
309 
RunColorPickerTaskBackground(const BufferDrawParam & param)310 void RSPointerRenderManager::RunColorPickerTaskBackground(const BufferDrawParam& param)
311 {
312 #ifdef RS_ENABLE_UNI_RENDER
313     std::lock_guard<std::mutex> locker(mtx_);
314     std::shared_ptr<Drawing::Image> image;
315     auto context = RSBackgroundThread::Instance().GetShareGPUContext().get();
316     if (backendTexturePre_.IsValid()) {
317         image = std::make_shared<Drawing::Image>();
318         SharedTextureContext* sharedContext = new SharedTextureContext(image_);
319         bool ret = image->BuildFromTexture(*context, backendTexturePre_.GetTextureInfo(),
320             Drawing::TextureOrigin::BOTTOM_LEFT, bitmapFormat_, nullptr,
321             SKResourceManager::DeleteSharedTextureContext, sharedContext);
322         if (!ret) {
323             RS_LOGE("RSPointerRenderManager::RunColorPickerTaskBackground BuildFromTexture failed.");
324             return;
325         }
326     } else {
327         image = GetIntersectImageByLayer(param);
328     }
329     if (image == nullptr || !image->IsValid(context)) {
330         RS_LOGE("RSPointerRenderManager::RunColorPickerTaskBackground image not valid.");
331         return;
332     }
333 
334     luminance_ = CalcAverageLuminance(image);
335 
336     CallPointerLuminanceChange(luminance_);
337 #endif
338 }
339 
RunColorPickerTask()340 void RSPointerRenderManager::RunColorPickerTask()
341 {
342     if (!image_ && (target_ == nullptr || rect_.IsEmpty())) {
343         ROSEN_LOGE("RSPointerRenderManager::RunColorPickerTask failed for null target or rect is empty!");
344         return;
345     }
346 
347     BufferDrawParam param;
348     if (!image_) {
349         param = RSUniRenderUtil::CreateLayerBufferDrawParam(target_, forceCPU_);
350     }
351     RSBackgroundThread::Instance().PostTask([this, param] () {
352         taskDoing_ = true;
353         this->RunColorPickerTaskBackground(param);
354         taskDoing_ = false;
355         backendTexturePre_ = backendTexture_;
356         backendTexture_ = Drawing::BackendTexture(false);
357         image_ = nullptr;
358     });
359 }
360 
GetIntersectImageByLayer(const BufferDrawParam & param)361 std::shared_ptr<Drawing::Image> RSPointerRenderManager::GetIntersectImageByLayer(const BufferDrawParam& param)
362 {
363 #ifdef RS_ENABLE_UNI_RENDER
364     Drawing::RectI imgCutRect = Drawing::RectI(rect_.GetLeft(), rect_.GetTop(), rect_.GetRight(), rect_.GetBottom());
365     auto context = RSBackgroundThread::Instance().GetShareGPUContext();
366     if (context == nullptr) {
367         RS_LOGE("RSPointerRenderManager::GetIntersectImageByLayer context is nullptr.");
368         return nullptr;
369     }
370     if (param.buffer == nullptr) {
371         ROSEN_LOGE("RSPointerRenderManager::GetIntersectImageByLayer param buffer is nullptr");
372         return nullptr;
373     }
374 #ifdef RS_ENABLE_VK
375     if (RSSystemProperties::GetGpuApiType() == GpuApiType::VULKAN
376         || RSSystemProperties::GetGpuApiType() == GpuApiType::DDGR) {
377         return GetIntersectImageFromVK(imgCutRect, context, param);
378         }
379 #endif
380 #if defined(RS_ENABLE_GL) && defined(RS_ENABLE_EGLIMAGE)
381     if (RSSystemProperties::GetGpuApiType() == GpuApiType::OPENGL) {
382         return GetIntersectImageFromGL(imgCutRect, context, param);
383     }
384 #endif
385 #endif
386     return nullptr;
387 }
388 
389 #ifdef RS_ENABLE_VK
GetIntersectImageFromVK(Drawing::RectI & imgCutRect,std::shared_ptr<Drawing::GPUContext> & context,const BufferDrawParam & param)390 std::shared_ptr<Drawing::Image> RSPointerRenderManager::GetIntersectImageFromVK(Drawing::RectI& imgCutRect,
391     std::shared_ptr<Drawing::GPUContext>& context, const BufferDrawParam& param)
392 {
393     if (vkImageManager_ == nullptr) {
394         RS_LOGE("RSPointerRenderManager::GetIntersectImageFromVK vkImageManager_ == nullptr");
395         return nullptr;
396     }
397     auto imageCache = vkImageManager_->CreateImageCacheFromBuffer(param.buffer, param.acquireFence);
398     if (imageCache == nullptr) {
399         ROSEN_LOGE("RSPointerRenderManager::GetIntersectImageFromVK imageCache == nullptr!");
400         return nullptr;
401     }
402     auto& backendTexture = imageCache->GetBackendTexture();
403     Drawing::BitmapFormat bitmapFormat = RSBaseRenderUtil::GenerateDrawingBitmapFormat(param.buffer);
404 
405     std::shared_ptr<Drawing::Image> layerImage = std::make_shared<Drawing::Image>();
406     if (!layerImage->BuildFromTexture(*context, backendTexture.GetTextureInfo(),
407         Drawing::TextureOrigin::TOP_LEFT, bitmapFormat, nullptr,
408         NativeBufferUtils::DeleteVkImage, imageCache->RefCleanupHelper())) {
409         ROSEN_LOGE("RSPointerRenderManager::GetIntersectImageFromVK image BuildFromTexture failed.");
410         return nullptr;
411     }
412 
413     std::shared_ptr<Drawing::Image> cutDownImage = std::make_shared<Drawing::Image>();
414     cutDownImage->BuildSubset(layerImage, imgCutRect, *context);
415     return cutDownImage;
416 }
417 #endif
418 
419 #if defined (RS_ENABLE_GL) && defined (RS_ENABLE_EGLIMAGE)
GetIntersectImageFromGL(Drawing::RectI & imgCutRect,std::shared_ptr<Drawing::GPUContext> & context,const BufferDrawParam & param)420 std::shared_ptr<Drawing::Image> RSPointerRenderManager::GetIntersectImageFromGL(Drawing::RectI& imgCutRect,
421     std::shared_ptr<Drawing::GPUContext>& context, const BufferDrawParam& param)
422 {
423     if (eglImageManager_ == nullptr) {
424         RS_LOGE("RSPointerRenderManager::GetIntersectImageFromGL eglImageManager_ == nullptr");
425         return nullptr;
426     }
427     auto eglTextureId = eglImageManager_->MapEglImageFromSurfaceBuffer(param.buffer,
428         param.acquireFence, param.threadIndex);
429     if (eglTextureId == 0) {
430         RS_LOGE("RSPointerRenderManager::GetIntersectImageFromGL invalid texture ID");
431         return nullptr;
432     }
433 
434     Drawing::BitmapFormat bitmapFormat = RSBaseRenderUtil::GenerateDrawingBitmapFormat(param.buffer);
435     Drawing::TextureInfo externalTextureInfo;
436     externalTextureInfo.SetWidth(param.buffer->GetSurfaceBufferWidth());
437     externalTextureInfo.SetHeight(param.buffer->GetSurfaceBufferHeight());
438     externalTextureInfo.SetIsMipMapped(false);
439     externalTextureInfo.SetTarget(GL_TEXTURE_EXTERNAL_OES);
440     externalTextureInfo.SetID(eglTextureId);
441     auto glType = GR_GL_RGBA8;
442     auto pixelFmt = param.buffer->GetFormat();
443     if (pixelFmt == GRAPHIC_PIXEL_FMT_BGRA_8888) {
444         glType = GR_GL_BGRA8;
445     } else if (pixelFmt == GRAPHIC_PIXEL_FMT_YCBCR_P010 || pixelFmt == GRAPHIC_PIXEL_FMT_YCRCB_P010) {
446         glType = GL_RGB10_A2;
447     }
448     externalTextureInfo.SetFormat(glType);
449 
450     std::shared_ptr<Drawing::Image> layerImage = std::make_shared<Drawing::Image>();
451     if (!layerImage->BuildFromTexture(*context, externalTextureInfo,
452         Drawing::TextureOrigin::TOP_LEFT, bitmapFormat, nullptr)) {
453         RS_LOGE("RSPointerRenderManager::GetIntersectImageFromGL image BuildFromTexture failed");
454         return nullptr;
455     }
456 
457     std::shared_ptr<Drawing::Image> cutDownImage = std::make_shared<Drawing::Image>();
458     cutDownImage->BuildSubset(layerImage, imgCutRect, *context);
459     Drawing::ImageInfo info = Drawing::ImageInfo(imgCutRect.GetWidth(), imgCutRect.GetHeight(),
460         Drawing::COLORTYPE_RGBA_8888, Drawing::ALPHATYPE_PREMUL);
461 
462     std::shared_ptr<Drawing::Surface> surface = Drawing::Surface::MakeRenderTarget(context.get(), false, info);
463     if (surface == nullptr) {
464         RS_LOGE("RSPointerRenderManager::GetIntersectImageFromGL MakeRenderTarget failed.");
465         return nullptr;
466     }
467     auto drawCanvas = std::make_shared<RSPaintFilterCanvas>(surface.get());
468     drawCanvas->DrawImage(*cutDownImage, 0.f, 0.f, Drawing::SamplingOptions());
469     surface->FlushAndSubmit(true);
470     return surface.get()->GetImageSnapshot();
471 }
472 #endif
473 } // namespace Rosen
474 } // namespace OHOS