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