diff --git a/CMakeLists.txt b/CMakeLists.txt index 89672b8e..868dd6c5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -423,10 +423,12 @@ target_include_directories(webputils PRIVATE ${CMAKE_CURRENT_BINARY_DIR} add_library(webp $ $ $ $) if(WEBP_ENABLE_FBOUNDS_SAFETY) - # Enable -fbounds-safety only for webputils for now. + # Enable -fbounds-safety only for webputils and webpdecode(r) for now. add_definitions(-DWEBP_SUPPORT_FBOUNDS_SAFETY=1) target_compile_options(webputils PRIVATE -fbounds-safety) target_compile_options(webputilsdecode PRIVATE -fbounds-safety) + target_compile_options(webpdecode PRIVATE -fbounds-safety) + target_compile_options(webpdecoder PRIVATE -fbounds-safety) endif() target_link_libraries(webp sharpyuv) diff --git a/src/dec/alpha_dec.c b/src/dec/alpha_dec.c index 2632073f..4ef11188 100644 --- a/src/dec/alpha_dec.c +++ b/src/dec/alpha_dec.c @@ -106,7 +106,12 @@ WEBP_NODISCARD static int ALPHInit(ALPHDecoder* const dec, const uint8_t* data, ok = (alpha_data_size >= alpha_decoded_size); } else { assert(dec->method == ALPHA_LOSSLESS_COMPRESSION); - ok = VP8LDecodeAlphaHeader(dec, alpha_data, alpha_data_size); + { + const uint8_t* WEBP_BIDI_INDEXABLE const bounded_alpha_data = + WEBP_UNSAFE_FORGE_BIDI_INDEXABLE(const uint8_t*, alpha_data, + alpha_data_size); + ok = VP8LDecodeAlphaHeader(dec, bounded_alpha_data, alpha_data_size); + } } return ok; @@ -225,7 +230,11 @@ WEBP_NODISCARD const uint8_t* VP8DecompressAlphaRows(VP8Decoder* const dec, if (dec->alpha_dithering > 0) { uint8_t* const alpha = dec->alpha_plane + io->crop_top * width + io->crop_left; - if (!WebPDequantizeLevels(alpha, io->crop_right - io->crop_left, + uint8_t* WEBP_BIDI_INDEXABLE const bounded_alpha = + WEBP_UNSAFE_FORGE_BIDI_INDEXABLE( + uint8_t*, alpha, + (size_t)width*(io->crop_bottom - io->crop_top)); + if (!WebPDequantizeLevels(bounded_alpha, io->crop_right - io->crop_left, io->crop_bottom - io->crop_top, width, dec->alpha_dithering)) { goto Error; diff --git a/src/dec/idec_dec.c b/src/dec/idec_dec.c index dd0e0879..d5e7a9cb 100644 --- a/src/dec/idec_dec.c +++ b/src/dec/idec_dec.c @@ -144,20 +144,30 @@ static void DoRemap(WebPIDecoder* const idec, ptrdiff_t offset) { // parse the partitions). The bitreader is only used meaningfully when // there is enough data to begin parsing partition 0. if (last_start != NULL) { - VP8BitReaderSetBuffer(&dec->parts[last_part], last_start, - mem->buf + mem->end - last_start); + const size_t part_size = mem->buf + mem->end - last_start; + const uint8_t* WEBP_BIDI_INDEXABLE const bounded_last_start = + WEBP_UNSAFE_FORGE_BIDI_INDEXABLE(const uint8_t*, last_start, + part_size); + VP8BitReaderSetBuffer(&dec->parts[last_part], bounded_last_start, + part_size); } } if (NeedCompressedAlpha(idec)) { ALPHDecoder* const alph_dec = dec->alph_dec; dec->alpha_data += offset; + dec->alpha_data_size = dec->alpha_data_size; if (alph_dec != NULL && alph_dec->vp8l_dec != NULL) { if (alph_dec->method == ALPHA_LOSSLESS_COMPRESSION) { VP8LDecoder* const alph_vp8l_dec = alph_dec->vp8l_dec; + size_t data_size; + const uint8_t* WEBP_BIDI_INDEXABLE bounded_alpha_data; + assert(dec->alpha_data_size >= ALPHA_HEADER_LEN); - VP8LBitReaderSetBuffer(&alph_vp8l_dec->br, - dec->alpha_data + ALPHA_HEADER_LEN, - dec->alpha_data_size - ALPHA_HEADER_LEN); + data_size = dec->alpha_data_size - ALPHA_HEADER_LEN; + bounded_alpha_data = WEBP_UNSAFE_FORGE_BIDI_INDEXABLE( + const uint8_t*, dec->alpha_data + ALPHA_HEADER_LEN, data_size); + VP8LBitReaderSetBuffer(&alph_vp8l_dec->br, bounded_alpha_data, + data_size); } else { // alph_dec->method == ALPHA_NO_COMPRESSION // Nothing special to do in this case. } @@ -165,7 +175,10 @@ static void DoRemap(WebPIDecoder* const idec, ptrdiff_t offset) { } } else { // Resize lossless bitreader VP8LDecoder* const dec = (VP8LDecoder*)idec->dec; - VP8LBitReaderSetBuffer(&dec->br, new_base, MemDataSize(mem)); + const size_t data_size = MemDataSize(mem); + const uint8_t* WEBP_BIDI_INDEXABLE const bounded_new_base = + WEBP_UNSAFE_FORGE_BIDI_INDEXABLE(const uint8_t*, new_base, data_size); + VP8LBitReaderSetBuffer(&dec->br, bounded_new_base, data_size); } } } @@ -328,7 +341,8 @@ static VP8StatusCode DecodeWebPHeaders(WebPIDecoder* const idec) { VP8StatusCode status; WebPHeaderStructure headers; - headers.data = data; + headers.data = + WEBP_UNSAFE_FORGE_BIDI_INDEXABLE(const uint8_t*, data, curr_size); headers.data_size = curr_size; headers.have_all_data = 0; status = WebPParseHeaders(&headers); @@ -371,8 +385,13 @@ static VP8StatusCode DecodeVP8FrameHeader(WebPIDecoder* const idec) { // Not enough data bytes to extract VP8 Frame Header. return VP8_STATUS_SUSPENDED; } - if (!VP8GetInfo(data, curr_size, idec->chunk_size, &width, &height)) { - return IDecError(idec, VP8_STATUS_BITSTREAM_ERROR); + { + const uint8_t* WEBP_BIDI_INDEXABLE const bounded_data = + WEBP_UNSAFE_FORGE_BIDI_INDEXABLE(const uint8_t*, data, curr_size); + if (!VP8GetInfo(bounded_data, curr_size, idec->chunk_size, &width, + &height)) { + return IDecError(idec, VP8_STATUS_BITSTREAM_ERROR); + } } bits = data[0] | (data[1] << 8) | (data[2] << 16); @@ -399,7 +418,8 @@ static VP8StatusCode CopyParts0Data(WebPIDecoder* const idec) { } if (mem->mode == MEM_MODE_APPEND) { // We copy and grab ownership of the partition #0 data. - uint8_t* const part0_buf = (uint8_t*)WebPSafeMalloc(1ULL, part_size); + uint8_t* WEBP_BIDI_INDEXABLE const part0_buf = + (uint8_t*)WebPSafeMalloc(1ULL, part_size); if (part0_buf == NULL) { return VP8_STATUS_OUT_OF_MEMORY; } @@ -660,8 +680,8 @@ WebPIDecoder* WebPINewDecoder(WebPDecBuffer* output_buffer) { return NewDecoder(output_buffer, NULL); } -WebPIDecoder* WebPIDecode(const uint8_t* data, size_t data_size, - WebPDecoderConfig* config) { +WebPIDecoder* WebPIDecode(const uint8_t* WEBP_COUNTED_BY(data_size) data, + size_t data_size, WebPDecoderConfig* config) { WebPIDecoder* idec; WebPBitstreamFeatures tmp_features; WebPBitstreamFeatures* const features = @@ -710,13 +730,16 @@ void WebPIDelete(WebPIDecoder* idec) { //------------------------------------------------------------------------------ // Wrapper toward WebPINewDecoder -WebPIDecoder* WebPINewRGB(WEBP_CSP_MODE csp, uint8_t* output_buffer, +WebPIDecoder* WebPINewRGB(WEBP_CSP_MODE csp, + uint8_t* WEBP_COUNTED_BY(output_buffer_size) + output_buffer, size_t output_buffer_size, int output_stride) { const int is_external_memory = (output_buffer != NULL) ? 1 : 0; WebPIDecoder* idec; if (csp >= MODE_YUV) return NULL; if (is_external_memory == 0) { // Overwrite parameters to sane values. + output_buffer = NULL; output_buffer_size = 0; output_stride = 0; } else { // A buffer was passed. Validate the other params. @@ -734,18 +757,27 @@ WebPIDecoder* WebPINewRGB(WEBP_CSP_MODE csp, uint8_t* output_buffer, return idec; } -WebPIDecoder* WebPINewYUVA(uint8_t* luma, size_t luma_size, int luma_stride, - uint8_t* u, size_t u_size, int u_stride, uint8_t* v, - size_t v_size, int v_stride, uint8_t* a, - size_t a_size, int a_stride) { +WebPIDecoder* WebPINewYUVA(uint8_t* WEBP_COUNTED_BY(luma_size) luma, + size_t luma_size, int luma_stride, + uint8_t* WEBP_COUNTED_BY(u_size) u, size_t u_size, + int u_stride, uint8_t* WEBP_COUNTED_BY(v_size) v, + size_t v_size, int v_stride, + uint8_t* WEBP_COUNTED_BY(a_size) a, size_t a_size, + int a_stride) { const int is_external_memory = (luma != NULL) ? 1 : 0; WebPIDecoder* idec; WEBP_CSP_MODE colorspace; if (is_external_memory == 0) { // Overwrite parameters to sane values. - luma_size = u_size = v_size = a_size = 0; + luma = NULL; + luma_size = 0; + u = NULL; + u_size = 0; + v = NULL; + v_size = 0; + a = NULL; + a_size = 0; luma_stride = u_stride = v_stride = a_stride = 0; - u = v = a = NULL; colorspace = MODE_YUVA; } else { // A luma buffer was passed. Validate the other parameters. if (u == NULL || v == NULL) return NULL; @@ -777,8 +809,10 @@ WebPIDecoder* WebPINewYUVA(uint8_t* luma, size_t luma_size, int luma_stride, return idec; } -WebPIDecoder* WebPINewYUV(uint8_t* luma, size_t luma_size, int luma_stride, - uint8_t* u, size_t u_size, int u_stride, uint8_t* v, +WebPIDecoder* WebPINewYUV(uint8_t* WEBP_COUNTED_BY(luma_size) luma, + size_t luma_size, int luma_stride, + uint8_t* WEBP_COUNTED_BY(u_size) u, size_t u_size, + int u_stride, uint8_t* WEBP_COUNTED_BY(v_size) v, size_t v_size, int v_stride) { return WebPINewYUVA(luma, luma_size, luma_stride, u, u_size, u_stride, v, v_size, v_stride, NULL, 0, 0); @@ -797,7 +831,8 @@ static VP8StatusCode IDecCheckStatus(const WebPIDecoder* const idec) { return VP8_STATUS_SUSPENDED; } -VP8StatusCode WebPIAppend(WebPIDecoder* idec, const uint8_t* data, +VP8StatusCode WebPIAppend(WebPIDecoder* idec, + const uint8_t* WEBP_COUNTED_BY(data_size) data, size_t data_size) { VP8StatusCode status; if (idec == NULL || data == NULL) { @@ -818,7 +853,8 @@ VP8StatusCode WebPIAppend(WebPIDecoder* idec, const uint8_t* data, return IDecode(idec); } -VP8StatusCode WebPIUpdate(WebPIDecoder* idec, const uint8_t* data, +VP8StatusCode WebPIUpdate(WebPIDecoder* idec, + const uint8_t* WEBP_COUNTED_BY(data_size) data, size_t data_size) { VP8StatusCode status; if (idec == NULL || data == NULL) { diff --git a/src/dec/io_dec.c b/src/dec/io_dec.c index d96d11aa..b34bf7d0 100644 --- a/src/dec/io_dec.c +++ b/src/dec/io_dec.c @@ -311,7 +311,7 @@ static int InitYUVRescaler(const VP8Io* const io, WebPDecParams* const p) { const size_t uv_work_size = 2 * uv_out_width; // and for each u/v ones uint64_t total_size; size_t rescaler_size; - rescaler_t* work; + rescaler_t* WEBP_BIDI_INDEXABLE work; WebPRescaler* scalers; const int num_rescalers = has_alpha ? 4 : 3; @@ -325,11 +325,11 @@ static int InitYUVRescaler(const VP8Io* const io, WebPDecParams* const p) { return 0; } - p->memory = WebPSafeMalloc(1ULL, (size_t)total_size); - if (p->memory == NULL) { + work = (rescaler_t*)WebPSafeMalloc(1ULL, (size_t)total_size); + if (work == NULL) { return 0; // memory error } - work = (rescaler_t*)p->memory; + p->memory = work; scalers = (WebPRescaler*)WEBP_ALIGN((const uint8_t*)work + total_size - rescaler_size); @@ -499,8 +499,9 @@ static int InitRGBRescaler(const VP8Io* const io, WebPDecParams* const p) { const int uv_in_height = (io->mb_h + 1) >> 1; // scratch memory for one rescaler const size_t work_size = 2 * (size_t)out_width; - rescaler_t* work; // rescalers work area - uint8_t* tmp; // tmp storage for scaled YUV444 samples before RGB conversion + rescaler_t* WEBP_BIDI_INDEXABLE work; // rescalers work area + uint8_t* WEBP_BIDI_INDEXABLE + tmp; // tmp storage for scaled YUV444 samples before RGB conversion uint64_t tmp_size1, tmp_size2, total_size; size_t rescaler_size; WebPRescaler* scalers; @@ -515,11 +516,11 @@ static int InitRGBRescaler(const VP8Io* const io, WebPDecParams* const p) { return 0; } - p->memory = WebPSafeMalloc(1ULL, (size_t)total_size); - if (p->memory == NULL) { + work = (rescaler_t*)WebPSafeMalloc(1ULL, (size_t)total_size); + if (work == NULL) { return 0; // memory error } - work = (rescaler_t*)p->memory; + p->memory = work; tmp = (uint8_t*)(work + tmp_size1); scalers = (WebPRescaler*)WEBP_ALIGN((const uint8_t*)work + total_size - diff --git a/src/dec/vp8_dec.c b/src/dec/vp8_dec.c index 2f675fad..26831042 100644 --- a/src/dec/vp8_dec.c +++ b/src/dec/vp8_dec.c @@ -113,13 +113,14 @@ int VP8SetError(VP8Decoder* const dec, VP8StatusCode error, //------------------------------------------------------------------------------ -int VP8CheckSignature(const uint8_t* const data, size_t data_size) { +int VP8CheckSignature(const uint8_t* const WEBP_COUNTED_BY(data_size) data, + size_t data_size) { return (data_size >= 3 && data[0] == 0x9d && data[1] == 0x01 && data[2] == 0x2a); } -int VP8GetInfo(const uint8_t* data, size_t data_size, size_t chunk_size, - int* const width, int* const height) { +int VP8GetInfo(const uint8_t* WEBP_COUNTED_BY(data_size) data, size_t data_size, + size_t chunk_size, int* const width, int* const height) { if (data == NULL || data_size < VP8_FRAME_HEADER_SIZE) { return 0; // not enough data } @@ -218,12 +219,13 @@ static int ParseSegmentHeader(VP8BitReader* br, VP8SegmentHeader* hdr, // If we don't even have the partitions' sizes, then VP8_STATUS_NOT_ENOUGH_DATA // is returned, and this is an unrecoverable error. // If the partitions were positioned ok, VP8_STATUS_OK is returned. -static VP8StatusCode ParsePartitions(VP8Decoder* const dec, const uint8_t* buf, +static VP8StatusCode ParsePartitions(VP8Decoder* const dec, + const uint8_t* WEBP_COUNTED_BY(size) buf, size_t size) { VP8BitReader* const br = &dec->br; - const uint8_t* sz = buf; + const uint8_t* WEBP_BIDI_INDEXABLE sz = buf; const uint8_t* buf_end = buf + size; - const uint8_t* part_start; + const uint8_t* WEBP_BIDI_INDEXABLE part_start; size_t size_left = size; size_t last_part; size_t p; @@ -279,8 +281,8 @@ static int ParseFilterHeader(VP8BitReader* br, VP8Decoder* const dec) { // Topmost call int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) { - const uint8_t* buf; size_t buf_size; + const uint8_t* WEBP_COUNTED_BY(buf_size) buf; VP8FrameHeader* frm_hdr; VP8PictureHeader* pic_hdr; VP8BitReader* br; @@ -294,8 +296,9 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) { return VP8SetError(dec, VP8_STATUS_INVALID_PARAM, "null VP8Io passed to VP8GetHeaders()"); } - buf = io->data; buf_size = io->data_size; + buf = + WEBP_UNSAFE_FORGE_BIDI_INDEXABLE(const uint8_t*, io->data, io->data_size); if (buf_size < 4) { return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA, "Truncated header."); } diff --git a/src/dec/vp8_dec.h b/src/dec/vp8_dec.h index a0f66ba6..5cccae6b 100644 --- a/src/dec/vp8_dec.h +++ b/src/dec/vp8_dec.h @@ -160,24 +160,27 @@ void VP8Delete(VP8Decoder* const dec); // Miscellaneous VP8/VP8L bitstream probing functions. // Returns true if the next 3 bytes in data contain the VP8 signature. -WEBP_EXTERN int VP8CheckSignature(const uint8_t* const data, size_t data_size); +WEBP_EXTERN int VP8CheckSignature( + const uint8_t* const WEBP_COUNTED_BY(data_size) data, size_t data_size); // Validates the VP8 data-header and retrieves basic header information viz // width and height. Returns 0 in case of formatting error. *width/*height // can be passed NULL. WEBP_EXTERN int VP8GetInfo( - const uint8_t* data, + const uint8_t* WEBP_COUNTED_BY(data_size) data, size_t data_size, // data available so far size_t chunk_size, // total data size expected in the chunk int* const width, int* const height); // Returns true if the next byte(s) in data is a VP8L signature. -WEBP_EXTERN int VP8LCheckSignature(const uint8_t* const data, size_t size); +WEBP_EXTERN int VP8LCheckSignature(const uint8_t* const WEBP_COUNTED_BY(size) + data, + size_t size); // Validates the VP8L data-header and retrieves basic header information viz // width, height and alpha. Returns 0 in case of formatting error. // width/height/has_alpha can be passed NULL. -WEBP_EXTERN int VP8LGetInfo(const uint8_t* data, +WEBP_EXTERN int VP8LGetInfo(const uint8_t* WEBP_COUNTED_BY(data_size) data, size_t data_size, // data available so far int* const width, int* const height, int* const has_alpha); diff --git a/src/dec/vp8i_dec.h b/src/dec/vp8i_dec.h index 28ec15c5..ec074ed1 100644 --- a/src/dec/vp8i_dec.h +++ b/src/dec/vp8i_dec.h @@ -263,7 +263,8 @@ struct VP8Decoder { // Alpha struct ALPHDecoder* alph_dec; // alpha-plane decoder object - const uint8_t* alpha_data; // compressed alpha data (if present) + const uint8_t* WEBP_COUNTED_BY(alpha_data_size) + alpha_data; // compressed alpha data (if present) size_t alpha_data_size; int is_alpha_decoded; // true if alpha_data is decoded in alpha_plane uint8_t* alpha_plane_mem; // memory allocated for alpha_plane diff --git a/src/dec/vp8l_dec.c b/src/dec/vp8l_dec.c index 7f6b759a..44bae3d4 100644 --- a/src/dec/vp8l_dec.c +++ b/src/dec/vp8l_dec.c @@ -104,7 +104,8 @@ static int DecodeImageStream(int xsize, int ysize, int is_level0, //------------------------------------------------------------------------------ -int VP8LCheckSignature(const uint8_t* const data, size_t size) { +int VP8LCheckSignature(const uint8_t* const WEBP_COUNTED_BY(size) data, + size_t size) { return (size >= VP8L_FRAME_HEADER_SIZE && data[0] == VP8L_MAGIC_BYTE && (data[4] >> 5) == 0); // version } @@ -119,8 +120,9 @@ static int ReadImageInfo(VP8LBitReader* const br, int* const width, return !br->eos; } -int VP8LGetInfo(const uint8_t* data, size_t data_size, int* const width, - int* const height, int* const has_alpha) { +int VP8LGetInfo(const uint8_t* WEBP_COUNTED_BY(data_size) data, + size_t data_size, int* const width, int* const height, + int* const has_alpha) { if (data == NULL || data_size < VP8L_FRAME_HEADER_SIZE) { return 0; // not enough data } else if (!VP8LCheckSignature(data, data_size)) { @@ -248,10 +250,14 @@ static int ReadHuffmanCodeLengths(VP8LDecoder* const dec, int max_symbol; int prev_code_len = DEFAULT_CODE_LENGTH; HuffmanTables tables; + const int* WEBP_BIDI_INDEXABLE const bounded_code_lengths = + WEBP_UNSAFE_FORGE_BIDI_INDEXABLE( + const int*, code_length_code_lengths, + NUM_CODE_LENGTH_CODES * sizeof(*code_length_code_lengths)); if (!VP8LHuffmanTablesAllocate(1 << LENGTHS_TABLE_BITS, &tables) || - !VP8LBuildHuffmanTable(&tables, LENGTHS_TABLE_BITS, - code_length_code_lengths, NUM_CODE_LENGTH_CODES)) { + !VP8LBuildHuffmanTable(&tables, LENGTHS_TABLE_BITS, bounded_code_lengths, + NUM_CODE_LENGTH_CODES)) { goto End; } @@ -338,8 +344,11 @@ static int ReadHuffmanCode(int alphabet_size, VP8LDecoder* const dec, ok = ok && !br->eos; if (ok) { - size = VP8LBuildHuffmanTable(table, HUFFMAN_TABLE_BITS, code_lengths, - alphabet_size); + const int* WEBP_BIDI_INDEXABLE const bounded_code_lengths = + WEBP_UNSAFE_FORGE_BIDI_INDEXABLE(const int*, code_lengths, + alphabet_size * sizeof(int)); + size = VP8LBuildHuffmanTable(table, HUFFMAN_TABLE_BITS, + bounded_code_lengths, alphabet_size); } if (!ok || size == 0) { return VP8LSetError(dec, VP8_STATUS_BITSTREAM_ERROR); @@ -553,13 +562,15 @@ static int AllocateAndInitRescaler(VP8LDecoder* const dec, VP8Io* const io) { const int in_height = io->mb_h; const int out_height = io->scaled_height; const uint64_t work_size = 2 * num_channels * (uint64_t)out_width; - rescaler_t* work; // Rescaler work area. + rescaler_t* WEBP_BIDI_INDEXABLE work; // Rescaler work area. const uint64_t scaled_data_size = (uint64_t)out_width; - uint32_t* scaled_data; // Temporary storage for scaled BGRA data. + uint32_t* WEBP_BIDI_INDEXABLE + scaled_data; // Temporary storage for scaled BGRA data. const uint64_t memory_size = sizeof(*dec->rescaler) + work_size * sizeof(*work) + scaled_data_size * sizeof(*scaled_data); - uint8_t* memory = (uint8_t*)WebPSafeMalloc(memory_size, sizeof(*memory)); + uint8_t* WEBP_BIDI_INDEXABLE memory = + (uint8_t*)WebPSafeMalloc(memory_size, sizeof(*memory)); if (memory == NULL) { return VP8LSetError(dec, VP8_STATUS_OUT_OF_MEMORY); } @@ -1684,7 +1695,8 @@ static void ExtractAlphaRows(VP8LDecoder* const dec, int last_row, } int VP8LDecodeAlphaHeader(ALPHDecoder* const alph_dec, - const uint8_t* const data, size_t data_size) { + const uint8_t* const WEBP_COUNTED_BY(data_size) data, + size_t data_size) { int ok = 0; VP8LDecoder* dec = VP8LNew(); @@ -1763,7 +1775,12 @@ int VP8LDecodeHeader(VP8LDecoder* const dec, VP8Io* const io) { dec->io = io; dec->status = VP8_STATUS_OK; - VP8LInitBitReader(&dec->br, io->data, io->data_size); + { + const uint8_t* WEBP_BIDI_INDEXABLE const bounded_data = + WEBP_UNSAFE_FORGE_BIDI_INDEXABLE(const uint8_t*, io->data, + io->data_size); + VP8LInitBitReader(&dec->br, bounded_data, io->data_size); + } if (!ReadImageInfo(&dec->br, &width, &height, &has_alpha)) { VP8LSetError(dec, VP8_STATUS_BITSTREAM_ERROR); goto Error; diff --git a/src/dec/vp8li_dec.h b/src/dec/vp8li_dec.h index cbf3ce1f..5a3bfcb4 100644 --- a/src/dec/vp8li_dec.h +++ b/src/dec/vp8li_dec.h @@ -105,9 +105,9 @@ struct ALPHDecoder; // Defined in dec/alphai.h. // Decodes image header for alpha data stored using lossless compression. // Returns false in case of error. -WEBP_NODISCARD int VP8LDecodeAlphaHeader(struct ALPHDecoder* const alph_dec, - const uint8_t* const data, - size_t data_size); +WEBP_NODISCARD int VP8LDecodeAlphaHeader( + struct ALPHDecoder* const alph_dec, + const uint8_t* const WEBP_COUNTED_BY(data_size) data, size_t data_size); // Decodes *at least* 'last_row' rows of alpha. If some of the initial rows are // already decoded in previous call(s), it will resume decoding from where it diff --git a/src/dec/webp_dec.c b/src/dec/webp_dec.c index d1ef1943..650f446a 100644 --- a/src/dec/webp_dec.c +++ b/src/dec/webp_dec.c @@ -61,9 +61,11 @@ WEBP_ASSUME_UNSAFE_INDEXABLE_ABI // and VP8_STATUS_OK otherwise. // In case there are not enough bytes (partial RIFF container), return 0 for // *riff_size. Else return the RIFF size extracted from the header. -static VP8StatusCode ParseRIFF(const uint8_t** const data, - size_t* const data_size, int have_all_data, - size_t* const riff_size) { +static VP8StatusCode ParseRIFF(const uint8_t* WEBP_COUNTED_BY(*data_size) * + WEBP_SINGLE const data, + size_t* WEBP_SINGLE const data_size, + int have_all_data, + size_t* WEBP_SINGLE const riff_size) { assert(data != NULL); assert(data_size != NULL); assert(riff_size != NULL); @@ -86,8 +88,8 @@ static VP8StatusCode ParseRIFF(const uint8_t** const data, } // We have a RIFF container. Skip it. *riff_size = size; - *data += RIFF_HEADER_SIZE; *data_size -= RIFF_HEADER_SIZE; + *data += RIFF_HEADER_SIZE; } } return VP8_STATUS_OK; @@ -100,10 +102,13 @@ static VP8StatusCode ParseRIFF(const uint8_t** const data, // If a VP8X chunk is found, found_vp8x is set to true and *width_ptr, // *height_ptr and *flags_ptr are set to the corresponding values extracted // from the VP8X chunk. -static VP8StatusCode ParseVP8X(const uint8_t** const data, - size_t* const data_size, int* const found_vp8x, - int* const width_ptr, int* const height_ptr, - uint32_t* const flags_ptr) { +static VP8StatusCode ParseVP8X(const uint8_t* WEBP_COUNTED_BY(*data_size) * + WEBP_SINGLE const data, + size_t* WEBP_SINGLE const data_size, + int* WEBP_SINGLE const found_vp8x, + int* WEBP_SINGLE const width_ptr, + int* WEBP_SINGLE const height_ptr, + uint32_t* WEBP_SINGLE const flags_ptr) { const uint32_t vp8x_size = CHUNK_HEADER_SIZE + VP8X_CHUNK_SIZE; assert(data != NULL); assert(data_size != NULL); @@ -138,8 +143,8 @@ static VP8StatusCode ParseVP8X(const uint8_t** const data, if (width_ptr != NULL) *width_ptr = width; if (height_ptr != NULL) *height_ptr = height; // Skip over VP8X header bytes. - *data += vp8x_size; *data_size -= vp8x_size; + *data += vp8x_size; *found_vp8x = 1; } return VP8_STATUS_OK; @@ -152,13 +157,13 @@ static VP8StatusCode ParseVP8X(const uint8_t** const data, // VP8_STATUS_OK otherwise. // If an alpha chunk is found, *alpha_data and *alpha_size are set // appropriately. -static VP8StatusCode ParseOptionalChunks(const uint8_t** const data, - size_t* const data_size, - size_t const riff_size, - const uint8_t** const alpha_data, - size_t* const alpha_size) { - const uint8_t* buf; +static VP8StatusCode ParseOptionalChunks( + const uint8_t* WEBP_COUNTED_BY(*data_size) * WEBP_SINGLE const data, + size_t* WEBP_SINGLE const data_size, size_t const riff_size, + const uint8_t* WEBP_COUNTED_BY(*alpha_size) * WEBP_SINGLE const alpha_data, + size_t* WEBP_SINGLE const alpha_size) { size_t buf_size; + const uint8_t* WEBP_COUNTED_BY(buf_size) buf; uint32_t total_size = TAG_SIZE + // "WEBP". CHUNK_HEADER_SIZE + // "VP8Xnnnn". VP8X_CHUNK_SIZE; // data. @@ -176,8 +181,8 @@ static VP8StatusCode ParseOptionalChunks(const uint8_t** const data, uint32_t chunk_size; uint32_t disk_chunk_size; // chunk_size with padding - *data = buf; *data_size = buf_size; + *data = buf; if (buf_size < CHUNK_HEADER_SIZE) { // Insufficient data. return VP8_STATUS_NOT_ENOUGH_DATA; @@ -227,16 +232,20 @@ static VP8StatusCode ParseOptionalChunks(const uint8_t** const data, // If a VP8/VP8L chunk is found, *chunk_size is set to the total number of bytes // extracted from the VP8/VP8L chunk header. // The flag '*is_lossless' is set to 1 in case of VP8L chunk / raw VP8L data. -static VP8StatusCode ParseVP8Header(const uint8_t** const data_ptr, - size_t* const data_size, int have_all_data, - size_t riff_size, size_t* const chunk_size, - int* const is_lossless) { - const uint8_t* const data = *data_ptr; +static VP8StatusCode ParseVP8Header(const uint8_t* WEBP_COUNTED_BY(*data_size) * + WEBP_SINGLE const data_ptr, + size_t* WEBP_SINGLE const data_size, + int have_all_data, size_t riff_size, + size_t* WEBP_SINGLE const chunk_size, + int* WEBP_SINGLE const is_lossless) { + const size_t local_data_size = *data_size; + const uint8_t* WEBP_COUNTED_BY(local_data_size) const data = *data_ptr; const int is_vp8 = !memcmp(data, "VP8 ", TAG_SIZE); const int is_vp8l = !memcmp(data, "VP8L", TAG_SIZE); const uint32_t minimal_size = TAG_SIZE + CHUNK_HEADER_SIZE; // "WEBP" + "VP8 nnnn" OR // "WEBP" + "VP8Lnnnn" + (void)local_data_size; assert(data != NULL); assert(data_size != NULL); assert(chunk_size != NULL); @@ -257,8 +266,8 @@ static VP8StatusCode ParseVP8Header(const uint8_t** const data_ptr, } // Skip over CHUNK_HEADER_SIZE bytes from VP8/VP8L Header. *chunk_size = size; - *data_ptr += CHUNK_HEADER_SIZE; *data_size -= CHUNK_HEADER_SIZE; + *data_ptr += CHUNK_HEADER_SIZE; *is_lossless = is_vp8l; } else { // Raw VP8/VP8L bitstream (no header). @@ -282,12 +291,13 @@ static VP8StatusCode ParseVP8Header(const uint8_t** const data_ptr, // RIFF + VP8X + (optional chunks) + VP8(L) // ALPH + VP8 <-- Not a valid WebP format: only allowed for internal purpose. // VP8(L) <-- Not a valid WebP format: only allowed for internal purpose. -static VP8StatusCode ParseHeadersInternal(const uint8_t* data, size_t data_size, - int* const width, int* const height, - int* const has_alpha, - int* const has_animation, - int* const format, - WebPHeaderStructure* const headers) { +static VP8StatusCode ParseHeadersInternal( + const uint8_t* WEBP_COUNTED_BY(data_size_param) data_param, + size_t data_size_param, int* const width, int* const height, + int* const has_alpha, int* const has_animation, int* const format, + WebPHeaderStructure* const headers) { + size_t data_size = data_size_param; + const uint8_t* WEBP_COUNTED_BY(data_size) data = data_param; int canvas_width = 0; int canvas_height = 0; int image_width = 0; @@ -348,11 +358,16 @@ static VP8StatusCode ParseHeadersInternal(const uint8_t* data, size_t data_size, // Skip over optional chunks if data started with "RIFF + VP8X" or "ALPH". if ((found_riff && found_vp8x) || (!found_riff && !found_vp8x && !memcmp(data, "ALPH", TAG_SIZE))) { + size_t local_alpha_data_size = 0; + const uint8_t* WEBP_COUNTED_BY(local_alpha_data_size) local_alpha_data = + NULL; status = ParseOptionalChunks(&data, &data_size, hdrs.riff_size, - &hdrs.alpha_data, &hdrs.alpha_data_size); + &local_alpha_data, &local_alpha_data_size); if (status != VP8_STATUS_OK) { goto ReturnWidthHeight; // Invalid chunk size / insufficient data. } + hdrs.alpha_data = local_alpha_data; + hdrs.alpha_data_size = local_alpha_data_size; } // Skip over VP8/VP8L header. @@ -423,8 +438,13 @@ VP8StatusCode WebPParseHeaders(WebPHeaderStructure* const headers) { int has_animation = 0; assert(headers != NULL); // fill out headers, ignore width/height/has_alpha. - status = ParseHeadersInternal(headers->data, headers->data_size, NULL, NULL, - NULL, &has_animation, NULL, headers); + { + const uint8_t* WEBP_BIDI_INDEXABLE const bounded_data = + WEBP_UNSAFE_FORGE_BIDI_INDEXABLE(const uint8_t*, headers->data, + headers->data_size); + status = ParseHeadersInternal(bounded_data, headers->data_size, NULL, NULL, + NULL, &has_animation, NULL, headers); + } if (status == VP8_STATUS_OK || status == VP8_STATUS_NOT_ENOUGH_DATA) { // The WebPDemux API + libwebp can be used to decode individual // uncomposited frames or the WebPAnimDecoder can be used to fully @@ -449,9 +469,9 @@ void WebPResetDecParams(WebPDecParams* const params) { // "Into" decoding variants // Main flow -WEBP_NODISCARD static VP8StatusCode DecodeInto(const uint8_t* const data, - size_t data_size, - WebPDecParams* const params) { +WEBP_NODISCARD static VP8StatusCode DecodeInto( + const uint8_t* WEBP_COUNTED_BY(data_size) const data, size_t data_size, + WebPDecParams* const params) { VP8StatusCode status; VP8Io io; WebPHeaderStructure headers; @@ -531,11 +551,10 @@ WEBP_NODISCARD static VP8StatusCode DecodeInto(const uint8_t* const data, } // Helpers -WEBP_NODISCARD static uint8_t* DecodeIntoRGBABuffer(WEBP_CSP_MODE colorspace, - const uint8_t* const data, - size_t data_size, - uint8_t* const rgba, - int stride, size_t size) { +WEBP_NODISCARD static uint8_t* DecodeIntoRGBABuffer( + WEBP_CSP_MODE colorspace, + const uint8_t* WEBP_COUNTED_BY(data_size) const data, size_t data_size, + uint8_t* WEBP_COUNTED_BY(size) const rgba, int stride, size_t size) { WebPDecParams params; WebPDecBuffer buf; if (rgba == NULL || !WebPInitDecBuffer(&buf)) { @@ -554,34 +573,47 @@ WEBP_NODISCARD static uint8_t* DecodeIntoRGBABuffer(WEBP_CSP_MODE colorspace, return rgba; } -uint8_t* WebPDecodeRGBInto(const uint8_t* data, size_t data_size, - uint8_t* output, size_t size, int stride) { +uint8_t* WebPDecodeRGBInto(const uint8_t* WEBP_COUNTED_BY(data_size) data, + size_t data_size, + uint8_t* WEBP_COUNTED_BY(size) output, size_t size, + int stride) { return DecodeIntoRGBABuffer(MODE_RGB, data, data_size, output, stride, size); } -uint8_t* WebPDecodeRGBAInto(const uint8_t* data, size_t data_size, - uint8_t* output, size_t size, int stride) { +uint8_t* WebPDecodeRGBAInto(const uint8_t* WEBP_COUNTED_BY(data_size) data, + size_t data_size, + uint8_t* WEBP_COUNTED_BY(size) output, size_t size, + int stride) { return DecodeIntoRGBABuffer(MODE_RGBA, data, data_size, output, stride, size); } -uint8_t* WebPDecodeARGBInto(const uint8_t* data, size_t data_size, - uint8_t* output, size_t size, int stride) { +uint8_t* WebPDecodeARGBInto(const uint8_t* WEBP_COUNTED_BY(data_size) data, + size_t data_size, + uint8_t* WEBP_COUNTED_BY(size) output, size_t size, + int stride) { return DecodeIntoRGBABuffer(MODE_ARGB, data, data_size, output, stride, size); } -uint8_t* WebPDecodeBGRInto(const uint8_t* data, size_t data_size, - uint8_t* output, size_t size, int stride) { +uint8_t* WebPDecodeBGRInto(const uint8_t* WEBP_COUNTED_BY(data_size) data, + size_t data_size, + uint8_t* WEBP_COUNTED_BY(size) output, size_t size, + int stride) { return DecodeIntoRGBABuffer(MODE_BGR, data, data_size, output, stride, size); } -uint8_t* WebPDecodeBGRAInto(const uint8_t* data, size_t data_size, - uint8_t* output, size_t size, int stride) { +uint8_t* WebPDecodeBGRAInto(const uint8_t* WEBP_COUNTED_BY(data_size) data, + size_t data_size, + uint8_t* WEBP_COUNTED_BY(size) output, size_t size, + int stride) { return DecodeIntoRGBABuffer(MODE_BGRA, data, data_size, output, stride, size); } -uint8_t* WebPDecodeYUVInto(const uint8_t* data, size_t data_size, uint8_t* luma, - size_t luma_size, int luma_stride, uint8_t* u, - size_t u_size, int u_stride, uint8_t* v, +uint8_t* WebPDecodeYUVInto(const uint8_t* WEBP_COUNTED_BY(data_size) data, + size_t data_size, + uint8_t* WEBP_COUNTED_BY(luma_size) luma, + size_t luma_size, int luma_stride, + uint8_t* WEBP_COUNTED_BY(u_size) u, size_t u_size, + int u_stride, uint8_t* WEBP_COUNTED_BY(v_size) v, size_t v_size, int v_stride) { WebPDecParams params; WebPDecBuffer output; @@ -608,7 +640,8 @@ uint8_t* WebPDecodeYUVInto(const uint8_t* data, size_t data_size, uint8_t* luma, //------------------------------------------------------------------------------ WEBP_NODISCARD static uint8_t* Decode(WEBP_CSP_MODE mode, - const uint8_t* const data, + const uint8_t* WEBP_COUNTED_BY(data_size) + const data, size_t data_size, int* const width, int* const height, WebPDecBuffer* const keep_info) { @@ -640,34 +673,34 @@ WEBP_NODISCARD static uint8_t* Decode(WEBP_CSP_MODE mode, return WebPIsRGBMode(mode) ? output.u.RGBA.rgba : output.u.YUVA.y; } -uint8_t* WebPDecodeRGB(const uint8_t* data, size_t data_size, int* width, - int* height) { +uint8_t* WebPDecodeRGB(const uint8_t* WEBP_COUNTED_BY(data_size) data, + size_t data_size, int* width, int* height) { return Decode(MODE_RGB, data, data_size, width, height, NULL); } -uint8_t* WebPDecodeRGBA(const uint8_t* data, size_t data_size, int* width, - int* height) { +uint8_t* WebPDecodeRGBA(const uint8_t* WEBP_COUNTED_BY(data_size) data, + size_t data_size, int* width, int* height) { return Decode(MODE_RGBA, data, data_size, width, height, NULL); } -uint8_t* WebPDecodeARGB(const uint8_t* data, size_t data_size, int* width, - int* height) { +uint8_t* WebPDecodeARGB(const uint8_t* WEBP_COUNTED_BY(data_size) data, + size_t data_size, int* width, int* height) { return Decode(MODE_ARGB, data, data_size, width, height, NULL); } -uint8_t* WebPDecodeBGR(const uint8_t* data, size_t data_size, int* width, - int* height) { +uint8_t* WebPDecodeBGR(const uint8_t* WEBP_COUNTED_BY(data_size) data, + size_t data_size, int* width, int* height) { return Decode(MODE_BGR, data, data_size, width, height, NULL); } -uint8_t* WebPDecodeBGRA(const uint8_t* data, size_t data_size, int* width, - int* height) { +uint8_t* WebPDecodeBGRA(const uint8_t* WEBP_COUNTED_BY(data_size) data, + size_t data_size, int* width, int* height) { return Decode(MODE_BGRA, data, data_size, width, height, NULL); } -uint8_t* WebPDecodeYUV(const uint8_t* data, size_t data_size, int* width, - int* height, uint8_t** u, uint8_t** v, int* stride, - int* uv_stride) { +uint8_t* WebPDecodeYUV(const uint8_t* WEBP_COUNTED_BY(data_size) data, + size_t data_size, int* width, int* height, uint8_t** u, + uint8_t** v, int* stride, int* uv_stride) { // data, width and height are checked by Decode(). if (u == NULL || v == NULL || stride == NULL || uv_stride == NULL) { return NULL; @@ -695,7 +728,9 @@ static void DefaultFeatures(WebPBitstreamFeatures* const features) { WEBP_UNSAFE_MEMSET(features, 0, sizeof(*features)); } -static VP8StatusCode GetFeatures(const uint8_t* const data, size_t data_size, +static VP8StatusCode GetFeatures(const uint8_t* WEBP_COUNTED_BY(data_size) + const data, + size_t data_size, WebPBitstreamFeatures* const features) { if (features == NULL || data == NULL) { return VP8_STATUS_INVALID_PARAM; @@ -711,8 +746,8 @@ static VP8StatusCode GetFeatures(const uint8_t* const data, size_t data_size, //------------------------------------------------------------------------------ // WebPGetInfo() -int WebPGetInfo(const uint8_t* data, size_t data_size, int* width, - int* height) { +int WebPGetInfo(const uint8_t* WEBP_COUNTED_BY(data_size) data, + size_t data_size, int* width, int* height) { WebPBitstreamFeatures features; if (GetFeatures(data, data_size, &features) != VP8_STATUS_OK) { @@ -802,7 +837,9 @@ int WebPValidateDecoderConfig(const WebPDecoderConfig* config) { return 1; } -VP8StatusCode WebPGetFeaturesInternal(const uint8_t* data, size_t data_size, +VP8StatusCode WebPGetFeaturesInternal(const uint8_t* WEBP_COUNTED_BY(data_size) + data, + size_t data_size, WebPBitstreamFeatures* features, int version) { if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_DECODER_ABI_VERSION)) { @@ -814,8 +851,8 @@ VP8StatusCode WebPGetFeaturesInternal(const uint8_t* data, size_t data_size, return GetFeatures(data, data_size, features); } -VP8StatusCode WebPDecode(const uint8_t* data, size_t data_size, - WebPDecoderConfig* config) { +VP8StatusCode WebPDecode(const uint8_t* WEBP_COUNTED_BY(data_size) data, + size_t data_size, WebPDecoderConfig* config) { WebPDecParams params; VP8StatusCode status; diff --git a/src/dec/webpi_dec.h b/src/dec/webpi_dec.h index 506ffcc0..c307d47f 100644 --- a/src/dec/webpi_dec.h +++ b/src/dec/webpi_dec.h @@ -61,15 +61,16 @@ void WebPResetDecParams(WebPDecParams* const params); // Structure storing a description of the RIFF headers. typedef struct { - const uint8_t* data; // input buffer - size_t data_size; // input buffer size - int have_all_data; // true if all data is known to be available - size_t offset; // offset to main data chunk (VP8 or VP8L) - const uint8_t* alpha_data; // points to alpha chunk (if present) - size_t alpha_data_size; // alpha chunk size - size_t compressed_size; // VP8/VP8L compressed data size - size_t riff_size; // size of the riff payload (or 0 if absent) - int is_lossless; // true if a VP8L chunk is present + const uint8_t* WEBP_COUNTED_BY(data_size) data; // input buffer + size_t data_size; // input buffer size + int have_all_data; // true if all data is known to be available + size_t offset; // offset to main data chunk (VP8 or VP8L) + const uint8_t* WEBP_COUNTED_BY(alpha_data_size) + alpha_data; // points to alpha chunk (if present) + size_t alpha_data_size; // alpha chunk size + size_t compressed_size; // VP8/VP8L compressed data size + size_t riff_size; // size of the riff payload (or 0 if absent) + int is_lossless; // true if a VP8L chunk is present } WebPHeaderStructure; // Skips over all valid chunks prior to the first VP8/VP8L frame header. diff --git a/src/dsp/dsp.h b/src/dsp/dsp.h index f1e3365b..00b5dc95 100644 --- a/src/dsp/dsp.h +++ b/src/dsp/dsp.h @@ -18,8 +18,10 @@ #include "src/webp/config.h" #endif +#include "src/dec/common_dec.h" #include "src/dsp/cpu.h" #include "src/utils/bounds_safety.h" +#include "src/webp/decode.h" #include "src/webp/types.h" WEBP_ASSUME_UNSAFE_INDEXABLE_ABI @@ -229,9 +231,9 @@ extern VP8WHT VP8TransformWHT; // *dst is the destination block, with stride BPS. Boundary samples are // assumed accessible when needed. typedef void (*VP8PredFunc)(uint8_t* dst); -extern VP8PredFunc VP8PredLuma16[/* NUM_B_DC_MODES */]; -extern VP8PredFunc VP8PredChroma8[/* NUM_B_DC_MODES */]; -extern VP8PredFunc VP8PredLuma4[/* NUM_BMODES */]; +extern VP8PredFunc VP8PredLuma16[NUM_B_DC_MODES]; +extern VP8PredFunc VP8PredChroma8[NUM_B_DC_MODES]; +extern VP8PredFunc VP8PredLuma4[NUM_BMODES]; // clipping tables (for filtering) extern const int8_t* const VP8ksclip1; // clips [-1020, 1020] to [-128, 127] @@ -294,7 +296,7 @@ typedef void (*WebPUpsampleLinePairFunc)( #ifdef FANCY_UPSAMPLING // Fancy upsampling functions to convert YUV to RGB(A) modes -extern WebPUpsampleLinePairFunc WebPUpsamplers[/* MODE_LAST */]; +extern WebPUpsampleLinePairFunc WebPUpsamplers[MODE_LAST]; #endif // FANCY_UPSAMPLING @@ -311,7 +313,7 @@ void WebPSamplerProcessPlane(const uint8_t* WEBP_RESTRICT y, int y_stride, int width, int height, WebPSamplerRowFunc func); // Sampling functions to convert rows of YUV to RGB(A) -extern WebPSamplerRowFunc WebPSamplers[/* MODE_LAST */]; +extern WebPSamplerRowFunc WebPSamplers[MODE_LAST]; // General function for converting two lines of ARGB or RGBA. // 'alpha_is_last' should be true if 0xff000000 is stored in memory as @@ -324,7 +326,7 @@ typedef void (*WebPYUV444Converter)(const uint8_t* WEBP_RESTRICT y, const uint8_t* WEBP_RESTRICT v, uint8_t* WEBP_RESTRICT dst, int len); -extern WebPYUV444Converter WebPYUV444Converters[/* MODE_LAST */]; +extern WebPYUV444Converter WebPYUV444Converters[MODE_LAST]; // Must be called before using the WebPUpsamplers[] (and for premultiplied // colorspaces like rgbA, rgbA4444, etc) diff --git a/src/webp/decode.h b/src/webp/decode.h index 8a57fb26..e0eaeda3 100644 --- a/src/webp/decode.h +++ b/src/webp/decode.h @@ -52,39 +52,39 @@ WEBP_EXTERN int WebPGetDecoderVersion(void); // RIFF + VP8X + (optional chunks) + VP8(L) // ALPH + VP8 <-- Not a valid WebP format: only allowed for internal purpose. // VP8(L) <-- Not a valid WebP format: only allowed for internal purpose. -WEBP_NODISCARD WEBP_EXTERN int WebPGetInfo(const uint8_t* data, - size_t data_size, int* width, - int* height); +WEBP_NODISCARD WEBP_EXTERN int WebPGetInfo( + const uint8_t* WEBP_COUNTED_BY(data_size) data, size_t data_size, + int* width, int* height); // Decodes WebP images pointed to by 'data' and returns RGBA samples, along // with the dimensions in *width and *height. The ordering of samples in // memory is R, G, B, A, R, G, B, A... in scan order (endian-independent). // The returned pointer should be deleted calling WebPFree(). // Returns NULL in case of error. -WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeRGBA(const uint8_t* data, - size_t data_size, int* width, - int* height); +WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeRGBA( + const uint8_t* WEBP_COUNTED_BY(data_size) data, size_t data_size, + int* width, int* height); // Same as WebPDecodeRGBA, but returning A, R, G, B, A, R, G, B... ordered data. -WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeARGB(const uint8_t* data, - size_t data_size, int* width, - int* height); +WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeARGB( + const uint8_t* WEBP_COUNTED_BY(data_size) data, size_t data_size, + int* width, int* height); // Same as WebPDecodeRGBA, but returning B, G, R, A, B, G, R, A... ordered data. -WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeBGRA(const uint8_t* data, - size_t data_size, int* width, - int* height); +WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeBGRA( + const uint8_t* WEBP_COUNTED_BY(data_size) data, size_t data_size, + int* width, int* height); // Same as WebPDecodeRGBA, but returning R, G, B, R, G, B... ordered data. // If the bitstream contains transparency, it is ignored. -WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeRGB(const uint8_t* data, - size_t data_size, int* width, - int* height); +WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeRGB( + const uint8_t* WEBP_COUNTED_BY(data_size) data, size_t data_size, + int* width, int* height); // Same as WebPDecodeRGB, but returning B, G, R, B, G, R... ordered data. -WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeBGR(const uint8_t* data, - size_t data_size, int* width, - int* height); +WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeBGR( + const uint8_t* WEBP_COUNTED_BY(data_size) data, size_t data_size, + int* width, int* height); // Decode WebP images pointed to by 'data' to Y'UV format(*). The pointer // returned is the Y samples buffer. Upon return, *u and *v will point to @@ -96,11 +96,10 @@ WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeBGR(const uint8_t* data, // 'width' and 'height' may be NULL, the other pointers must not be. // Returns NULL in case of error. // (*) Also named Y'CbCr. See: https://en.wikipedia.org/wiki/YCbCr -WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeYUV(const uint8_t* data, - size_t data_size, int* width, - int* height, uint8_t** u, - uint8_t** v, int* stride, - int* uv_stride); +WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeYUV( + const uint8_t* WEBP_COUNTED_BY(data_size) data, size_t data_size, + int* width, int* height, uint8_t** u, uint8_t** v, int* stride, + int* uv_stride); // These five functions are variants of the above ones, that decode the image // directly into a pre-allocated buffer 'output_buffer'. The maximum storage @@ -111,27 +110,28 @@ WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeYUV(const uint8_t* data, // between scanlines. Hence, output_buffer_size is expected to be at least // output_stride x picture-height. WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeRGBAInto( - const uint8_t* data, size_t data_size, uint8_t* output_buffer, + const uint8_t* WEBP_COUNTED_BY(data_size) data, size_t data_size, + uint8_t* WEBP_COUNTED_BY(output_buffer_size) output_buffer, size_t output_buffer_size, int output_stride); WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeARGBInto( - const uint8_t* data, size_t data_size, uint8_t* output_buffer, + const uint8_t* WEBP_COUNTED_BY(data_size) data, size_t data_size, + uint8_t* WEBP_COUNTED_BY(output_buffer_size) output_buffer, size_t output_buffer_size, int output_stride); WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeBGRAInto( - const uint8_t* data, size_t data_size, uint8_t* output_buffer, + const uint8_t* WEBP_COUNTED_BY(data_size) data, size_t data_size, + uint8_t* WEBP_COUNTED_BY(output_buffer_size) output_buffer, size_t output_buffer_size, int output_stride); // RGB and BGR variants. Here too the transparency information, if present, // will be dropped and ignored. -WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeRGBInto(const uint8_t* data, - size_t data_size, - uint8_t* output_buffer, - size_t output_buffer_size, - int output_stride); -WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeBGRInto(const uint8_t* data, - size_t data_size, - uint8_t* output_buffer, - size_t output_buffer_size, - int output_stride); +WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeRGBInto( + const uint8_t* WEBP_COUNTED_BY(data_size) data, size_t data_size, + uint8_t* WEBP_COUNTED_BY(output_buffer_size) output_buffer, + size_t output_buffer_size, int output_stride); +WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeBGRInto( + const uint8_t* WEBP_COUNTED_BY(data_size) data, size_t data_size, + uint8_t* WEBP_COUNTED_BY(output_buffer_size) output_buffer, + size_t output_buffer_size, int output_stride); // WebPDecodeYUVInto() is a variant of WebPDecodeYUV() that operates directly // into pre-allocated luma/chroma plane buffers. This function requires the @@ -141,9 +141,10 @@ WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeBGRInto(const uint8_t* data, // Pointer to the luma plane ('*luma') is returned or NULL if an error occurred // during decoding (or because some buffers were found to be too small). WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeYUVInto( - const uint8_t* data, size_t data_size, uint8_t* luma, size_t luma_size, - int luma_stride, uint8_t* u, size_t u_size, int u_stride, uint8_t* v, - size_t v_size, int v_stride); + const uint8_t* WEBP_COUNTED_BY(data_size) data, size_t data_size, + uint8_t* WEBP_COUNTED_BY(luma_size) luma, size_t luma_size, int luma_stride, + uint8_t* WEBP_COUNTED_BY(u_size) u, size_t u_size, int u_stride, + uint8_t* WEBP_COUNTED_BY(v_size) v, size_t v_size, int v_stride); //------------------------------------------------------------------------------ // Output colorspaces and buffer @@ -312,10 +313,10 @@ WEBP_NODISCARD WEBP_EXTERN WebPIDecoder* WebPINewDecoder( // colorspace 'csp' is taken into account for allocating this buffer. All other // parameters are ignored. // Returns NULL if the allocation failed, or if some parameters are invalid. -WEBP_NODISCARD WEBP_EXTERN WebPIDecoder* WebPINewRGB(WEBP_CSP_MODE csp, - uint8_t* output_buffer, - size_t output_buffer_size, - int output_stride); +WEBP_NODISCARD WEBP_EXTERN WebPIDecoder* WebPINewRGB( + WEBP_CSP_MODE csp, + uint8_t* WEBP_COUNTED_BY(output_buffer_size) output_buffer, + size_t output_buffer_size, int output_stride); // This function allocates and initializes an incremental-decoder object, which // will output the raw luma/chroma samples into a preallocated planes if @@ -329,15 +330,17 @@ WEBP_NODISCARD WEBP_EXTERN WebPIDecoder* WebPINewRGB(WEBP_CSP_MODE csp, // MODE_YUVA) when decoding starts. All parameters are then ignored. // Returns NULL if the allocation failed or if a parameter is invalid. WEBP_NODISCARD WEBP_EXTERN WebPIDecoder* WebPINewYUVA( - uint8_t* luma, size_t luma_size, int luma_stride, uint8_t* u, size_t u_size, - int u_stride, uint8_t* v, size_t v_size, int v_stride, uint8_t* a, - size_t a_size, int a_stride); + uint8_t* WEBP_COUNTED_BY(luma_size) luma, size_t luma_size, int luma_stride, + uint8_t* WEBP_COUNTED_BY(u_size) u, size_t u_size, int u_stride, + uint8_t* WEBP_COUNTED_BY(v_size) v, size_t v_size, int v_stride, + uint8_t* WEBP_COUNTED_BY(a_size) a, size_t a_size, int a_stride); // Deprecated version of the above, without the alpha plane. // Kept for backward compatibility. WEBP_NODISCARD WEBP_EXTERN WebPIDecoder* WebPINewYUV( - uint8_t* luma, size_t luma_size, int luma_stride, uint8_t* u, size_t u_size, - int u_stride, uint8_t* v, size_t v_size, int v_stride); + uint8_t* WEBP_COUNTED_BY(luma_size) luma, size_t luma_size, int luma_stride, + uint8_t* WEBP_COUNTED_BY(u_size) u, size_t u_size, int u_stride, + uint8_t* WEBP_COUNTED_BY(v_size) v, size_t v_size, int v_stride); // Deletes the WebPIDecoder object and associated memory. Must always be called // if WebPINewDecoder, WebPINewRGB or WebPINewYUV succeeded. @@ -346,7 +349,9 @@ WEBP_EXTERN void WebPIDelete(WebPIDecoder* idec); // Copies and decodes the next available data. Returns VP8_STATUS_OK when // the image is successfully decoded. Returns VP8_STATUS_SUSPENDED when more // data is expected. Returns error in other cases. -WEBP_EXTERN VP8StatusCode WebPIAppend(WebPIDecoder* idec, const uint8_t* data, +WEBP_EXTERN VP8StatusCode WebPIAppend(WebPIDecoder* idec, + const uint8_t* WEBP_COUNTED_BY(data_size) + data, size_t data_size); // A variant of the above function to be used when data buffer contains @@ -354,7 +359,9 @@ WEBP_EXTERN VP8StatusCode WebPIAppend(WebPIDecoder* idec, const uint8_t* data, // to the internal memory. // Note that the value of the 'data' pointer can change between calls to // WebPIUpdate, for instance when the data buffer is resized to fit larger data. -WEBP_EXTERN VP8StatusCode WebPIUpdate(WebPIDecoder* idec, const uint8_t* data, +WEBP_EXTERN VP8StatusCode WebPIUpdate(WebPIDecoder* idec, + const uint8_t* WEBP_COUNTED_BY(data_size) + data, size_t data_size); // Returns the RGB/A image decoded so far. Returns NULL if output params @@ -441,8 +448,9 @@ struct WebPBitstreamFeatures { }; // Internal, version-checked, entry point -WEBP_EXTERN VP8StatusCode WebPGetFeaturesInternal(const uint8_t*, size_t, - WebPBitstreamFeatures*, int); +WEBP_EXTERN VP8StatusCode +WebPGetFeaturesInternal(const uint8_t* WEBP_COUNTED_BY(data_size), + size_t data_size, WebPBitstreamFeatures*, int); // Retrieve features from the bitstream. The *features structure is filled // with information gathered from the bitstream. @@ -455,8 +463,9 @@ WEBP_EXTERN VP8StatusCode WebPGetFeaturesInternal(const uint8_t*, size_t, // RIFF + VP8X + (optional chunks) + VP8(L) // ALPH + VP8 <-- Not a valid WebP format: only allowed for internal purpose. // VP8(L) <-- Not a valid WebP format: only allowed for internal purpose. -static WEBP_INLINE VP8StatusCode WebPGetFeatures( - const uint8_t* data, size_t data_size, WebPBitstreamFeatures* features) { +static WEBP_INLINE VP8StatusCode +WebPGetFeatures(const uint8_t* WEBP_COUNTED_BY(data_size) data, + size_t data_size, WebPBitstreamFeatures* features) { return WebPGetFeaturesInternal(data, data_size, features, WEBP_DECODER_ABI_VERSION); } @@ -516,14 +525,16 @@ WEBP_NODISCARD WEBP_EXTERN int WebPValidateDecoderConfig( // The return WebPIDecoder object must always be deleted calling WebPIDelete(). // Returns NULL in case of error (and config->status will then reflect // the error condition, if available). -WEBP_NODISCARD WEBP_EXTERN WebPIDecoder* WebPIDecode(const uint8_t* data, - size_t data_size, - WebPDecoderConfig* config); +WEBP_NODISCARD WEBP_EXTERN WebPIDecoder* WebPIDecode( + const uint8_t* WEBP_COUNTED_BY(data_size) data, size_t data_size, + WebPDecoderConfig* config); // Non-incremental version. This version decodes the full data at once, taking // 'config' into account. Returns decoding status (which should be VP8_STATUS_OK // if the decoding was successful). Note that 'config' cannot be NULL. -WEBP_EXTERN VP8StatusCode WebPDecode(const uint8_t* data, size_t data_size, +WEBP_EXTERN VP8StatusCode WebPDecode(const uint8_t* WEBP_COUNTED_BY(data_size) + data, + size_t data_size, WebPDecoderConfig* config); #ifdef __cplusplus