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