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