| // Copyright 2024 The Wuffs Authors. | |
| // | |
| // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
| // https://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
| // <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your | |
| // option. This file may not be copied, modified, or distributed | |
| // except according to those terms. | |
| // | |
| // SPDX-License-Identifier: Apache-2.0 OR MIT | |
| #include <limits.h> | |
| #include <stdint.h> | |
| #include "webp/decode.h" | |
| static void // | |
| mimiclib_convert_to_y_from_bgra_nonpremul(uint8_t* dst, | |
| const uint8_t* src, | |
| uint32_t n) { | |
| while (n--) { | |
| uint32_t s0_nonpremul = wuffs_base__peek_u32le__no_bounds_check(src); | |
| uint32_t s0_premul = | |
| wuffs_base__color_u32_argb_nonpremul__as__color_u32_argb_premul( | |
| s0_nonpremul); | |
| dst[0] = wuffs_base__color_u32_argb_premul__as__color_u8_gray(s0_premul); | |
| dst += 1; | |
| src += 4; | |
| } | |
| } | |
| const char* // | |
| mimic_webp_decode(uint64_t* n_bytes_out, | |
| wuffs_base__io_buffer* dst, | |
| uint32_t wuffs_initialize_flags, | |
| wuffs_base__pixel_format pixfmt, | |
| uint32_t* quirks_ptr, | |
| size_t quirks_len, | |
| wuffs_base__io_buffer* src) { | |
| wuffs_base__io_buffer dst_fallback = | |
| wuffs_base__slice_u8__writer(g_mimiclib_scratch_slice_u8); | |
| if (!dst) { | |
| dst = &dst_fallback; | |
| } | |
| WebPDecoderConfig config; | |
| if (!WebPInitDecoderConfig(&config)) { | |
| return "mimic_webp_decode: WebPInitDecoderConfig failed"; | |
| } else if (WebPGetFeatures(wuffs_base__io_buffer__reader_pointer(src), | |
| wuffs_base__io_buffer__reader_length(src), | |
| &config.input) != VP8_STATUS_OK) { | |
| return "mimic_webp_decode: WebPGetFeatures failed"; | |
| } | |
| const uint32_t w = config.input.width; | |
| const uint32_t h = config.input.height; | |
| if ((w > 0x4000) || (h > 0x4000)) { | |
| return "mimic_webp_decode: impossible WebP dimensions"; | |
| } | |
| switch (pixfmt.repr) { | |
| case WUFFS_BASE__PIXEL_FORMAT__Y: | |
| if (((1 * w * h) > wuffs_base__io_buffer__writer_length(dst)) || | |
| ((4 * w * h) > g_mimiclib_scratch_slice_u8.len)) { | |
| return "mimic_webp_decode: image is too large"; | |
| } | |
| config.output.u.RGBA.rgba = g_mimiclib_scratch_slice_u8.ptr; | |
| break; | |
| case WUFFS_BASE__PIXEL_FORMAT__BGRA_NONPREMUL: | |
| if ((4 * w * h) > wuffs_base__io_buffer__writer_length(dst)) { | |
| return "mimic_webp_decode: image is too large"; | |
| } | |
| config.output.u.RGBA.rgba = wuffs_base__io_buffer__writer_pointer(dst); | |
| break; | |
| default: | |
| return "mimic_webp_decode: unsupported pixfmt"; | |
| } | |
| config.output.colorspace = MODE_BGRA; | |
| config.output.width = w; | |
| config.output.height = h; | |
| config.output.is_external_memory = 1; | |
| config.output.u.RGBA.stride = 4 * w; | |
| config.output.u.RGBA.size = 4 * w * h; | |
| VP8StatusCode status = | |
| WebPDecode(wuffs_base__io_buffer__reader_pointer(src), | |
| wuffs_base__io_buffer__reader_length(src), &config); | |
| WebPFreeDecBuffer(&config.output); | |
| if (status != VP8_STATUS_OK) { | |
| return "mimic_webp_decode: WebPDecode failed"; | |
| } | |
| size_t n = 0; | |
| switch (pixfmt.repr) { | |
| case WUFFS_BASE__PIXEL_FORMAT__Y: | |
| mimiclib_convert_to_y_from_bgra_nonpremul( | |
| wuffs_base__io_buffer__writer_pointer(dst), | |
| g_mimiclib_scratch_slice_u8.ptr, 1 * w * h); | |
| n = 1 * w * h; | |
| break; | |
| case WUFFS_BASE__PIXEL_FORMAT__BGRA_NONPREMUL: | |
| n = 4 * w * h; | |
| break; | |
| default: | |
| break; | |
| } | |
| dst->meta.wi += n; | |
| if (n_bytes_out) { | |
| *n_bytes_out += n; | |
| } | |
| return NULL; | |
| } |