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