• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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 "webp_decoder.h"
17 
18 #include "image_log.h"
19 #include "image_trace.h"
20 #include "image_utils.h"
21 #include "media_errors.h"
22 #include "multimedia_templates.h"
23 #include "securec.h"
24 #if !defined(IOS_PLATFORM) && !defined(A_PLATFORM)
25 #include "surface_buffer.h"
26 #endif
27 
28 #undef LOG_DOMAIN
29 #define LOG_DOMAIN LOG_TAG_DOMAIN_ID_PLUGIN
30 
31 #undef LOG_TAG
32 #define LOG_TAG "WebpDecoder"
33 
34 namespace OHOS {
35 namespace ImagePlugin {
36 using namespace MultimediaPlugin;
37 using namespace Media;
38 using namespace MultiMedia;
39 
40 namespace {
41 constexpr int32_t WEBP_IMAGE_NUM = 1;
42 constexpr int32_t EXTERNAL_MEMORY = 1;
43 constexpr size_t DECODE_VP8CHUNK_MIN_SIZE = 4096;
44 } // namespace
45 
WebpDecoder()46 WebpDecoder::WebpDecoder()
47 {}
48 
~WebpDecoder()49 WebpDecoder::~WebpDecoder()
50 {
51     Reset();
52 }
53 
SetSource(InputDataStream & sourceStream)54 void WebpDecoder::SetSource(InputDataStream &sourceStream)
55 {
56     stream_ = &sourceStream;
57     state_ = WebpDecodingState::SOURCE_INITED;
58 }
59 
GetImageSize(uint32_t index,PlSize & size)60 uint32_t WebpDecoder::GetImageSize(uint32_t index, PlSize &size)
61 {
62     if (index >= WEBP_IMAGE_NUM) {
63         IMAGE_LOGE("image size:invalid index, index:%{public}u, range:%{public}u.", index, WEBP_IMAGE_NUM);
64         return ERR_IMAGE_INVALID_PARAMETER;
65     }
66     if (state_ < WebpDecodingState::SOURCE_INITED) {
67         IMAGE_LOGE("get image size failed for state %{public}d.", state_);
68         return ERR_MEDIA_INVALID_OPERATION;
69     }
70     if (state_ >= WebpDecodingState::BASE_INFO_PARSED) {
71         size = webpSize_;
72         return SUCCESS;
73     }
74 
75     uint32_t ret = DecodeHeader();
76     if (ret != SUCCESS) {
77         IMAGE_LOGD("decode header error on get image ret:%{public}u.", ret);
78         return ret;
79     }
80     size = webpSize_;
81     return SUCCESS;
82 }
83 
SetDecodeOptions(uint32_t index,const PixelDecodeOptions & opts,PlImageInfo & info)84 uint32_t WebpDecoder::SetDecodeOptions(uint32_t index, const PixelDecodeOptions &opts, PlImageInfo &info)
85 {
86     if (index >= WEBP_IMAGE_NUM) {
87         IMAGE_LOGE("set option:invalid index, index:%{public}u, range:%{public}u.", index, WEBP_IMAGE_NUM);
88         return ERR_IMAGE_INVALID_PARAMETER;
89     }
90     if (state_ < WebpDecodingState::SOURCE_INITED) {
91         IMAGE_LOGE("set decode option failed for state %{public}d.", state_);
92         return ERR_MEDIA_INVALID_OPERATION;
93     }
94     if (state_ >= WebpDecodingState::IMAGE_DECODING) {
95         FinishOldDecompress();
96         state_ = WebpDecodingState::SOURCE_INITED;
97     }
98     if (state_ < WebpDecodingState::BASE_INFO_PARSED) {
99         uint32_t ret = DecodeHeader();
100         if (ret != SUCCESS) {
101             IMAGE_LOGE("decode header error on set decode options:%{public}u.", ret);
102             state_ = WebpDecodingState::BASE_INFO_PARSING;
103             return ret;
104         }
105         state_ = WebpDecodingState::BASE_INFO_PARSED;
106     }
107 
108     bool hasAlpha = true;
109     if (opts.desiredPixelFormat == PlPixelFormat::RGB_565) {
110         hasAlpha = false;
111         info.alphaType = PlAlphaType::IMAGE_ALPHA_TYPE_OPAQUE;
112     } else {
113         info.alphaType = opts.desireAlphaType;
114     }
115     webpMode_ = GetWebpDecodeMode(opts.desiredPixelFormat,
116                                   hasAlpha && (opts.desireAlphaType == PlAlphaType::IMAGE_ALPHA_TYPE_PREMUL));
117     info.size = webpSize_;
118     info.pixelFormat = outputFormat_;
119     opts_ = opts;
120 
121     state_ = WebpDecodingState::IMAGE_DECODING;
122     return SUCCESS;
123 }
124 
Decode(uint32_t index,DecodeContext & context)125 uint32_t WebpDecoder::Decode(uint32_t index, DecodeContext &context)
126 {
127     ImageTrace imageTrace("WebpDecoder::Decode, index:%u", index);
128 #if defined(A_PLATFORM) || defined(IOS_PLATFORM)
129     context.allocatorType = Media::AllocatorType::HEAP_ALLOC;
130 #endif
131     if (index >= WEBP_IMAGE_NUM) {
132         IMAGE_LOGE("decode:invalid index, index:%{public}u, range:%{public}u.", index, WEBP_IMAGE_NUM);
133         return ERR_IMAGE_INVALID_PARAMETER;
134     }
135     if (state_ < WebpDecodingState::IMAGE_DECODING) {
136         IMAGE_LOGE("set decode option failed for state %{public}d.", state_);
137         return ERR_MEDIA_INVALID_OPERATION;
138     }
139     if (state_ > WebpDecodingState::IMAGE_DECODING) {
140         FinishOldDecompress();
141         uint32_t ret = DecodeHeader();
142         if (ret != SUCCESS) {
143             IMAGE_LOGE("decode header error on set decode options:%{public}u.", ret);
144             state_ = WebpDecodingState::BASE_INFO_PARSING;
145             return ret;
146         }
147         bool hasAlpha = true;
148         if (opts_.desiredPixelFormat == PlPixelFormat::RGB_565) {
149             hasAlpha = false;
150         }
151         webpMode_ =
152             GetWebpDecodeMode(opts_.desiredPixelFormat,
153                               hasAlpha && opts_.desireAlphaType == PlAlphaType::IMAGE_ALPHA_TYPE_PREMUL);
154         state_ = WebpDecodingState::IMAGE_DECODING;
155     }
156 
157     return DoCommonDecode(context);
158 }
159 
PromoteIncrementalDecode(uint32_t index,ProgDecodeContext & context)160 uint32_t WebpDecoder::PromoteIncrementalDecode(uint32_t index, ProgDecodeContext &context)
161 {
162     context.totalProcessProgress = 0;
163     if (index >= WEBP_IMAGE_NUM) {
164         IMAGE_LOGE("incremental:invalid index, index:%{public}u, range:%{public}u.", index, WEBP_IMAGE_NUM);
165         return ERR_IMAGE_INVALID_PARAMETER;
166     }
167 
168     if (state_ != WebpDecodingState::IMAGE_DECODING) {
169         IMAGE_LOGE("incremental decode failed for state %{public}d.", state_);
170         return ERR_MEDIA_INVALID_OPERATION;
171     }
172 
173     if (!IsDataEnough()) {
174         IMAGE_LOGD("increment data not enough, need next data.");
175         return ERR_IMAGE_SOURCE_DATA_INCOMPLETE;
176     }
177     return DoIncrementalDecode(context);
178 }
179 
DecodeHeader()180 uint32_t WebpDecoder::DecodeHeader()
181 {
182     uint32_t ret = ReadIncrementalHead();
183     if (ret != SUCCESS) {
184         if (ret == ERR_IMAGE_SOURCE_DATA_INCOMPLETE) {
185             state_ = WebpDecodingState::BASE_INFO_PARSING;
186         } else {
187             state_ = WebpDecodingState::SOURCE_INITED;
188             IMAGE_LOGE("decode image head failed, ret:%{public}u.", ret);
189         }
190         return ret;
191     }
192     state_ = WebpDecodingState::BASE_INFO_PARSED;
193     return SUCCESS;
194 }
195 
ReadIncrementalHead()196 uint32_t WebpDecoder::ReadIncrementalHead()
197 {
198     size_t stremSize = stream_->GetStreamSize();
199     if (stremSize >= DECODE_VP8CHUNK_MIN_SIZE || stream_->IsStreamCompleted()) {
200         stream_->Seek(0);
201         if (!stream_->Read(stream_->GetStreamSize(), dataBuffer_)) {
202             IMAGE_LOGE("read data fail.");
203             return ERR_IMAGE_SOURCE_DATA_INCOMPLETE;
204         }
205         if (dataBuffer_.inputStreamBuffer == nullptr || dataBuffer_.dataSize == 0) {
206             IMAGE_LOGE("inputStreamBuffer is null or data size is %{public}u.", dataBuffer_.dataSize);
207             return ERR_IMAGE_GET_DATA_ABNORMAL;
208         }
209 
210         int32_t width = 0;
211         int32_t height = 0;
212         int32_t ret = WebPGetInfo(dataBuffer_.inputStreamBuffer, dataBuffer_.bufferSize, &width, &height);
213         if (ret == 0 || (width == 0 && height == 0)) {
214             // may be incomplete data
215             IMAGE_LOGE("get width and height fail.");
216             return ERR_IMAGE_SOURCE_DATA_INCOMPLETE;
217         }
218 
219         if (width < 0 || height < 0) {
220             IMAGE_LOGE("width and height invalid, width:%{public}d, height:%{public}d.", width, height);
221             return ERR_IMAGE_INVALID_PARAMETER;
222         }
223         webpSize_.width = static_cast<uint32_t>(width);
224         webpSize_.height = static_cast<uint32_t>(height);
225         incrementSize_ = stremSize;
226         lastDecodeSize_ = stremSize;
227         return SUCCESS;
228     }
229 
230     return ERR_IMAGE_SOURCE_DATA_INCOMPLETE;
231 }
232 
IsDataEnough()233 bool WebpDecoder::IsDataEnough()
234 {
235     size_t streamSize = stream_->GetStreamSize();
236     if (incrementSize_ < DECODE_VP8CHUNK_MIN_SIZE && !stream_->IsStreamCompleted()) {
237         incrementSize_ += streamSize - lastDecodeSize_;
238         lastDecodeSize_ = streamSize;
239         return false;
240     }
241     incrementSize_ = streamSize - lastDecodeSize_;
242     lastDecodeSize_ = streamSize;
243     return true;
244 }
245 
GetWebpDecodeMode(const PlPixelFormat & pixelFormat,bool premul)246 WEBP_CSP_MODE WebpDecoder::GetWebpDecodeMode(const PlPixelFormat &pixelFormat, bool premul)
247 {
248     WEBP_CSP_MODE webpMode = MODE_RGBA;
249     outputFormat_ = pixelFormat;
250     switch (pixelFormat) {
251         case PlPixelFormat::BGRA_8888:
252             webpMode = premul ? MODE_bgrA : MODE_BGRA;
253             break;
254         case PlPixelFormat::RGBA_8888:
255             webpMode = premul ? MODE_rgbA : MODE_RGBA;
256             break;
257         case PlPixelFormat::RGB_565:
258             bytesPerPixel_ = 2;  // RGB_565 2 bytes each pixel
259             webpMode = MODE_RGB_565;
260             break;
261         case PlPixelFormat::UNKNOWN:
262         default:
263             outputFormat_ = PlPixelFormat::RGBA_8888;
264             webpMode = premul ? MODE_rgbA : MODE_RGBA;
265             break;
266     }
267     return webpMode;
268 }
269 
FinishOldDecompress()270 void WebpDecoder::FinishOldDecompress()
271 {
272     if (state_ < WebpDecodingState::IMAGE_DECODING) {
273         return;
274     }
275     Reset();
276 }
277 
DoCommonDecode(DecodeContext & context)278 uint32_t WebpDecoder::DoCommonDecode(DecodeContext &context)
279 {
280     WebPDecoderConfig config;
281     if (!PreDecodeProc(context, config, false)) {
282         IMAGE_LOGE("prepare common decode failed.");
283         state_ = WebpDecodingState::IMAGE_ERROR;
284         return ERR_IMAGE_MALLOC_ABNORMAL;
285     }
286 
287     TAutoCallProc<WebPDecBuffer, WebPFreeDecBuffer> webpOutput(&config.output);
288     TAutoCallProc<WebPIDecoder, WebPIDelete> idec(WebPINewDecoder(&config.output));
289     if (idec == nullptr) {
290         IMAGE_LOGE("common decode:idec is null.");
291         state_ = WebpDecodingState::IMAGE_ERROR;
292         return ERR_IMAGE_DECODE_FAILED;
293     }
294 
295     VP8StatusCode status = WebPIUpdate(idec, dataBuffer_.inputStreamBuffer, static_cast<size_t>(dataBuffer_.dataSize));
296     if (status == VP8_STATUS_OK) {
297         state_ = WebpDecodingState::IMAGE_DECODED;
298         return SUCCESS;
299     }
300     if (status == VP8_STATUS_SUSPENDED && opts_.allowPartialImage) {
301         state_ = WebpDecodingState::IMAGE_PARTIAL;
302         context.ifPartialOutput = true;
303         IMAGE_LOGI("this is partial image data to decode.");
304         return SUCCESS;
305     }
306 
307     IMAGE_LOGE("decode image data failed, status:%{public}d.", status);
308     state_ = WebpDecodingState::IMAGE_ERROR;
309     return ERR_IMAGE_DECODE_FAILED;
310 }
311 
DoIncrementalDecode(ProgDecodeContext & context)312 uint32_t WebpDecoder::DoIncrementalDecode(ProgDecodeContext &context)
313 {
314     WebPDecoderConfig config;
315     if (!PreDecodeProc(context.decodeContext, config, true)) {
316         IMAGE_LOGE("prepare increment decode failed.");
317         return ERR_IMAGE_MALLOC_ABNORMAL;
318     }
319 
320     TAutoCallProc<WebPDecBuffer, WebPFreeDecBuffer> webpOutput(&config.output);
321     TAutoCallProc<WebPIDecoder, WebPIDelete> idec(WebPINewDecoder(&config.output));
322     if (idec == nullptr) {
323         IMAGE_LOGE("incremental code:idec is null.");
324         return ERR_IMAGE_DECODE_FAILED;
325     }
326 
327     dataBuffer_ = { nullptr, 0, 0 };
328     stream_->Seek(0);
329     if (!stream_->Read(stream_->GetStreamSize(), dataBuffer_)) {
330         IMAGE_LOGE("incremental:read data failed.");
331         return ERR_IMAGE_DECODE_FAILED;
332     }
333     if (dataBuffer_.inputStreamBuffer == nullptr || dataBuffer_.dataSize == 0) {
334         IMAGE_LOGE("incremental:data is null.");
335         return ERR_IMAGE_DECODE_FAILED;
336     }
337 
338     VP8StatusCode status = WebPIUpdate(idec, dataBuffer_.inputStreamBuffer, static_cast<size_t>(dataBuffer_.dataSize));
339     if (status != VP8_STATUS_OK && status != VP8_STATUS_SUSPENDED) {
340         IMAGE_LOGE("incremental:webp status exception,status:%{public}d.", status);
341         return ERR_IMAGE_DECODE_FAILED;
342     }
343     if (status == VP8_STATUS_SUSPENDED) {
344         int32_t curHeight = 0;
345         if (WebPIDecGetRGB(idec, &curHeight, nullptr, nullptr, nullptr) == nullptr) {
346             IMAGE_LOGD("refresh image failed, current height:%{public}d.", curHeight);
347         }
348         if (curHeight > 0 && webpSize_.height != 0) {
349             context.totalProcessProgress =
350                 static_cast<uint32_t>(curHeight) * ProgDecodeContext::FULL_PROGRESS / webpSize_.height;
351         }
352         return ERR_IMAGE_SOURCE_DATA_INCOMPLETE;
353     }
354     if (status == VP8_STATUS_OK) {
355         context.totalProcessProgress = context.FULL_PROGRESS;
356         state_ = WebpDecodingState::IMAGE_DECODED;
357     }
358     return SUCCESS;
359 }
360 
InitWebpOutput(const DecodeContext & context,WebPDecBuffer & output)361 void WebpDecoder::InitWebpOutput(const DecodeContext &context, WebPDecBuffer &output)
362 {
363     output.is_external_memory = EXTERNAL_MEMORY;  // external allocated space
364     output.u.RGBA.rgba = static_cast<uint8_t *>(context.pixelsBuffer.buffer);
365     output.u.RGBA.stride = webpSize_.width * bytesPerPixel_;
366     output.u.RGBA.size = context.pixelsBuffer.bufferSize;
367     output.colorspace = webpMode_;
368 }
369 
PreDecodeProc(DecodeContext & context,WebPDecoderConfig & config,bool isIncremental)370 bool WebpDecoder::PreDecodeProc(DecodeContext &context, WebPDecoderConfig &config, bool isIncremental)
371 {
372     if (WebPInitDecoderConfig(&config) == 0) {
373         IMAGE_LOGE("init config failed.");
374         return false;
375     }
376     if (!AllocOutputBuffer(context, isIncremental)) {
377         IMAGE_LOGE("get pixels memory failed.");
378         return false;
379     }
380 
381     InitWebpOutput(context, config.output);
382     return true;
383 }
384 
Reset()385 void WebpDecoder::Reset()
386 {
387     stream_->Seek(0);
388     dataBuffer_ = { nullptr, 0, 0 };
389     webpSize_ = { 0, 0 };
390 }
391 
SharedMemoryCreate(DecodeContext & context,const uint32_t & byteCount)392 static bool SharedMemoryCreate(DecodeContext &context, const uint32_t &byteCount)
393 {
394 #if defined(_WIN32) || defined(_APPLE) || defined(A_PLATFORM) || defined(IOS_PLATFORM)
395     IMAGE_LOGE("Unsupport dma mem alloc");
396     return false;
397 #else
398     uint32_t id = context.pixelmapUniqueId_;
399     std::string name = "WEBP RawData, uniqueId: " + std::to_string(getpid()) + '_' + std::to_string(id);
400     int fd = AshmemCreate(name.c_str(), byteCount);
401     if (fd < 0) {
402         return false;
403     }
404     int result = AshmemSetProt(fd, PROT_READ | PROT_WRITE);
405     if (result < 0) {
406         ::close(fd);
407         return false;
408     }
409     void* ptr = ::mmap(nullptr, byteCount, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
410     if (ptr == MAP_FAILED) {
411         ::close(fd);
412         return false;
413     }
414     context.pixelsBuffer.buffer = ptr;
415     void *fdBuffer = new int32_t();
416     if (fdBuffer == nullptr) {
417         IMAGE_LOGE("malloc fdBuffer fail");
418         ::munmap(ptr, byteCount);
419         ::close(fd);
420         context.pixelsBuffer.buffer = nullptr;
421         return false;
422     }
423     *static_cast<int32_t *>(fdBuffer) = fd;
424     context.pixelsBuffer.context = fdBuffer;
425     context.pixelsBuffer.bufferSize = byteCount;
426     context.allocatorType = AllocatorType::SHARE_MEM_ALLOC;
427     context.freeFunc = nullptr;
428     return true;
429 #endif
430 }
431 
HeapMemoryCreate(DecodeContext & context,const uint32_t & byteCount)432 static bool HeapMemoryCreate(DecodeContext &context, const uint32_t &byteCount)
433 {
434     if (byteCount == 0 || byteCount > PIXEL_MAP_MAX_RAM_SIZE) {
435         IMAGE_LOGE("Invalid value of byteCount");
436         return false;
437     }
438     void *outputBuffer = malloc(byteCount);
439     if (outputBuffer == nullptr) {
440         IMAGE_LOGE("alloc output buffer size:[%{public}llu] error.",
441             static_cast<unsigned long long>(byteCount));
442         return false;
443     }
444 #ifdef _WIN32
445     errno_t backRet = memset_s(outputBuffer, 0, byteCount);
446     if (backRet != EOK) {
447         IMAGE_LOGE("memset buffer failed.", backRet);
448         free(outputBuffer);
449         outputBuffer = nullptr;
450         return false;
451     }
452 #else
453     if (memset_s(outputBuffer, byteCount, 0, byteCount) != EOK) {
454         IMAGE_LOGE("memset buffer failed.");
455         free(outputBuffer);
456         outputBuffer = nullptr;
457         return false;
458     }
459 #endif
460     context.pixelsBuffer.buffer = outputBuffer;
461     context.pixelsBuffer.bufferSize = byteCount;
462     context.pixelsBuffer.context = nullptr;
463     context.allocatorType = AllocatorType::HEAP_ALLOC;
464     context.freeFunc = nullptr;
465     return true;
466 }
467 
DmaMemoryCreate(DecodeContext & context,const uint32_t & byteCount,const PlSize & webpSize)468 static bool DmaMemoryCreate(DecodeContext &context, const uint32_t &byteCount, const PlSize &webpSize)
469 {
470 #if defined(_WIN32) || defined(_APPLE) || defined(A_PLATFORM) || defined(IOS_PLATFORM)
471     IMAGE_LOGE("Unsupport dma mem alloc");
472     return false;
473 #else
474     sptr<SurfaceBuffer> sb = SurfaceBuffer::Create();
475     BufferRequestConfig requestConfig = {
476         .width = webpSize.width,
477         .height = webpSize.height,
478         .strideAlignment = 0x8, // set 0x8 as default value to alloc SurfaceBufferImpl
479         .format = GRAPHIC_PIXEL_FMT_RGBA_8888, // PixelFormat
480         .usage = BUFFER_USAGE_CPU_READ | BUFFER_USAGE_CPU_WRITE | BUFFER_USAGE_MEM_DMA,
481         .timeout = 0,
482         .colorGamut = GraphicColorGamut::GRAPHIC_COLOR_GAMUT_SRGB,
483         .transform = GraphicTransformType::GRAPHIC_ROTATE_NONE,
484     };
485     GSError ret = sb->Alloc(requestConfig);
486     if (ret != GSERROR_OK) {
487         IMAGE_LOGE("SurfaceBuffer Alloc failed, %{public}s", GSErrorStr(ret).c_str());
488         return false;
489     }
490     void* nativeBuffer = sb.GetRefPtr();
491     int32_t err = ImageUtils::SurfaceBuffer_Reference(nativeBuffer);
492     if (err != OHOS::GSERROR_OK) {
493         IMAGE_LOGE("NativeBufferReference failed");
494         return false;
495     }
496 
497     context.pixelsBuffer.buffer = sb->GetVirAddr();
498     context.pixelsBuffer.context = nativeBuffer;
499     context.pixelsBuffer.bufferSize = byteCount;
500     context.allocatorType = AllocatorType::DMA_ALLOC;
501     context.freeFunc = nullptr;
502     return true;
503 #endif
504 }
505 
AllocOutputBuffer(DecodeContext & context,bool isIncremental)506 bool WebpDecoder::AllocOutputBuffer(DecodeContext &context, bool isIncremental)
507 {
508     if (isIncremental) {
509         if (context.pixelsBuffer.buffer != nullptr && context.allocatorType == AllocatorType::HEAP_ALLOC) {
510             free(context.pixelsBuffer.buffer);
511             context.pixelsBuffer.buffer = nullptr;
512         }
513     }
514     if (context.pixelsBuffer.buffer == nullptr) {
515         uint64_t byteCount = static_cast<uint64_t>(webpSize_.width * webpSize_.height * bytesPerPixel_);
516         if (context.allocatorType == Media::AllocatorType::SHARE_MEM_ALLOC) {
517             return SharedMemoryCreate(context, byteCount);
518         } else if (context.allocatorType == Media::AllocatorType::HEAP_ALLOC) {
519             return HeapMemoryCreate(context, byteCount);
520         } else if (context.allocatorType == Media::AllocatorType::DMA_ALLOC) {
521             return DmaMemoryCreate(context, byteCount, webpSize_);
522         }
523         // Current Defalut alloc function
524         return SharedMemoryCreate(context, byteCount);
525     }
526     return true;
527 }
528 } // namespace ImagePlugin
529 } // namespace OHOS
530