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(ANDROID_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,Size & size)60 uint32_t WebpDecoder::GetImageSize(uint32_t index, Size &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 == PixelFormat::RGB_565) {
110         hasAlpha = false;
111         info.alphaType = AlphaType::IMAGE_ALPHA_TYPE_OPAQUE;
112     } else {
113         info.alphaType = opts.desireAlphaType;
114     }
115     webpMode_ = GetWebpDecodeMode(opts.desiredPixelFormat,
116                                   hasAlpha && (opts.desireAlphaType == AlphaType::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(ANDROID_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 == PixelFormat::RGB_565) {
149             hasAlpha = false;
150         }
151         webpMode_ =
152             GetWebpDecodeMode(opts_.desiredPixelFormat,
153                               hasAlpha && opts_.desireAlphaType == AlphaType::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 PixelFormat & pixelFormat,bool premul)246 WEBP_CSP_MODE WebpDecoder::GetWebpDecodeMode(const PixelFormat &pixelFormat, bool premul)
247 {
248     WEBP_CSP_MODE webpMode = MODE_RGBA;
249     outputFormat_ = pixelFormat;
250     switch (pixelFormat) {
251         case PixelFormat::BGRA_8888:
252             webpMode = premul ? MODE_bgrA : MODE_BGRA;
253             break;
254         case PixelFormat::RGBA_8888:
255             webpMode = premul ? MODE_rgbA : MODE_RGBA;
256             break;
257         case PixelFormat::RGB_565:
258             bytesPerPixel_ = 2;  // RGB_565 2 bytes each pixel
259             webpMode = MODE_RGB_565;
260             break;
261         case PixelFormat::UNKNOWN:
262         default:
263             outputFormat_ = PixelFormat::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         ImageUtils::FlushContextSurfaceBuffer(context);
299         return SUCCESS;
300     }
301     if (status == VP8_STATUS_SUSPENDED && opts_.allowPartialImage) {
302         state_ = WebpDecodingState::IMAGE_PARTIAL;
303         context.ifPartialOutput = true;
304         IMAGE_LOGI("this is partial image data to decode.");
305         ImageUtils::FlushContextSurfaceBuffer(context);
306         return SUCCESS;
307     }
308 
309     IMAGE_LOGE("decode image data failed, status:%{public}d.", status);
310     state_ = WebpDecodingState::IMAGE_ERROR;
311     return ERR_IMAGE_DECODE_FAILED;
312 }
313 
DoIncrementalDecode(ProgDecodeContext & context)314 uint32_t WebpDecoder::DoIncrementalDecode(ProgDecodeContext &context) __attribute__((no_sanitize("cfi")))
315 {
316     WebPDecoderConfig config;
317     if (!PreDecodeProc(context.decodeContext, config, true)) {
318         IMAGE_LOGE("prepare increment decode failed.");
319         return ERR_IMAGE_MALLOC_ABNORMAL;
320     }
321 
322     TAutoCallProc<WebPDecBuffer, WebPFreeDecBuffer> webpOutput(&config.output);
323     TAutoCallProc<WebPIDecoder, WebPIDelete> idec(WebPINewDecoder(&config.output));
324     if (idec == nullptr) {
325         IMAGE_LOGE("incremental code:idec is null.");
326         return ERR_IMAGE_DECODE_FAILED;
327     }
328 
329     dataBuffer_ = { nullptr, 0, 0 };
330     stream_->Seek(0);
331     if (!stream_->Read(stream_->GetStreamSize(), dataBuffer_)) {
332         IMAGE_LOGE("incremental:read data failed.");
333         return ERR_IMAGE_DECODE_FAILED;
334     }
335     if (dataBuffer_.inputStreamBuffer == nullptr || dataBuffer_.dataSize == 0) {
336         IMAGE_LOGE("incremental:data is null.");
337         return ERR_IMAGE_DECODE_FAILED;
338     }
339 
340     VP8StatusCode status = WebPIUpdate(idec, dataBuffer_.inputStreamBuffer, static_cast<size_t>(dataBuffer_.dataSize));
341     if (status != VP8_STATUS_OK && status != VP8_STATUS_SUSPENDED) {
342         IMAGE_LOGE("incremental:webp status exception,status:%{public}d.", status);
343         return ERR_IMAGE_DECODE_FAILED;
344     }
345     if (status == VP8_STATUS_SUSPENDED) {
346         int32_t curHeight = 0;
347         if (WebPIDecGetRGB(idec, &curHeight, nullptr, nullptr, nullptr) == nullptr) {
348             IMAGE_LOGD("refresh image failed, current height:%{public}d.", curHeight);
349         }
350         if (curHeight > 0 && webpSize_.height != 0) {
351             context.totalProcessProgress =
352                 static_cast<uint32_t>(curHeight) * ProgDecodeContext::FULL_PROGRESS / webpSize_.height;
353         }
354         return ERR_IMAGE_SOURCE_DATA_INCOMPLETE;
355     }
356     if (status == VP8_STATUS_OK) {
357         context.totalProcessProgress = context.FULL_PROGRESS;
358         state_ = WebpDecodingState::IMAGE_DECODED;
359     }
360     return SUCCESS;
361 }
362 
InitWebpOutput(const DecodeContext & context,WebPDecBuffer & output)363 void WebpDecoder::InitWebpOutput(const DecodeContext &context, WebPDecBuffer &output)
364 {
365     output.is_external_memory = EXTERNAL_MEMORY;  // external allocated space
366     output.u.RGBA.rgba = static_cast<uint8_t *>(context.pixelsBuffer.buffer);
367     output.u.RGBA.stride = webpSize_.width * bytesPerPixel_;
368     output.u.RGBA.size = context.pixelsBuffer.bufferSize;
369     output.colorspace = webpMode_;
370 }
371 
PreDecodeProc(DecodeContext & context,WebPDecoderConfig & config,bool isIncremental)372 bool WebpDecoder::PreDecodeProc(DecodeContext &context, WebPDecoderConfig &config, bool isIncremental)
373 {
374     if (WebPInitDecoderConfig(&config) == 0) {
375         IMAGE_LOGE("init config failed.");
376         return false;
377     }
378     if (!AllocOutputBuffer(context, isIncremental)) {
379         IMAGE_LOGE("get pixels memory failed.");
380         return false;
381     }
382 
383     InitWebpOutput(context, config.output);
384     return true;
385 }
386 
Reset()387 void WebpDecoder::Reset()
388 {
389     stream_->Seek(0);
390     dataBuffer_ = { nullptr, 0, 0 };
391     webpSize_ = { 0, 0 };
392 }
393 
SharedMemoryCreate(DecodeContext & context,const uint32_t & byteCount)394 static bool SharedMemoryCreate(DecodeContext &context, const uint32_t &byteCount)
395 {
396 #if defined(_WIN32) || defined(_APPLE) || defined(ANDROID_PLATFORM) || defined(IOS_PLATFORM)
397     IMAGE_LOGE("Unsupport dma mem alloc");
398     return false;
399 #else
400     uint32_t id = context.pixelmapUniqueId_;
401     std::string name = "WEBP RawData, uniqueId: " + std::to_string(getpid()) + '_' + std::to_string(id);
402     int fd = AshmemCreate(name.c_str(), byteCount);
403     if (fd < 0) {
404         return false;
405     }
406     int result = AshmemSetProt(fd, PROT_READ | PROT_WRITE);
407     if (result < 0) {
408         ::close(fd);
409         return false;
410     }
411     void* ptr = ::mmap(nullptr, byteCount, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
412     if (ptr == MAP_FAILED) {
413         ::close(fd);
414         return false;
415     }
416     context.pixelsBuffer.buffer = ptr;
417     void *fdBuffer = new int32_t();
418     if (fdBuffer == nullptr) {
419         IMAGE_LOGE("malloc fdBuffer fail");
420         ::munmap(ptr, byteCount);
421         ::close(fd);
422         context.pixelsBuffer.buffer = nullptr;
423         return false;
424     }
425     *static_cast<int32_t *>(fdBuffer) = fd;
426     context.pixelsBuffer.context = fdBuffer;
427     context.pixelsBuffer.bufferSize = byteCount;
428     context.allocatorType = AllocatorType::SHARE_MEM_ALLOC;
429     context.freeFunc = nullptr;
430     return true;
431 #endif
432 }
433 
HeapMemoryCreate(DecodeContext & context,const uint32_t & byteCount)434 static bool HeapMemoryCreate(DecodeContext &context, const uint32_t &byteCount)
435 {
436     if (byteCount == 0 || byteCount > PIXEL_MAP_MAX_RAM_SIZE) {
437         IMAGE_LOGE("Invalid value of byteCount");
438         return false;
439     }
440     void *outputBuffer = malloc(byteCount);
441     if (outputBuffer == nullptr) {
442         IMAGE_LOGE("alloc output buffer size:[%{public}llu] error.",
443             static_cast<unsigned long long>(byteCount));
444         return false;
445     }
446 #ifdef _WIN32
447     errno_t backRet = memset_s(outputBuffer, 0, byteCount);
448     if (backRet != EOK) {
449         IMAGE_LOGE("memset buffer failed.", backRet);
450         free(outputBuffer);
451         outputBuffer = nullptr;
452         return false;
453     }
454 #else
455     if (memset_s(outputBuffer, byteCount, 0, byteCount) != EOK) {
456         IMAGE_LOGE("memset buffer failed.");
457         free(outputBuffer);
458         outputBuffer = nullptr;
459         return false;
460     }
461 #endif
462     context.pixelsBuffer.buffer = outputBuffer;
463     context.pixelsBuffer.bufferSize = byteCount;
464     context.pixelsBuffer.context = nullptr;
465     context.allocatorType = AllocatorType::HEAP_ALLOC;
466     context.freeFunc = nullptr;
467     return true;
468 }
469 
DmaMemoryCreate(DecodeContext & context,const uint32_t & byteCount,const Size & webpSize)470 static bool DmaMemoryCreate(DecodeContext &context, const uint32_t &byteCount, const Size &webpSize)
471 {
472 #if defined(_WIN32) || defined(_APPLE) || defined(ANDROID_PLATFORM) || defined(IOS_PLATFORM)
473     IMAGE_LOGE("Unsupport dma mem alloc");
474     return false;
475 #else
476     sptr<SurfaceBuffer> sb = SurfaceBuffer::Create();
477     BufferRequestConfig requestConfig = {
478         .width = webpSize.width,
479         .height = webpSize.height,
480         .strideAlignment = 0x8, // set 0x8 as default value to alloc SurfaceBufferImpl
481         .format = GRAPHIC_PIXEL_FMT_RGBA_8888, // PixelFormat
482         .usage = BUFFER_USAGE_CPU_READ | BUFFER_USAGE_CPU_WRITE | BUFFER_USAGE_MEM_DMA,
483         .timeout = 0,
484         .colorGamut = GraphicColorGamut::GRAPHIC_COLOR_GAMUT_SRGB,
485         .transform = GraphicTransformType::GRAPHIC_ROTATE_NONE,
486     };
487     GSError ret = sb->Alloc(requestConfig);
488     if (ret != GSERROR_OK) {
489         IMAGE_LOGE("SurfaceBuffer Alloc failed, %{public}s", GSErrorStr(ret).c_str());
490         return false;
491     }
492     void* nativeBuffer = sb.GetRefPtr();
493     int32_t err = ImageUtils::SurfaceBuffer_Reference(nativeBuffer);
494     if (err != OHOS::GSERROR_OK) {
495         IMAGE_LOGE("NativeBufferReference failed");
496         return false;
497     }
498 
499     context.pixelsBuffer.buffer = sb->GetVirAddr();
500     context.pixelsBuffer.context = nativeBuffer;
501     context.pixelsBuffer.bufferSize = byteCount;
502     context.allocatorType = AllocatorType::DMA_ALLOC;
503     context.freeFunc = nullptr;
504     return true;
505 #endif
506 }
507 
AllocOutputBuffer(DecodeContext & context,bool isIncremental)508 bool WebpDecoder::AllocOutputBuffer(DecodeContext &context, bool isIncremental)
509 {
510     if (isIncremental) {
511         if (context.pixelsBuffer.buffer != nullptr && context.allocatorType == AllocatorType::HEAP_ALLOC) {
512             free(context.pixelsBuffer.buffer);
513             context.pixelsBuffer.buffer = nullptr;
514         }
515     }
516     if (context.pixelsBuffer.buffer == nullptr) {
517         uint64_t byteCount = static_cast<uint64_t>(webpSize_.width * webpSize_.height * bytesPerPixel_);
518         if (context.allocatorType == Media::AllocatorType::SHARE_MEM_ALLOC) {
519             return SharedMemoryCreate(context, byteCount);
520         } else if (context.allocatorType == Media::AllocatorType::HEAP_ALLOC) {
521             return HeapMemoryCreate(context, byteCount);
522         } else if (context.allocatorType == Media::AllocatorType::DMA_ALLOC) {
523             return DmaMemoryCreate(context, byteCount, webpSize_);
524         }
525         // Current Defalut alloc function
526         return SharedMemoryCreate(context, byteCount);
527     }
528     return true;
529 }
530 } // namespace ImagePlugin
531 } // namespace OHOS
532