1 /*
2 * Copyright (c) 2023 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 "property/rs_color_picker_cache_task.h"
17
18 #include <atomic>
19
20 #ifndef USE_ROSEN_DRAWING
21 #include "include/gpu/GrBackendSurface.h"
22 #include "src/image/SkImage_Base.h"
23 #else
24 #include "effect/runtime_effect.h"
25 #include "effect/runtime_shader_builder.h"
26 #endif
27
28 #if defined(NEW_SKIA)
29 #include "include/gpu/GrDirectContext.h"
30 #else
31 #include "include/gpu/GrContext.h"
32 #endif
33
34 #include "common/rs_optional_trace.h"
35 #include "platform/common/rs_log.h"
36 #include "platform/common/rs_system_properties.h"
37
38 namespace OHOS {
39 namespace Rosen {
40 #define CHECK_CACHE_PROCESS_STATUS \
41 do { \
42 if (cacheProcessStatus_.load() == CacheProcessStatus::WAITING) { \
43 return false; \
44 } \
45 } while (false)
46
47 const bool RSColorPickerCacheTask::ColorPickerPartialEnabled =
48 RSSystemProperties::GetColorPickerPartialEnabled() && RSUniRenderJudgement::IsUniRender();
49
50 #ifndef USE_ROSEN_DRAWING
InitSurface(GrRecordingContext * grContext)51 bool RSColorPickerCacheTask::InitSurface(GrRecordingContext* grContext)
52 #else
53 bool RSColorPickerCacheTask::InitSurface(Drawing::GPUContext* gpuContext)
54 #endif
55 {
56 RS_TRACE_NAME("RSColorPickerCacheTask InitSurface");
57 if (cacheSurface_ != nullptr) {
58 return true;
59 }
60 #ifdef IS_OHOS
61 auto runner = AppExecFwk::EventRunner::Current();
62 handler_ = std::make_shared<AppExecFwk::EventHandler>(runner);
63 #endif
64 #ifndef USE_ROSEN_DRAWING
65 SkImageInfo info = SkImageInfo::MakeN32Premul(surfaceSize_.width(), surfaceSize_.height());
66 cacheSurface_ = SkSurface::MakeRenderTarget(grContext, SkBudgeted::kYes, info);
67 #else
68 Drawing::ImageInfo info = Drawing::ImageInfo::MakeN32Premul(surfaceSize_.GetWidth(), surfaceSize_.GetHeight());
69 cacheSurface_ = Drawing::Surface::MakeRenderTarget(gpuContext, true, info);
70 #endif
71
72 return cacheSurface_ != nullptr;
73 }
74
75 #ifndef USE_ROSEN_DRAWING
InitTask(const sk_sp<SkImage> imageSnapshot)76 bool RSColorPickerCacheTask::InitTask(const sk_sp<SkImage> imageSnapshot)
77 #else
78 bool RSColorPickerCacheTask::InitTask(const std::shared_ptr<Drawing::Image> imageSnapshot)
79 #endif
80 {
81 if (imageSnapshot == nullptr) {
82 ROSEN_LOGE("RSColorPickerCacheTask imageSnapshot is null");
83 return false;
84 }
85 if (imageSnapshotCache_) {
86 #ifndef USE_ROSEN_DRAWING
87 cacheBackendTexture_ = imageSnapshotCache_->getBackendTexture(false);
88 #else
89 cacheBackendTexture_ = imageSnapshotCache_->GetBackendTexture(false, nullptr);
90 #endif
91 return true;
92 }
93 ROSEN_LOGD("RSColorPickerCacheTask InitTask:%{public}p", this);
94 #ifdef IS_OHOS
95 auto runner = AppExecFwk::EventRunner::Current();
96 initHandler_ = std::make_shared<AppExecFwk::EventHandler>(runner);
97 #endif
98 imageSnapshotCache_ = imageSnapshot;
99 #ifndef USE_ROSEN_DRAWING
100 surfaceSize_.set(imageSnapshotCache_->width(), imageSnapshotCache_->height());
101 #else
102 surfaceSize_.SetRight(imageSnapshotCache_->GetWidth());
103 surfaceSize_.SetBottom(imageSnapshotCache_->GetHeight());
104 #endif
105 return false;
106 }
107
Reset()108 void RSColorPickerCacheTask::Reset()
109 {
110 ROSEN_LOGD("RSColorPickerCacheTask::Reset:%{public}p", this);
111 {
112 std::unique_lock<std::mutex> lock(*grBackendTextureMutex_);
113 if (!imageSnapshotCache_) {
114 return;
115 }
116 imageSnapshotCache_.reset();
117 waitRelease_->store(false);
118 }
119 }
120
121 #ifndef USE_ROSEN_DRAWING
GpuScaleImage(std::shared_ptr<RSPaintFilterCanvas> cacheCanvas,const sk_sp<SkImage> threadImage,std::shared_ptr<SkPixmap> & dst)122 bool RSColorPickerCacheTask::GpuScaleImage(std::shared_ptr<RSPaintFilterCanvas> cacheCanvas,
123 const sk_sp<SkImage> threadImage, std::shared_ptr<SkPixmap>& dst)
124 {
125 if (cacheCanvas == nullptr) {
126 SetStatus(CacheProcessStatus::WAITING);
127 ROSEN_LOGE("RSColorPickerCacheTask cacheCanvas is null");
128 return false;
129 }
130 SkString shaderString(R"(
131 uniform shader imageInput;
132
133 half4 main(float2 xy) {
134 half4 c = imageInput.eval(xy);
135 return half4(c.rgb, 1.0);
136 }
137 )");
138
139 auto [effect, error] = SkRuntimeEffect::MakeForShader(shaderString);
140 if (!effect) {
141 ROSEN_LOGE("RSColorPickerCacheTask effect is null");
142 SetStatus(CacheProcessStatus::WAITING);
143 return false;
144 }
145
146 SkSamplingOptions linear(SkFilterMode::kLinear, SkMipmapMode::kNone);
147 SkRuntimeShaderBuilder effectBulider(effect);
148 SkImageInfo pcInfo;
149 SkMatrix matrix;
150 if (threadImage->width() * threadImage->height() < 10000) { // 10000 = 100 * 100 pixels
151 pcInfo = SkImageInfo::MakeN32Premul(10, 10); // 10 * 10 pixels
152 matrix = SkMatrix::Scale(10.0 / threadImage->width(), 10.0 / threadImage->height()); // 10.0 pixels
153 } else {
154 pcInfo = SkImageInfo::MakeN32Premul(100, 100); // 100 * 100 pixels
155 matrix = SkMatrix::Scale(100.0 / threadImage->width(), 100.0 / threadImage->height()); // 100.0 pixels
156 }
157 effectBulider.child("imageInput") = threadImage->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear, matrix);
158 sk_sp<SkImage> tmpColorImg = effectBulider.makeImage(cacheCanvas->recordingContext(), nullptr, pcInfo, false);
159
160 const int buffLen = tmpColorImg->width() * tmpColorImg->height();
161 if (pixelPtr_ != nullptr) {
162 delete [] pixelPtr_;
163 pixelPtr_ = nullptr;
164 }
165 pixelPtr_ = new uint32_t[buffLen];
166
167 auto info = tmpColorImg->imageInfo();
168 dst = std::make_shared<SkPixmap>(info, pixelPtr_, info.width() * info.bytesPerPixel());
169 bool flag = tmpColorImg->readPixels(*dst, 0, 0);
170
171 return flag;
172 }
173 #else
GpuScaleImage(std::shared_ptr<RSPaintFilterCanvas> cacheCanvas,const std::shared_ptr<Drawing::Image> threadImage,std::shared_ptr<Drawing::Pixmap> & dst)174 bool RSColorPickerCacheTask::GpuScaleImage(std::shared_ptr<RSPaintFilterCanvas> cacheCanvas,
175 const std::shared_ptr<Drawing::Image> threadImage, std::shared_ptr<Drawing::Pixmap>& dst)
176 {
177 if (cacheCanvas == nullptr) {
178 SetStatus(CacheProcessStatus::WAITING);
179 ROSEN_LOGE("RSColorPickerCacheTask cacheCanvas is null");
180 return false;
181 }
182 std::string shaderString(R"(
183 uniform shader imageInput;
184
185 half4 main(float2 xy) {
186 half4 c = imageInput.eval(xy);
187 return half4(c.rgb, 1.0);
188 }
189 )");
190
191 std::shared_ptr<Drawing::RuntimeEffect> effect = Drawing::RuntimeEffect::CreateForShader(shaderString);
192 if (!effect) {
193 ROSEN_LOGE("RSColorPickerCacheTask effect is null");
194 SetStatus(CacheProcessStatus::WAITING);
195 return false;
196 }
197
198 Drawing::SamplingOptions linear(Drawing::FilterMode::LINEAR, Drawing::MipmapMode::NONE);
199 std::shared_ptr<Drawing::RuntimeShaderBuilder> effectBulider =
200 std::make_shared<Drawing::RuntimeShaderBuilder>(effect);
201 Drawing::ImageInfo pcInfo;
202 Drawing::Matrix matrix;
203 matrix.SetScale(1.0, 1.0);
204 if (threadImage->GetWidth() * threadImage->GetHeight() < 10000) { // 10000 = 100 * 100 pixels
205 pcInfo = Drawing::ImageInfo::MakeN32Premul(10, 10); // 10 * 10 pixels
206 matrix.SetScale(10.0 / threadImage->GetWidth(), 10.0 / threadImage->GetHeight()); // 10.0 pixels
207 } else {
208 pcInfo = Drawing::ImageInfo::MakeN32Premul(100, 100); // 100 * 100 pixels
209 matrix.SetScale(100.0 / threadImage->GetWidth(), 100.0 / threadImage->GetHeight()); // 100.0 pixels
210 }
211 effectBulider->SetChild("imageInput", Drawing::ShaderEffect::CreateImageShader(
212 *threadImage, Drawing::TileMode::CLAMP, Drawing::TileMode::CLAMP, linear, matrix));
213 std::shared_ptr<Drawing::Image> tmpColorImg = effectBulider->MakeImage(
214 cacheCanvas->GetGPUContext().get(), nullptr, pcInfo, false);
215
216 const int buffLen = tmpColorImg->GetWidth() * tmpColorImg->GetHeight();
217 if (pixelPtr_ != nullptr) {
218 delete [] pixelPtr_;
219 pixelPtr_ = nullptr;
220 }
221 pixelPtr_ = new uint32_t[buffLen];
222
223 auto info = tmpColorImg->GetImageInfo();
224 dst = std::make_shared<Drawing::Pixmap>(info, pixelPtr_, info.GetWidth() * info.GetBytesPerPixel());
225 bool flag = tmpColorImg->ReadPixels(*dst, 0, 0);
226
227 return flag;
228 }
229 #endif
230
231 #ifndef USE_ROSEN_DRAWING
Render()232 bool RSColorPickerCacheTask::Render()
233 {
234 RS_TRACE_NAME_FMT("RSColorPickerCacheTask::Render:%p", this);
235 if (cacheSurface_ == nullptr) {
236 SetStatus(CacheProcessStatus::WAITING);
237 ROSEN_LOGE("RSColorPickerCacheTask cacheSurface is null");
238 return false;
239 }
240 CHECK_CACHE_PROCESS_STATUS;
241 std::shared_ptr<RSPaintFilterCanvas> cacheCanvas = std::make_shared<RSPaintFilterCanvas>(cacheSurface_.get());
242 if (cacheCanvas == nullptr) {
243 SetStatus(CacheProcessStatus::WAITING);
244 ROSEN_LOGE("RSColorPickerCacheTask cacheCanvas is null");
245 return false;
246 }
247
248 auto threadImage = SkImage::MakeFromTexture(cacheCanvas->recordingContext(), cacheBackendTexture_,
249 kBottomLeft_GrSurfaceOrigin, kRGBA_8888_SkColorType, kPremul_SkAlphaType, nullptr);
250
251 SkColor color;
252 std::shared_ptr<SkPixmap> dst;
253 if (GpuScaleImage(cacheCanvas, threadImage, dst)) {
254 uint32_t errorCode = 0;
255 std::shared_ptr<RSColorPicker> colorPicker = RSColorPicker::CreateColorPicker(dst, errorCode);
256 if (errorCode == 0) {
257 if (isShadow_ && shadowColorStrategy_ == SHADOW_COLOR_STRATEGY::COLOR_STRATEGY_MAIN) {
258 colorPicker->GetLargestProportionColor(color);
259 } else {
260 colorPicker->GetAverageColor(color);
261 }
262 std::unique_lock<std::mutex> lock(colorMutex_);
263 color_ = RSColor(SkColorGetR(color), SkColorGetG(color), SkColorGetB(color), SkColorGetA(color));
264 firstGetColorFinished_ = true;
265 valid_ = true;
266 } else {
267 valid_ = false;
268 }
269 } else {
270 valid_ = false;
271 }
272 if (pixelPtr_ != nullptr) {
273 delete [] pixelPtr_;
274 pixelPtr_ = nullptr;
275 }
276
277 CHECK_CACHE_PROCESS_STATUS;
278 std::unique_lock<std::mutex> lock(parallelRenderMutex_);
279 cacheProcessStatus_.store(CacheProcessStatus::DONE);
280 Notify();
281 return true;
282 }
283 #else
Render()284 bool RSColorPickerCacheTask::Render()
285 {
286 RS_TRACE_NAME_FMT("RSColorPickerCacheTask::Render:%p", this);
287 ROSEN_LOGD("RSColorPickerCacheTask::Render:%{public}p", this);
288 if (cacheSurface_ == nullptr) {
289 SetStatus(CacheProcessStatus::WAITING);
290 ROSEN_LOGE("RSColorPickerCacheTask cacheSurface is null");
291 return false;
292 }
293 CHECK_CACHE_PROCESS_STATUS;
294 std::shared_ptr<RSPaintFilterCanvas> cacheCanvas = std::make_shared<RSPaintFilterCanvas>(cacheSurface_.get());
295 CHECK_CACHE_PROCESS_STATUS;
296 auto threadImage = std::make_shared<Drawing::Image>();
297 Drawing::BitmapFormat info = Drawing::BitmapFormat { Drawing::COLORTYPE_RGBA_8888,
298 Drawing::ALPHATYPE_PREMUL };
299 {
300 std::unique_lock<std::mutex> lock(*grBackendTextureMutex_);
301 if (cacheCanvas == nullptr || !cacheBackendTexture_.IsValid()) {
302 SetStatus(CacheProcessStatus::WAITING);
303 ROSEN_LOGE("RSColorPickerCacheTask cacheCanvas is null or cacheBackendTexture not valid");
304 return false;
305 }
306 if (!threadImage->BuildFromTexture(*cacheCanvas->GetGPUContext(), cacheBackendTexture_.GetTextureInfo(),
307 Drawing::TextureOrigin::BOTTOM_LEFT, info, nullptr)) {
308 SetStatus(CacheProcessStatus::WAITING);
309 ROSEN_LOGE("RSColorPickerCacheTask::Render BuildFromTexture failed");
310 return false;
311 }
312 }
313 CHECK_CACHE_PROCESS_STATUS;
314 Drawing::ColorQuad color;
315 std::shared_ptr<Drawing::Pixmap> dst;
316 if (GpuScaleImage(cacheCanvas, threadImage, dst)) {
317 uint32_t errorCode = 0;
318 CHECK_CACHE_PROCESS_STATUS;
319 std::shared_ptr<RSColorPicker> colorPicker = RSColorPicker::CreateColorPicker(dst, errorCode);
320 if (errorCode == 0) {
321 if (isShadow_ && shadowColorStrategy_ == SHADOW_COLOR_STRATEGY::COLOR_STRATEGY_MAIN) {
322 colorPicker->GetLargestProportionColor(color);
323 } else {
324 colorPicker->GetAverageColor(color);
325 }
326 std::unique_lock<std::mutex> lock(colorMutex_);
327 color_ = RSColor(Drawing::Color::ColorQuadGetR(color), Drawing::Color::ColorQuadGetG(color),
328 Drawing::Color::ColorQuadGetB(color), Drawing::Color::ColorQuadGetA(color));
329 firstGetColorFinished_ = true;
330 valid_ = true;
331 } else {
332 valid_ = false;
333 }
334 } else {
335 valid_ = false;
336 }
337 if (pixelPtr_ != nullptr) {
338 delete [] pixelPtr_;
339 pixelPtr_ = nullptr;
340 }
341
342 CHECK_CACHE_PROCESS_STATUS;
343 std::unique_lock<std::mutex> lock(parallelRenderMutex_);
344 cacheProcessStatus_.store(CacheProcessStatus::DONE);
345 Notify();
346 return true;
347 }
348 #endif
349
GetColor(RSColor & color)350 bool RSColorPickerCacheTask::GetColor(RSColor& color)
351 {
352 std::unique_lock<std::mutex> lock(colorMutex_);
353 color = color_;
354 return valid_;
355 }
356
CalculateColorAverage(RSColor & colorCur)357 void RSColorPickerCacheTask::CalculateColorAverage(RSColor& colorCur)
358 {
359 // black color defination
360 RSColor black = RSColor(0, 0, 0, 255);
361 int colorArrayLen = colorArray_.size();
362 int colorArraySize = 20;
363 int continueBlackColorNum = 5;
364 if (colorArrayLen >= colorArraySize) {
365 colorArray_.pop_back();
366 }
367 colorArray_.emplace(colorArray_.begin(), colorCur);
368 int validColorNum = 0;
369 int R = 0;
370 int G = 0;
371 int B = 0;
372 int mark = 0;
373
374 for (int i = 0; i < static_cast<int>(colorArray_.size()); i++) {
375 if (colorArray_[i].GetRed() == 0 && colorArray_[i].GetGreen() == 0 && colorArray_[i].GetBlue() == 0) {
376 ++mark;
377 } else {
378 if (mark > continueBlackColorNum) {
379 R += black.GetRed() * mark;
380 G += black.GetGreen() * mark;
381 B += black.GetBlue() * mark;
382 validColorNum += mark;
383 }
384 R += colorArray_[i].GetRed();
385 G += colorArray_[i].GetGreen();
386 B += colorArray_[i].GetBlue();
387 validColorNum++;
388 mark = 0;
389 }
390 }
391
392 if (mark > continueBlackColorNum) {
393 R += black.GetRed() * mark;
394 G += black.GetGreen() * mark;
395 B += black.GetBlue() * mark;
396 validColorNum += mark;
397 }
398
399 if (validColorNum != 0) {
400 R = R / validColorNum;
401 G = G / validColorNum;
402 B = B / validColorNum;
403 } else {
404 colorAverage_ = colorCur;
405 }
406
407 colorAverage_ = RSColor(R, G, B, colorCur.GetAlpha());
408 }
409
GetColorAverage(RSColor & color)410 void RSColorPickerCacheTask::GetColorAverage(RSColor& color)
411 {
412 std::unique_lock<std::mutex> lock(parallelRenderMutex_);
413 CalculateColorAverage(color_);
414 color = colorAverage_;
415 }
416
GetFirstGetColorFinished()417 bool RSColorPickerCacheTask::GetFirstGetColorFinished()
418 {
419 return firstGetColorFinished_;
420 }
421
422 std::function<void(std::weak_ptr<RSColorPickerCacheTask>)> RSColorPickerCacheTask::postColorPickerTask = nullptr;
423 #ifdef IS_OHOS
424 std::function<void(std::shared_ptr<Drawing::Image> &&,
425 std::shared_ptr<Drawing::Surface> &&,
426 std::shared_ptr<OHOS::AppExecFwk::EventHandler>,
427 std::weak_ptr<std::atomic<bool>>,
428 std::weak_ptr<std::mutex>)> RSColorPickerCacheTask::saveImgAndSurToRelease = nullptr;
429 #endif
430
431 #ifndef USE_ROSEN_DRAWING
PostPartialColorPickerTask(std::shared_ptr<RSColorPickerCacheTask> colorPickerTask,sk_sp<SkImage> imageSnapshot)432 bool RSColorPickerCacheTask::PostPartialColorPickerTask(std::shared_ptr<RSColorPickerCacheTask> colorPickerTask,
433 sk_sp<SkImage> imageSnapshot)
434 #else
435 bool RSColorPickerCacheTask::PostPartialColorPickerTask(std::shared_ptr<RSColorPickerCacheTask> colorPickerTask,
436 std::shared_ptr<Drawing::Image> imageSnapshot)
437 #endif
438 {
439 ROSEN_LOGD("RSColorPickerCacheTask::PostPartialColorPickerTask:%{public}p", colorPickerTask.get());
440 if (RSColorPickerCacheTask::postColorPickerTask == nullptr) {
441 ROSEN_LOGD("PostPartialColorPickerTask::postColorPickerTask is null\n");
442 return false;
443 }
444
445 if (colorPickerTask == nullptr) {
446 ROSEN_LOGE("PostPartialColorPickerTask::colorPickerTask is null\n");
447 return false;
448 }
449
450 if (imageSnapshot == nullptr) {
451 ROSEN_LOGE("PostPartialColorPickerTask::colorPickerTask is null\n");
452 return false;
453 }
454
455 if (colorPickerTask->GetStatus() == CacheProcessStatus::WAITING && !colorPickerTask->GetWaitRelease()) {
456 if (colorPickerTask->InitTask(imageSnapshot)) {
457 ROSEN_LOGD("PostPartialColorPickerTask, init task");
458 colorPickerTask->SetStatus(CacheProcessStatus::DOING);
459 RSColorPickerCacheTask::postColorPickerTask(colorPickerTask);
460 }
461 return false;
462 } else if (colorPickerTask->GetStatus() == CacheProcessStatus::DONE) {
463 ROSEN_LOGD("PostPartialColorPickerTask, DONE");
464 #ifdef IS_OHOS
465 colorPickerTask->SetWaitRelease(true);
466 auto initHandler = colorPickerTask->GetInitHandler();
467 if (initHandler != nullptr) {
468 auto task = colorPickerTask;
469 ROSEN_LOGD("CacheProcessStatus::DONE, Reset():%{public}p", task.get());
470 initHandler->PostTask(
471 [task]() { task->Reset(); }, AppExecFwk::EventQueue::Priority::IMMEDIATE);
472 }
473 #endif
474 return true;
475 } else {
476 ROSEN_LOGD("PostPartialColorPickerTask, doing or wait release");
477 return false;
478 }
479 }
480
SetDeviceSize(int & deviceWidth,int & deviceHeight)481 void RSColorPickerCacheTask::SetDeviceSize(int& deviceWidth, int& deviceHeight)
482 {
483 deviceWidth_ = deviceWidth;
484 deviceHeight_ = deviceHeight;
485 }
486
SetIsShadow(bool isShadow)487 void RSColorPickerCacheTask::SetIsShadow(bool isShadow)
488 {
489 isShadow_ = isShadow;
490 }
491
SetShadowColorStrategy(int shadowColorStrategy)492 void RSColorPickerCacheTask::SetShadowColorStrategy(int shadowColorStrategy)
493 {
494 shadowColorStrategy_ = shadowColorStrategy;
495 }
496
SetWaitRelease(bool waitRelease)497 void RSColorPickerCacheTask::SetWaitRelease(bool waitRelease)
498 {
499 waitRelease_->store(waitRelease);
500 }
501
GetDeviceSize(int & deviceWidth,int & deviceHeight) const502 bool RSColorPickerCacheTask::GetDeviceSize(int& deviceWidth, int& deviceHeight) const
503 {
504 if (deviceWidth_.has_value() && deviceHeight_.has_value()) {
505 deviceWidth = deviceWidth_.value();
506 deviceHeight = deviceHeight_.value();
507 return true;
508 }
509 return false;
510 }
511
GetWaitRelease() const512 bool RSColorPickerCacheTask::GetWaitRelease() const
513 {
514 return waitRelease_->load();
515 }
516
ReleaseColorPicker()517 void RSColorPickerCacheTask::ReleaseColorPicker()
518 {
519 #ifdef IS_OHOS
520 waitRelease_->store(true);
521 cacheProcessStatus_.store(CacheProcessStatus::WAITING);
522 if (imageSnapshotCache_ || cacheSurface_ || initHandler_ || handler_) {
523 ROSEN_LOGD("RSColorPickerCacheTask::ReleaseColorPicker:%{public}p", this);
524 RSColorPickerCacheTask::saveImgAndSurToRelease(std::move(imageSnapshotCache_), std::move(cacheSurface_),
525 initHandler_, waitRelease_, grBackendTextureMutex_);
526 }
527 #endif
528 }
529
530 } // namespace Rosen
531 } // namespace OHOS
532
533