| File: | root/firefox-clang/media/libwebp/src/dec/idec_dec.c |
| Warning: | line 196, column 44 Subtraction of a null pointer (from variable 'old_start') and a non-null pointer (from variable 'old_base') results in undefined behavior |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | // Copyright 2011 Google Inc. All Rights Reserved. | |||
| 2 | // | |||
| 3 | // Use of this source code is governed by a BSD-style license | |||
| 4 | // that can be found in the COPYING file in the root of the source | |||
| 5 | // tree. An additional intellectual property rights grant can be found | |||
| 6 | // in the file PATENTS. All contributing project authors may | |||
| 7 | // be found in the AUTHORS file in the root of the source tree. | |||
| 8 | // ----------------------------------------------------------------------------- | |||
| 9 | // | |||
| 10 | // Incremental decoding | |||
| 11 | // | |||
| 12 | // Author: somnath@google.com (Somnath Banerjee) | |||
| 13 | ||||
| 14 | #include <assert.h> | |||
| 15 | #include <stdlib.h> | |||
| 16 | #include <string.h> | |||
| 17 | ||||
| 18 | #include "src/dec/alphai_dec.h" | |||
| 19 | #include "src/dec/vp8_dec.h" | |||
| 20 | #include "src/dec/vp8i_dec.h" | |||
| 21 | #include "src/dec/vp8li_dec.h" | |||
| 22 | #include "src/dec/webpi_dec.h" | |||
| 23 | #include "src/utils/bit_reader_utils.h" | |||
| 24 | #include "src/utils/thread_utils.h" | |||
| 25 | #include "src/utils/utils.h" | |||
| 26 | #include "src/webp/decode.h" | |||
| 27 | #include "src/webp/format_constants.h" | |||
| 28 | #include "src/webp/types.h" | |||
| 29 | ||||
| 30 | // In append mode, buffer allocations increase as multiples of this value. | |||
| 31 | // Needs to be a power of 2. | |||
| 32 | #define CHUNK_SIZE4096 4096 | |||
| 33 | #define MAX_MB_SIZE4096 4096 | |||
| 34 | ||||
| 35 | //------------------------------------------------------------------------------ | |||
| 36 | // Data structures for memory and states | |||
| 37 | ||||
| 38 | // Decoding states. State normally flows as: | |||
| 39 | // WEBP_HEADER->VP8_HEADER->VP8_PARTS0->VP8_DATA->DONE for a lossy image, and | |||
| 40 | // WEBP_HEADER->VP8L_HEADER->VP8L_DATA->DONE for a lossless image. | |||
| 41 | // If there is any error the decoder goes into state ERROR. | |||
| 42 | typedef enum { | |||
| 43 | STATE_WEBP_HEADER, // All the data before that of the VP8/VP8L chunk. | |||
| 44 | STATE_VP8_HEADER, // The VP8 Frame header (within the VP8 chunk). | |||
| 45 | STATE_VP8_PARTS0, | |||
| 46 | STATE_VP8_DATA, | |||
| 47 | STATE_VP8L_HEADER, | |||
| 48 | STATE_VP8L_DATA, | |||
| 49 | STATE_DONE, | |||
| 50 | STATE_ERROR | |||
| 51 | } DecState; | |||
| 52 | ||||
| 53 | // Operating state for the MemBuffer | |||
| 54 | typedef enum { | |||
| 55 | MEM_MODE_NONE = 0, | |||
| 56 | MEM_MODE_APPEND, | |||
| 57 | MEM_MODE_MAP | |||
| 58 | } MemBufferMode; | |||
| 59 | ||||
| 60 | // storage for partition #0 and partial data (in a rolling fashion) | |||
| 61 | typedef struct { | |||
| 62 | MemBufferMode mode; // Operation mode | |||
| 63 | size_t start; // start location of the data to be decoded | |||
| 64 | size_t end; // end location | |||
| 65 | size_t buf_size; // size of the allocated buffer | |||
| 66 | uint8_t* buf; // We don't own this buffer in case WebPIUpdate() | |||
| 67 | ||||
| 68 | size_t part0_size; // size of partition #0 | |||
| 69 | const uint8_t* part0_buf; // buffer to store partition #0 | |||
| 70 | } MemBuffer; | |||
| 71 | ||||
| 72 | struct WebPIDecoder { | |||
| 73 | DecState state; // current decoding state | |||
| 74 | WebPDecParams params; // Params to store output info | |||
| 75 | int is_lossless; // for down-casting 'dec'. | |||
| 76 | void* dec; // either a VP8Decoder or a VP8LDecoder instance | |||
| 77 | VP8Io io; | |||
| 78 | ||||
| 79 | MemBuffer mem; // input memory buffer. | |||
| 80 | WebPDecBuffer output; // output buffer (when no external one is supplied, | |||
| 81 | // or if the external one has slow-memory) | |||
| 82 | WebPDecBuffer* final_output; // Slow-memory output to copy to eventually. | |||
| 83 | size_t chunk_size; // Compressed VP8/VP8L size extracted from Header. | |||
| 84 | ||||
| 85 | int last_mb_y; // last row reached for intra-mode decoding | |||
| 86 | }; | |||
| 87 | ||||
| 88 | // MB context to restore in case VP8DecodeMB() fails | |||
| 89 | typedef struct { | |||
| 90 | VP8MB left; | |||
| 91 | VP8MB info; | |||
| 92 | VP8BitReader token_br; | |||
| 93 | } MBContext; | |||
| 94 | ||||
| 95 | //------------------------------------------------------------------------------ | |||
| 96 | // MemBuffer: incoming data handling | |||
| 97 | ||||
| 98 | static WEBP_INLINEinline size_t MemDataSize(const MemBuffer* mem) { | |||
| 99 | return (mem->end - mem->start); | |||
| 100 | } | |||
| 101 | ||||
| 102 | // Check if we need to preserve the compressed alpha data, as it may not have | |||
| 103 | // been decoded yet. | |||
| 104 | static int NeedCompressedAlpha(const WebPIDecoder* const idec) { | |||
| 105 | if (idec->state == STATE_WEBP_HEADER) { | |||
| 106 | // We haven't parsed the headers yet, so we don't know whether the image is | |||
| 107 | // lossy or lossless. This also means that we haven't parsed the ALPH chunk. | |||
| 108 | return 0; | |||
| 109 | } | |||
| 110 | if (idec->is_lossless) { | |||
| 111 | return 0; // ALPH chunk is not present for lossless images. | |||
| 112 | } else { | |||
| 113 | const VP8Decoder* const dec = (VP8Decoder*)idec->dec; | |||
| 114 | assert(dec != NULL)((void) sizeof ((dec != ((void*)0)) ? 1 : 0), __extension__ ( { if (dec != ((void*)0)) ; else __assert_fail ("dec != NULL", "/root/firefox-clang/media/libwebp/src/dec/idec_dec.c", 114, __extension__ __PRETTY_FUNCTION__); })); // Must be true as idec->state != STATE_WEBP_HEADER. | |||
| 115 | return (dec->alpha_data != NULL((void*)0)) && !dec->is_alpha_decoded; | |||
| 116 | } | |||
| 117 | } | |||
| 118 | ||||
| 119 | static void DoRemap(WebPIDecoder* const idec, ptrdiff_t offset) { | |||
| 120 | MemBuffer* const mem = &idec->mem; | |||
| 121 | const uint8_t* const new_base = mem->buf + mem->start; | |||
| 122 | // note: for VP8, setting up idec->io is only really needed at the beginning | |||
| 123 | // of the decoding, till partition #0 is complete. | |||
| 124 | idec->io.data = new_base; | |||
| 125 | idec->io.data_size = MemDataSize(mem); | |||
| 126 | ||||
| 127 | if (idec->dec != NULL((void*)0)) { | |||
| 128 | if (!idec->is_lossless) { | |||
| 129 | VP8Decoder* const dec = (VP8Decoder*)idec->dec; | |||
| 130 | const uint32_t last_part = dec->num_parts_minus_one; | |||
| 131 | if (offset != 0) { | |||
| 132 | uint32_t p; | |||
| 133 | for (p = 0; p <= last_part; ++p) { | |||
| 134 | VP8RemapBitReader(dec->parts + p, offset); | |||
| 135 | } | |||
| 136 | // Remap partition #0 data pointer to new offset, but only in MAP | |||
| 137 | // mode (in APPEND mode, partition #0 is copied into a fixed memory). | |||
| 138 | if (mem->mode == MEM_MODE_MAP) { | |||
| 139 | VP8RemapBitReader(&dec->br, offset); | |||
| 140 | } | |||
| 141 | } | |||
| 142 | { | |||
| 143 | const uint8_t* const last_start = dec->parts[last_part].buf; | |||
| 144 | // 'last_start' will be NULL when 'idec->state' is < STATE_VP8_PARTS0 | |||
| 145 | // and through a portion of that state (when there isn't enough data to | |||
| 146 | // parse the partitions). The bitreader is only used meaningfully when | |||
| 147 | // there is enough data to begin parsing partition 0. | |||
| 148 | if (last_start != NULL((void*)0)) { | |||
| 149 | VP8BitReaderSetBuffer(&dec->parts[last_part], last_start, | |||
| 150 | mem->buf + mem->end - last_start); | |||
| 151 | } | |||
| 152 | } | |||
| 153 | if (NeedCompressedAlpha(idec)) { | |||
| 154 | ALPHDecoder* const alph_dec = dec->alph_dec; | |||
| 155 | dec->alpha_data += offset; | |||
| 156 | if (alph_dec != NULL((void*)0) && alph_dec->vp8l_dec != NULL((void*)0)) { | |||
| 157 | if (alph_dec->method == ALPHA_LOSSLESS_COMPRESSION1) { | |||
| 158 | VP8LDecoder* const alph_vp8l_dec = alph_dec->vp8l_dec; | |||
| 159 | assert(dec->alpha_data_size >= ALPHA_HEADER_LEN)((void) sizeof ((dec->alpha_data_size >= 1) ? 1 : 0), __extension__ ({ if (dec->alpha_data_size >= 1) ; else __assert_fail ("dec->alpha_data_size >= ALPHA_HEADER_LEN", "/root/firefox-clang/media/libwebp/src/dec/idec_dec.c" , 159, __extension__ __PRETTY_FUNCTION__); })); | |||
| 160 | VP8LBitReaderSetBuffer(&alph_vp8l_dec->br, | |||
| 161 | dec->alpha_data + ALPHA_HEADER_LEN1, | |||
| 162 | dec->alpha_data_size - ALPHA_HEADER_LEN1); | |||
| 163 | } else { // alph_dec->method == ALPHA_NO_COMPRESSION | |||
| 164 | // Nothing special to do in this case. | |||
| 165 | } | |||
| 166 | } | |||
| 167 | } | |||
| 168 | } else { // Resize lossless bitreader | |||
| 169 | VP8LDecoder* const dec = (VP8LDecoder*)idec->dec; | |||
| 170 | VP8LBitReaderSetBuffer(&dec->br, new_base, MemDataSize(mem)); | |||
| 171 | } | |||
| 172 | } | |||
| 173 | } | |||
| 174 | ||||
| 175 | // Appends data to the end of MemBuffer->buf. It expands the allocated memory | |||
| 176 | // size if required and also updates VP8BitReader's if new memory is allocated. | |||
| 177 | WEBP_NODISCARD static int AppendToMemBuffer(WebPIDecoder* const idec, | |||
| 178 | const uint8_t* const data, | |||
| 179 | size_t data_size) { | |||
| 180 | VP8Decoder* const dec = (VP8Decoder*)idec->dec; | |||
| 181 | MemBuffer* const mem = &idec->mem; | |||
| 182 | const int need_compressed_alpha = NeedCompressedAlpha(idec); | |||
| 183 | const uint8_t* const old_start = | |||
| 184 | (mem->buf == NULL((void*)0)) ? NULL((void*)0) : mem->buf + mem->start; | |||
| 185 | const uint8_t* const old_base = | |||
| 186 | need_compressed_alpha
| |||
| 187 | assert(mem->buf != NULL || mem->start == 0)((void) sizeof ((mem->buf != ((void*)0) || mem->start == 0) ? 1 : 0), __extension__ ({ if (mem->buf != ((void*)0) || mem->start == 0) ; else __assert_fail ("mem->buf != NULL || mem->start == 0" , "/root/firefox-clang/media/libwebp/src/dec/idec_dec.c", 187 , __extension__ __PRETTY_FUNCTION__); })); | |||
| 188 | assert(mem->mode == MEM_MODE_APPEND)((void) sizeof ((mem->mode == MEM_MODE_APPEND) ? 1 : 0), __extension__ ({ if (mem->mode == MEM_MODE_APPEND) ; else __assert_fail ("mem->mode == MEM_MODE_APPEND", "/root/firefox-clang/media/libwebp/src/dec/idec_dec.c" , 188, __extension__ __PRETTY_FUNCTION__); })); | |||
| 189 | if (data_size > MAX_CHUNK_PAYLOAD(~0U - 8 - 1)) { | |||
| 190 | // security safeguard: trying to allocate more than what the format | |||
| 191 | // allows for a chunk should be considered a smoke smell. | |||
| 192 | return 0; | |||
| 193 | } | |||
| 194 | ||||
| 195 | if (mem->end + data_size > mem->buf_size) { // Need some free memory | |||
| 196 | const size_t new_mem_start = old_start - old_base; | |||
| ||||
| 197 | const size_t current_size = MemDataSize(mem) + new_mem_start; | |||
| 198 | const uint64_t new_size = (uint64_t)current_size + data_size; | |||
| 199 | const uint64_t extra_size = (new_size + CHUNK_SIZE4096 - 1) & ~(CHUNK_SIZE4096 - 1); | |||
| 200 | uint8_t* const new_buf = | |||
| 201 | (uint8_t*)WebPSafeMalloc(extra_size, sizeof(*new_buf)); | |||
| 202 | if (new_buf == NULL((void*)0)) return 0; | |||
| 203 | if (old_base != NULL((void*)0)) memcpy(new_buf, old_base, current_size); | |||
| 204 | WebPSafeFree(mem->buf); | |||
| 205 | mem->buf = new_buf; | |||
| 206 | mem->buf_size = (size_t)extra_size; | |||
| 207 | mem->start = new_mem_start; | |||
| 208 | mem->end = current_size; | |||
| 209 | } | |||
| 210 | ||||
| 211 | assert(mem->buf != NULL)((void) sizeof ((mem->buf != ((void*)0)) ? 1 : 0), __extension__ ({ if (mem->buf != ((void*)0)) ; else __assert_fail ("mem->buf != NULL" , "/root/firefox-clang/media/libwebp/src/dec/idec_dec.c", 211 , __extension__ __PRETTY_FUNCTION__); })); | |||
| 212 | memcpy(mem->buf + mem->end, data, data_size); | |||
| 213 | mem->end += data_size; | |||
| 214 | assert(mem->end <= mem->buf_size)((void) sizeof ((mem->end <= mem->buf_size) ? 1 : 0) , __extension__ ({ if (mem->end <= mem->buf_size) ; else __assert_fail ("mem->end <= mem->buf_size", "/root/firefox-clang/media/libwebp/src/dec/idec_dec.c" , 214, __extension__ __PRETTY_FUNCTION__); })); | |||
| 215 | ||||
| 216 | DoRemap(idec, mem->buf + mem->start - old_start); | |||
| 217 | return 1; | |||
| 218 | } | |||
| 219 | ||||
| 220 | WEBP_NODISCARD static int RemapMemBuffer(WebPIDecoder* const idec, | |||
| 221 | const uint8_t* const data, | |||
| 222 | size_t data_size) { | |||
| 223 | MemBuffer* const mem = &idec->mem; | |||
| 224 | const uint8_t* const old_buf = mem->buf; | |||
| 225 | const uint8_t* const old_start = | |||
| 226 | (old_buf == NULL((void*)0)) ? NULL((void*)0) : old_buf + mem->start; | |||
| 227 | assert(old_buf != NULL || mem->start == 0)((void) sizeof ((old_buf != ((void*)0) || mem->start == 0) ? 1 : 0), __extension__ ({ if (old_buf != ((void*)0) || mem-> start == 0) ; else __assert_fail ("old_buf != NULL || mem->start == 0" , "/root/firefox-clang/media/libwebp/src/dec/idec_dec.c", 227 , __extension__ __PRETTY_FUNCTION__); })); | |||
| 228 | assert(mem->mode == MEM_MODE_MAP)((void) sizeof ((mem->mode == MEM_MODE_MAP) ? 1 : 0), __extension__ ({ if (mem->mode == MEM_MODE_MAP) ; else __assert_fail ("mem->mode == MEM_MODE_MAP" , "/root/firefox-clang/media/libwebp/src/dec/idec_dec.c", 228 , __extension__ __PRETTY_FUNCTION__); })); | |||
| 229 | ||||
| 230 | if (data_size < mem->buf_size) return 0; // can't remap to a shorter buffer! | |||
| 231 | ||||
| 232 | mem->buf = (uint8_t*)data; | |||
| 233 | mem->end = mem->buf_size = data_size; | |||
| 234 | ||||
| 235 | DoRemap(idec, mem->buf + mem->start - old_start); | |||
| 236 | return 1; | |||
| 237 | } | |||
| 238 | ||||
| 239 | static void InitMemBuffer(MemBuffer* const mem) { | |||
| 240 | mem->mode = MEM_MODE_NONE; | |||
| 241 | mem->buf = NULL((void*)0); | |||
| 242 | mem->buf_size = 0; | |||
| 243 | mem->part0_buf = NULL((void*)0); | |||
| 244 | mem->part0_size = 0; | |||
| 245 | } | |||
| 246 | ||||
| 247 | static void ClearMemBuffer(MemBuffer* const mem) { | |||
| 248 | assert(mem)((void) sizeof ((mem) ? 1 : 0), __extension__ ({ if (mem) ; else __assert_fail ("mem", "/root/firefox-clang/media/libwebp/src/dec/idec_dec.c" , 248, __extension__ __PRETTY_FUNCTION__); })); | |||
| 249 | if (mem->mode == MEM_MODE_APPEND) { | |||
| 250 | WebPSafeFree(mem->buf); | |||
| 251 | WebPSafeFree((void*)mem->part0_buf); | |||
| 252 | } | |||
| 253 | } | |||
| 254 | ||||
| 255 | WEBP_NODISCARD static int CheckMemBufferMode(MemBuffer* const mem, | |||
| 256 | MemBufferMode expected) { | |||
| 257 | if (mem->mode == MEM_MODE_NONE) { | |||
| 258 | mem->mode = expected; // switch to the expected mode | |||
| 259 | } else if (mem->mode != expected) { | |||
| 260 | return 0; // we mixed the modes => error | |||
| 261 | } | |||
| 262 | assert(mem->mode == expected)((void) sizeof ((mem->mode == expected) ? 1 : 0), __extension__ ({ if (mem->mode == expected) ; else __assert_fail ("mem->mode == expected" , "/root/firefox-clang/media/libwebp/src/dec/idec_dec.c", 262 , __extension__ __PRETTY_FUNCTION__); })); // mode is ok | |||
| 263 | return 1; | |||
| 264 | } | |||
| 265 | ||||
| 266 | // To be called last. | |||
| 267 | WEBP_NODISCARD static VP8StatusCode FinishDecoding(WebPIDecoder* const idec) { | |||
| 268 | const WebPDecoderOptions* const options = idec->params.options; | |||
| 269 | WebPDecBuffer* const output = idec->params.output; | |||
| 270 | ||||
| 271 | idec->state = STATE_DONE; | |||
| 272 | if (options != NULL((void*)0) && options->flip) { | |||
| 273 | const VP8StatusCode status = WebPFlipBuffer(output); | |||
| 274 | if (status != VP8_STATUS_OK) return status; | |||
| 275 | } | |||
| 276 | if (idec->final_output != NULL((void*)0)) { | |||
| 277 | const VP8StatusCode status = WebPCopyDecBufferPixels( | |||
| 278 | output, idec->final_output); // do the slow-copy | |||
| 279 | WebPFreeDecBuffer(&idec->output); | |||
| 280 | if (status != VP8_STATUS_OK) return status; | |||
| 281 | *output = *idec->final_output; | |||
| 282 | idec->final_output = NULL((void*)0); | |||
| 283 | } | |||
| 284 | return VP8_STATUS_OK; | |||
| 285 | } | |||
| 286 | ||||
| 287 | //------------------------------------------------------------------------------ | |||
| 288 | // Macroblock-decoding contexts | |||
| 289 | ||||
| 290 | static void SaveContext(const VP8Decoder* dec, const VP8BitReader* token_br, | |||
| 291 | MBContext* const context) { | |||
| 292 | context->left = dec->mb_info[-1]; | |||
| 293 | context->info = dec->mb_info[dec->mb_x]; | |||
| 294 | context->token_br = *token_br; | |||
| 295 | } | |||
| 296 | ||||
| 297 | static void RestoreContext(const MBContext* context, VP8Decoder* const dec, | |||
| 298 | VP8BitReader* const token_br) { | |||
| 299 | dec->mb_info[-1] = context->left; | |||
| 300 | dec->mb_info[dec->mb_x] = context->info; | |||
| 301 | *token_br = context->token_br; | |||
| 302 | } | |||
| 303 | ||||
| 304 | //------------------------------------------------------------------------------ | |||
| 305 | ||||
| 306 | static VP8StatusCode IDecError(WebPIDecoder* const idec, VP8StatusCode error) { | |||
| 307 | if (idec->state == STATE_VP8_DATA) { | |||
| 308 | // Synchronize the thread, clean-up and check for errors. | |||
| 309 | (void)VP8ExitCritical((VP8Decoder*)idec->dec, &idec->io); | |||
| 310 | } | |||
| 311 | idec->state = STATE_ERROR; | |||
| 312 | return error; | |||
| 313 | } | |||
| 314 | ||||
| 315 | static void ChangeState(WebPIDecoder* const idec, DecState new_state, | |||
| 316 | size_t consumed_bytes) { | |||
| 317 | MemBuffer* const mem = &idec->mem; | |||
| 318 | idec->state = new_state; | |||
| 319 | mem->start += consumed_bytes; | |||
| 320 | assert(mem->start <= mem->end)((void) sizeof ((mem->start <= mem->end) ? 1 : 0), __extension__ ({ if (mem->start <= mem->end) ; else __assert_fail ("mem->start <= mem->end", "/root/firefox-clang/media/libwebp/src/dec/idec_dec.c" , 320, __extension__ __PRETTY_FUNCTION__); })); | |||
| 321 | idec->io.data = mem->buf + mem->start; | |||
| 322 | idec->io.data_size = MemDataSize(mem); | |||
| 323 | } | |||
| 324 | ||||
| 325 | // Headers | |||
| 326 | static VP8StatusCode DecodeWebPHeaders(WebPIDecoder* const idec) { | |||
| 327 | MemBuffer* const mem = &idec->mem; | |||
| 328 | const uint8_t* data = mem->buf + mem->start; | |||
| 329 | size_t curr_size = MemDataSize(mem); | |||
| 330 | VP8StatusCode status; | |||
| 331 | WebPHeaderStructure headers; | |||
| 332 | ||||
| 333 | headers.data = data; | |||
| 334 | headers.data_size = curr_size; | |||
| 335 | headers.have_all_data = 0; | |||
| 336 | status = WebPParseHeaders(&headers); | |||
| 337 | if (status == VP8_STATUS_NOT_ENOUGH_DATA) { | |||
| 338 | return VP8_STATUS_SUSPENDED; // We haven't found a VP8 chunk yet. | |||
| 339 | } else if (status != VP8_STATUS_OK) { | |||
| 340 | return IDecError(idec, status); | |||
| 341 | } | |||
| 342 | ||||
| 343 | idec->chunk_size = headers.compressed_size; | |||
| 344 | idec->is_lossless = headers.is_lossless; | |||
| 345 | if (!idec->is_lossless) { | |||
| 346 | VP8Decoder* const dec = VP8New(); | |||
| 347 | if (dec == NULL((void*)0)) { | |||
| 348 | return VP8_STATUS_OUT_OF_MEMORY; | |||
| 349 | } | |||
| 350 | dec->incremental = 1; | |||
| 351 | idec->dec = dec; | |||
| 352 | dec->alpha_data = headers.alpha_data; | |||
| 353 | dec->alpha_data_size = headers.alpha_data_size; | |||
| 354 | ChangeState(idec, STATE_VP8_HEADER, headers.offset); | |||
| 355 | } else { | |||
| 356 | VP8LDecoder* const dec = VP8LNew(); | |||
| 357 | if (dec == NULL((void*)0)) { | |||
| 358 | return VP8_STATUS_OUT_OF_MEMORY; | |||
| 359 | } | |||
| 360 | idec->dec = dec; | |||
| 361 | ChangeState(idec, STATE_VP8L_HEADER, headers.offset); | |||
| 362 | } | |||
| 363 | return VP8_STATUS_OK; | |||
| 364 | } | |||
| 365 | ||||
| 366 | static VP8StatusCode DecodeVP8FrameHeader(WebPIDecoder* const idec) { | |||
| 367 | const uint8_t* data = idec->mem.buf + idec->mem.start; | |||
| 368 | const size_t curr_size = MemDataSize(&idec->mem); | |||
| 369 | int width, height; | |||
| 370 | uint32_t bits; | |||
| 371 | ||||
| 372 | if (curr_size < VP8_FRAME_HEADER_SIZE10) { | |||
| 373 | // Not enough data bytes to extract VP8 Frame Header. | |||
| 374 | return VP8_STATUS_SUSPENDED; | |||
| 375 | } | |||
| 376 | if (!VP8GetInfo(data, curr_size, idec->chunk_size, &width, &height)) { | |||
| 377 | return IDecError(idec, VP8_STATUS_BITSTREAM_ERROR); | |||
| 378 | } | |||
| 379 | ||||
| 380 | bits = data[0] | (data[1] << 8) | (data[2] << 16); | |||
| 381 | idec->mem.part0_size = (bits >> 5) + VP8_FRAME_HEADER_SIZE10; | |||
| 382 | ||||
| 383 | idec->io.data = data; | |||
| 384 | idec->io.data_size = curr_size; | |||
| 385 | idec->state = STATE_VP8_PARTS0; | |||
| 386 | return VP8_STATUS_OK; | |||
| 387 | } | |||
| 388 | ||||
| 389 | // Partition #0 | |||
| 390 | static VP8StatusCode CopyParts0Data(WebPIDecoder* const idec) { | |||
| 391 | VP8Decoder* const dec = (VP8Decoder*)idec->dec; | |||
| 392 | VP8BitReader* const br = &dec->br; | |||
| 393 | const size_t part_size = br->buf_end - br->buf; | |||
| 394 | MemBuffer* const mem = &idec->mem; | |||
| 395 | assert(!idec->is_lossless)((void) sizeof ((!idec->is_lossless) ? 1 : 0), __extension__ ({ if (!idec->is_lossless) ; else __assert_fail ("!idec->is_lossless" , "/root/firefox-clang/media/libwebp/src/dec/idec_dec.c", 395 , __extension__ __PRETTY_FUNCTION__); })); | |||
| 396 | assert(mem->part0_buf == NULL)((void) sizeof ((mem->part0_buf == ((void*)0)) ? 1 : 0), __extension__ ({ if (mem->part0_buf == ((void*)0)) ; else __assert_fail ("mem->part0_buf == NULL", "/root/firefox-clang/media/libwebp/src/dec/idec_dec.c" , 396, __extension__ __PRETTY_FUNCTION__); })); | |||
| 397 | // the following is a format limitation, no need for runtime check: | |||
| 398 | assert(part_size <= mem->part0_size)((void) sizeof ((part_size <= mem->part0_size) ? 1 : 0) , __extension__ ({ if (part_size <= mem->part0_size) ; else __assert_fail ("part_size <= mem->part0_size", "/root/firefox-clang/media/libwebp/src/dec/idec_dec.c" , 398, __extension__ __PRETTY_FUNCTION__); })); | |||
| 399 | if (part_size == 0) { // can't have zero-size partition #0 | |||
| 400 | return VP8_STATUS_BITSTREAM_ERROR; | |||
| 401 | } | |||
| 402 | if (mem->mode == MEM_MODE_APPEND) { | |||
| 403 | // We copy and grab ownership of the partition #0 data. | |||
| 404 | uint8_t* const part0_buf = (uint8_t*)WebPSafeMalloc(1ULL, part_size); | |||
| 405 | if (part0_buf == NULL((void*)0)) { | |||
| 406 | return VP8_STATUS_OUT_OF_MEMORY; | |||
| 407 | } | |||
| 408 | memcpy(part0_buf, br->buf, part_size); | |||
| 409 | mem->part0_buf = part0_buf; | |||
| 410 | VP8BitReaderSetBuffer(br, part0_buf, part_size); | |||
| 411 | } else { | |||
| 412 | // Else: just keep pointers to the partition #0's data in dec->br. | |||
| 413 | } | |||
| 414 | mem->start += part_size; | |||
| 415 | return VP8_STATUS_OK; | |||
| 416 | } | |||
| 417 | ||||
| 418 | static VP8StatusCode DecodePartition0(WebPIDecoder* const idec) { | |||
| 419 | VP8Decoder* const dec = (VP8Decoder*)idec->dec; | |||
| 420 | VP8Io* const io = &idec->io; | |||
| 421 | const WebPDecParams* const params = &idec->params; | |||
| 422 | WebPDecBuffer* const output = params->output; | |||
| 423 | ||||
| 424 | // Wait till we have enough data for the whole partition #0 | |||
| 425 | if (MemDataSize(&idec->mem) < idec->mem.part0_size) { | |||
| 426 | return VP8_STATUS_SUSPENDED; | |||
| 427 | } | |||
| 428 | ||||
| 429 | if (!VP8GetHeaders(dec, io)) { | |||
| 430 | const VP8StatusCode status = dec->status; | |||
| 431 | if (status == VP8_STATUS_SUSPENDED || | |||
| 432 | status == VP8_STATUS_NOT_ENOUGH_DATA) { | |||
| 433 | // treating NOT_ENOUGH_DATA as SUSPENDED state | |||
| 434 | return VP8_STATUS_SUSPENDED; | |||
| 435 | } | |||
| 436 | return IDecError(idec, status); | |||
| 437 | } | |||
| 438 | ||||
| 439 | // Allocate/Verify output buffer now | |||
| 440 | dec->status = WebPAllocateDecBuffer(io->width, io->height, params->options, | |||
| 441 | output); | |||
| 442 | if (dec->status != VP8_STATUS_OK) { | |||
| 443 | return IDecError(idec, dec->status); | |||
| 444 | } | |||
| 445 | // This change must be done before calling VP8InitFrame() | |||
| 446 | dec->mt_method = VP8GetThreadMethod(params->options, NULL((void*)0), | |||
| 447 | io->width, io->height); | |||
| 448 | VP8InitDithering(params->options, dec); | |||
| 449 | ||||
| 450 | dec->status = CopyParts0Data(idec); | |||
| 451 | if (dec->status != VP8_STATUS_OK) { | |||
| 452 | return IDecError(idec, dec->status); | |||
| 453 | } | |||
| 454 | ||||
| 455 | // Finish setting up the decoding parameters. Will call io->setup(). | |||
| 456 | if (VP8EnterCritical(dec, io) != VP8_STATUS_OK) { | |||
| 457 | return IDecError(idec, dec->status); | |||
| 458 | } | |||
| 459 | ||||
| 460 | // Note: past this point, teardown() must always be called | |||
| 461 | // in case of error. | |||
| 462 | idec->state = STATE_VP8_DATA; | |||
| 463 | // Allocate memory and prepare everything. | |||
| 464 | if (!VP8InitFrame(dec, io)) { | |||
| 465 | return IDecError(idec, dec->status); | |||
| 466 | } | |||
| 467 | return VP8_STATUS_OK; | |||
| 468 | } | |||
| 469 | ||||
| 470 | // Remaining partitions | |||
| 471 | static VP8StatusCode DecodeRemaining(WebPIDecoder* const idec) { | |||
| 472 | VP8Decoder* const dec = (VP8Decoder*)idec->dec; | |||
| 473 | VP8Io* const io = &idec->io; | |||
| 474 | ||||
| 475 | // Make sure partition #0 has been read before, to set dec to ready. | |||
| 476 | if (!dec->ready) { | |||
| 477 | return IDecError(idec, VP8_STATUS_BITSTREAM_ERROR); | |||
| 478 | } | |||
| 479 | for (; dec->mb_y < dec->mb_h; ++dec->mb_y) { | |||
| 480 | if (idec->last_mb_y != dec->mb_y) { | |||
| 481 | if (!VP8ParseIntraModeRow(&dec->br, dec)) { | |||
| 482 | // note: normally, error shouldn't occur since we already have the whole | |||
| 483 | // partition0 available here in DecodeRemaining(). Reaching EOF while | |||
| 484 | // reading intra modes really means a BITSTREAM_ERROR. | |||
| 485 | return IDecError(idec, VP8_STATUS_BITSTREAM_ERROR); | |||
| 486 | } | |||
| 487 | idec->last_mb_y = dec->mb_y; | |||
| 488 | } | |||
| 489 | for (; dec->mb_x < dec->mb_w; ++dec->mb_x) { | |||
| 490 | VP8BitReader* const token_br = | |||
| 491 | &dec->parts[dec->mb_y & dec->num_parts_minus_one]; | |||
| 492 | MBContext context; | |||
| 493 | SaveContext(dec, token_br, &context); | |||
| 494 | if (!VP8DecodeMB(dec, token_br)) { | |||
| 495 | // We shouldn't fail when MAX_MB data was available | |||
| 496 | if (dec->num_parts_minus_one == 0 && | |||
| 497 | MemDataSize(&idec->mem) > MAX_MB_SIZE4096) { | |||
| 498 | return IDecError(idec, VP8_STATUS_BITSTREAM_ERROR); | |||
| 499 | } | |||
| 500 | // Synchronize the threads. | |||
| 501 | if (dec->mt_method > 0) { | |||
| 502 | if (!WebPGetWorkerInterface()->Sync(&dec->worker)) { | |||
| 503 | return IDecError(idec, VP8_STATUS_BITSTREAM_ERROR); | |||
| 504 | } | |||
| 505 | } | |||
| 506 | RestoreContext(&context, dec, token_br); | |||
| 507 | return VP8_STATUS_SUSPENDED; | |||
| 508 | } | |||
| 509 | // Release buffer only if there is only one partition | |||
| 510 | if (dec->num_parts_minus_one == 0) { | |||
| 511 | idec->mem.start = token_br->buf - idec->mem.buf; | |||
| 512 | assert(idec->mem.start <= idec->mem.end)((void) sizeof ((idec->mem.start <= idec->mem.end) ? 1 : 0), __extension__ ({ if (idec->mem.start <= idec-> mem.end) ; else __assert_fail ("idec->mem.start <= idec->mem.end" , "/root/firefox-clang/media/libwebp/src/dec/idec_dec.c", 512 , __extension__ __PRETTY_FUNCTION__); })); | |||
| 513 | } | |||
| 514 | } | |||
| 515 | VP8InitScanline(dec); // Prepare for next scanline | |||
| 516 | ||||
| 517 | // Reconstruct, filter and emit the row. | |||
| 518 | if (!VP8ProcessRow(dec, io)) { | |||
| 519 | return IDecError(idec, VP8_STATUS_USER_ABORT); | |||
| 520 | } | |||
| 521 | } | |||
| 522 | // Synchronize the thread and check for errors. | |||
| 523 | if (!VP8ExitCritical(dec, io)) { | |||
| 524 | idec->state = STATE_ERROR; // prevent re-entry in IDecError | |||
| 525 | return IDecError(idec, VP8_STATUS_USER_ABORT); | |||
| 526 | } | |||
| 527 | dec->ready = 0; | |||
| 528 | return FinishDecoding(idec); | |||
| 529 | } | |||
| 530 | ||||
| 531 | static VP8StatusCode ErrorStatusLossless(WebPIDecoder* const idec, | |||
| 532 | VP8StatusCode status) { | |||
| 533 | if (status == VP8_STATUS_SUSPENDED || status == VP8_STATUS_NOT_ENOUGH_DATA) { | |||
| 534 | return VP8_STATUS_SUSPENDED; | |||
| 535 | } | |||
| 536 | return IDecError(idec, status); | |||
| 537 | } | |||
| 538 | ||||
| 539 | static VP8StatusCode DecodeVP8LHeader(WebPIDecoder* const idec) { | |||
| 540 | VP8Io* const io = &idec->io; | |||
| 541 | VP8LDecoder* const dec = (VP8LDecoder*)idec->dec; | |||
| 542 | const WebPDecParams* const params = &idec->params; | |||
| 543 | WebPDecBuffer* const output = params->output; | |||
| 544 | size_t curr_size = MemDataSize(&idec->mem); | |||
| 545 | assert(idec->is_lossless)((void) sizeof ((idec->is_lossless) ? 1 : 0), __extension__ ({ if (idec->is_lossless) ; else __assert_fail ("idec->is_lossless" , "/root/firefox-clang/media/libwebp/src/dec/idec_dec.c", 545 , __extension__ __PRETTY_FUNCTION__); })); | |||
| 546 | ||||
| 547 | // Wait until there's enough data for decoding header. | |||
| 548 | if (curr_size < (idec->chunk_size >> 3)) { | |||
| 549 | dec->status = VP8_STATUS_SUSPENDED; | |||
| 550 | return ErrorStatusLossless(idec, dec->status); | |||
| 551 | } | |||
| 552 | ||||
| 553 | if (!VP8LDecodeHeader(dec, io)) { | |||
| 554 | if (dec->status == VP8_STATUS_BITSTREAM_ERROR && | |||
| 555 | curr_size < idec->chunk_size) { | |||
| 556 | dec->status = VP8_STATUS_SUSPENDED; | |||
| 557 | } | |||
| 558 | return ErrorStatusLossless(idec, dec->status); | |||
| 559 | } | |||
| 560 | // Allocate/verify output buffer now. | |||
| 561 | dec->status = WebPAllocateDecBuffer(io->width, io->height, params->options, | |||
| 562 | output); | |||
| 563 | if (dec->status != VP8_STATUS_OK) { | |||
| 564 | return IDecError(idec, dec->status); | |||
| 565 | } | |||
| 566 | ||||
| 567 | idec->state = STATE_VP8L_DATA; | |||
| 568 | return VP8_STATUS_OK; | |||
| 569 | } | |||
| 570 | ||||
| 571 | static VP8StatusCode DecodeVP8LData(WebPIDecoder* const idec) { | |||
| 572 | VP8LDecoder* const dec = (VP8LDecoder*)idec->dec; | |||
| 573 | const size_t curr_size = MemDataSize(&idec->mem); | |||
| 574 | assert(idec->is_lossless)((void) sizeof ((idec->is_lossless) ? 1 : 0), __extension__ ({ if (idec->is_lossless) ; else __assert_fail ("idec->is_lossless" , "/root/firefox-clang/media/libwebp/src/dec/idec_dec.c", 574 , __extension__ __PRETTY_FUNCTION__); })); | |||
| 575 | ||||
| 576 | // Switch to incremental decoding if we don't have all the bytes available. | |||
| 577 | dec->incremental = (curr_size < idec->chunk_size); | |||
| 578 | ||||
| 579 | if (!VP8LDecodeImage(dec)) { | |||
| 580 | return ErrorStatusLossless(idec, dec->status); | |||
| 581 | } | |||
| 582 | assert(dec->status == VP8_STATUS_OK || dec->status == VP8_STATUS_SUSPENDED)((void) sizeof ((dec->status == VP8_STATUS_OK || dec->status == VP8_STATUS_SUSPENDED) ? 1 : 0), __extension__ ({ if (dec-> status == VP8_STATUS_OK || dec->status == VP8_STATUS_SUSPENDED ) ; else __assert_fail ("dec->status == VP8_STATUS_OK || dec->status == VP8_STATUS_SUSPENDED" , "/root/firefox-clang/media/libwebp/src/dec/idec_dec.c", 582 , __extension__ __PRETTY_FUNCTION__); })); | |||
| 583 | return (dec->status == VP8_STATUS_SUSPENDED) ? dec->status | |||
| 584 | : FinishDecoding(idec); | |||
| 585 | } | |||
| 586 | ||||
| 587 | // Main decoding loop | |||
| 588 | static VP8StatusCode IDecode(WebPIDecoder* idec) { | |||
| 589 | VP8StatusCode status = VP8_STATUS_SUSPENDED; | |||
| 590 | ||||
| 591 | if (idec->state == STATE_WEBP_HEADER) { | |||
| 592 | status = DecodeWebPHeaders(idec); | |||
| 593 | } else { | |||
| 594 | if (idec->dec == NULL((void*)0)) { | |||
| 595 | return VP8_STATUS_SUSPENDED; // can't continue if we have no decoder. | |||
| 596 | } | |||
| 597 | } | |||
| 598 | if (idec->state == STATE_VP8_HEADER) { | |||
| 599 | status = DecodeVP8FrameHeader(idec); | |||
| 600 | } | |||
| 601 | if (idec->state == STATE_VP8_PARTS0) { | |||
| 602 | status = DecodePartition0(idec); | |||
| 603 | } | |||
| 604 | if (idec->state == STATE_VP8_DATA) { | |||
| 605 | const VP8Decoder* const dec = (VP8Decoder*)idec->dec; | |||
| 606 | if (dec == NULL((void*)0)) { | |||
| 607 | return VP8_STATUS_SUSPENDED; // can't continue if we have no decoder. | |||
| 608 | } | |||
| 609 | status = DecodeRemaining(idec); | |||
| 610 | } | |||
| 611 | if (idec->state == STATE_VP8L_HEADER) { | |||
| 612 | status = DecodeVP8LHeader(idec); | |||
| 613 | } | |||
| 614 | if (idec->state == STATE_VP8L_DATA) { | |||
| 615 | status = DecodeVP8LData(idec); | |||
| 616 | } | |||
| 617 | return status; | |||
| 618 | } | |||
| 619 | ||||
| 620 | //------------------------------------------------------------------------------ | |||
| 621 | // Internal constructor | |||
| 622 | ||||
| 623 | WEBP_NODISCARD static WebPIDecoder* NewDecoder( | |||
| 624 | WebPDecBuffer* const output_buffer, | |||
| 625 | const WebPBitstreamFeatures* const features) { | |||
| 626 | WebPIDecoder* idec = (WebPIDecoder*)WebPSafeCalloc(1ULL, sizeof(*idec)); | |||
| 627 | if (idec == NULL((void*)0)) { | |||
| 628 | return NULL((void*)0); | |||
| 629 | } | |||
| 630 | ||||
| 631 | idec->state = STATE_WEBP_HEADER; | |||
| 632 | idec->chunk_size = 0; | |||
| 633 | ||||
| 634 | idec->last_mb_y = -1; | |||
| 635 | ||||
| 636 | InitMemBuffer(&idec->mem); | |||
| 637 | if (!WebPInitDecBuffer(&idec->output) || !VP8InitIo(&idec->io)) { | |||
| 638 | WebPSafeFree(idec); | |||
| 639 | return NULL((void*)0); | |||
| 640 | } | |||
| 641 | ||||
| 642 | WebPResetDecParams(&idec->params); | |||
| 643 | if (output_buffer == NULL((void*)0) || WebPAvoidSlowMemory(output_buffer, features)) { | |||
| 644 | idec->params.output = &idec->output; | |||
| 645 | idec->final_output = output_buffer; | |||
| 646 | if (output_buffer != NULL((void*)0)) { | |||
| 647 | idec->params.output->colorspace = output_buffer->colorspace; | |||
| 648 | } | |||
| 649 | } else { | |||
| 650 | idec->params.output = output_buffer; | |||
| 651 | idec->final_output = NULL((void*)0); | |||
| 652 | } | |||
| 653 | WebPInitCustomIo(&idec->params, &idec->io); // Plug the I/O functions. | |||
| 654 | ||||
| 655 | return idec; | |||
| 656 | } | |||
| 657 | ||||
| 658 | //------------------------------------------------------------------------------ | |||
| 659 | // Public functions | |||
| 660 | ||||
| 661 | WebPIDecoder* WebPINewDecoder(WebPDecBuffer* output_buffer) { | |||
| 662 | return NewDecoder(output_buffer, NULL((void*)0)); | |||
| 663 | } | |||
| 664 | ||||
| 665 | WebPIDecoder* WebPIDecode(const uint8_t* data, size_t data_size, | |||
| 666 | WebPDecoderConfig* config) { | |||
| 667 | WebPIDecoder* idec; | |||
| 668 | WebPBitstreamFeatures tmp_features; | |||
| 669 | WebPBitstreamFeatures* const features = | |||
| 670 | (config == NULL((void*)0)) ? &tmp_features : &config->input; | |||
| 671 | memset(&tmp_features, 0, sizeof(tmp_features)); | |||
| 672 | ||||
| 673 | // Parse the bitstream's features, if requested: | |||
| 674 | if (data != NULL((void*)0) && data_size > 0) { | |||
| 675 | if (WebPGetFeatures(data, data_size, features) != VP8_STATUS_OK) { | |||
| 676 | return NULL((void*)0); | |||
| 677 | } | |||
| 678 | } | |||
| 679 | ||||
| 680 | // Create an instance of the incremental decoder | |||
| 681 | idec = (config != NULL((void*)0)) ? NewDecoder(&config->output, features) | |||
| 682 | : NewDecoder(NULL((void*)0), features); | |||
| 683 | if (idec == NULL((void*)0)) { | |||
| 684 | return NULL((void*)0); | |||
| 685 | } | |||
| 686 | // Finish initialization | |||
| 687 | if (config != NULL((void*)0)) { | |||
| 688 | idec->params.options = &config->options; | |||
| 689 | } | |||
| 690 | return idec; | |||
| 691 | } | |||
| 692 | ||||
| 693 | void WebPIDelete(WebPIDecoder* idec) { | |||
| 694 | if (idec == NULL((void*)0)) return; | |||
| 695 | if (idec->dec != NULL((void*)0)) { | |||
| 696 | if (!idec->is_lossless) { | |||
| 697 | if (idec->state == STATE_VP8_DATA) { | |||
| 698 | // Synchronize the thread, clean-up and check for errors. | |||
| 699 | // TODO(vrabaud) do we care about the return result? | |||
| 700 | (void)VP8ExitCritical((VP8Decoder*)idec->dec, &idec->io); | |||
| 701 | } | |||
| 702 | VP8Delete((VP8Decoder*)idec->dec); | |||
| 703 | } else { | |||
| 704 | VP8LDelete((VP8LDecoder*)idec->dec); | |||
| 705 | } | |||
| 706 | } | |||
| 707 | ClearMemBuffer(&idec->mem); | |||
| 708 | WebPFreeDecBuffer(&idec->output); | |||
| 709 | WebPSafeFree(idec); | |||
| 710 | } | |||
| 711 | ||||
| 712 | //------------------------------------------------------------------------------ | |||
| 713 | // Wrapper toward WebPINewDecoder | |||
| 714 | ||||
| 715 | WebPIDecoder* WebPINewRGB(WEBP_CSP_MODE csp, uint8_t* output_buffer, | |||
| 716 | size_t output_buffer_size, int output_stride) { | |||
| 717 | const int is_external_memory = (output_buffer != NULL((void*)0)) ? 1 : 0; | |||
| 718 | WebPIDecoder* idec; | |||
| 719 | ||||
| 720 | if (csp >= MODE_YUV) return NULL((void*)0); | |||
| 721 | if (is_external_memory == 0) { // Overwrite parameters to sane values. | |||
| 722 | output_buffer_size = 0; | |||
| 723 | output_stride = 0; | |||
| 724 | } else { // A buffer was passed. Validate the other params. | |||
| 725 | if (output_stride == 0 || output_buffer_size == 0) { | |||
| 726 | return NULL((void*)0); // invalid parameter. | |||
| 727 | } | |||
| 728 | } | |||
| 729 | idec = WebPINewDecoder(NULL((void*)0)); | |||
| 730 | if (idec == NULL((void*)0)) return NULL((void*)0); | |||
| 731 | idec->output.colorspace = csp; | |||
| 732 | idec->output.is_external_memory = is_external_memory; | |||
| 733 | idec->output.u.RGBA.rgba = output_buffer; | |||
| 734 | idec->output.u.RGBA.stride = output_stride; | |||
| 735 | idec->output.u.RGBA.size = output_buffer_size; | |||
| 736 | return idec; | |||
| 737 | } | |||
| 738 | ||||
| 739 | WebPIDecoder* WebPINewYUVA(uint8_t* luma, size_t luma_size, int luma_stride, | |||
| 740 | uint8_t* u, size_t u_size, int u_stride, | |||
| 741 | uint8_t* v, size_t v_size, int v_stride, | |||
| 742 | uint8_t* a, size_t a_size, int a_stride) { | |||
| 743 | const int is_external_memory = (luma != NULL((void*)0)) ? 1 : 0; | |||
| 744 | WebPIDecoder* idec; | |||
| 745 | WEBP_CSP_MODE colorspace; | |||
| 746 | ||||
| 747 | if (is_external_memory == 0) { // Overwrite parameters to sane values. | |||
| 748 | luma_size = u_size = v_size = a_size = 0; | |||
| 749 | luma_stride = u_stride = v_stride = a_stride = 0; | |||
| 750 | u = v = a = NULL((void*)0); | |||
| 751 | colorspace = MODE_YUVA; | |||
| 752 | } else { // A luma buffer was passed. Validate the other parameters. | |||
| 753 | if (u == NULL((void*)0) || v == NULL((void*)0)) return NULL((void*)0); | |||
| 754 | if (luma_size == 0 || u_size == 0 || v_size == 0) return NULL((void*)0); | |||
| 755 | if (luma_stride == 0 || u_stride == 0 || v_stride == 0) return NULL((void*)0); | |||
| 756 | if (a != NULL((void*)0)) { | |||
| 757 | if (a_size == 0 || a_stride == 0) return NULL((void*)0); | |||
| 758 | } | |||
| 759 | colorspace = (a == NULL((void*)0)) ? MODE_YUV : MODE_YUVA; | |||
| 760 | } | |||
| 761 | ||||
| 762 | idec = WebPINewDecoder(NULL((void*)0)); | |||
| 763 | if (idec == NULL((void*)0)) return NULL((void*)0); | |||
| 764 | ||||
| 765 | idec->output.colorspace = colorspace; | |||
| 766 | idec->output.is_external_memory = is_external_memory; | |||
| 767 | idec->output.u.YUVA.y = luma; | |||
| 768 | idec->output.u.YUVA.y_stride = luma_stride; | |||
| 769 | idec->output.u.YUVA.y_size = luma_size; | |||
| 770 | idec->output.u.YUVA.u = u; | |||
| 771 | idec->output.u.YUVA.u_stride = u_stride; | |||
| 772 | idec->output.u.YUVA.u_size = u_size; | |||
| 773 | idec->output.u.YUVA.v = v; | |||
| 774 | idec->output.u.YUVA.v_stride = v_stride; | |||
| 775 | idec->output.u.YUVA.v_size = v_size; | |||
| 776 | idec->output.u.YUVA.a = a; | |||
| 777 | idec->output.u.YUVA.a_stride = a_stride; | |||
| 778 | idec->output.u.YUVA.a_size = a_size; | |||
| 779 | return idec; | |||
| 780 | } | |||
| 781 | ||||
| 782 | WebPIDecoder* WebPINewYUV(uint8_t* luma, size_t luma_size, int luma_stride, | |||
| 783 | uint8_t* u, size_t u_size, int u_stride, | |||
| 784 | uint8_t* v, size_t v_size, int v_stride) { | |||
| 785 | return WebPINewYUVA(luma, luma_size, luma_stride, | |||
| 786 | u, u_size, u_stride, | |||
| 787 | v, v_size, v_stride, | |||
| 788 | NULL((void*)0), 0, 0); | |||
| 789 | } | |||
| 790 | ||||
| 791 | //------------------------------------------------------------------------------ | |||
| 792 | ||||
| 793 | static VP8StatusCode IDecCheckStatus(const WebPIDecoder* const idec) { | |||
| 794 | assert(idec)((void) sizeof ((idec) ? 1 : 0), __extension__ ({ if (idec) ; else __assert_fail ("idec", "/root/firefox-clang/media/libwebp/src/dec/idec_dec.c" , 794, __extension__ __PRETTY_FUNCTION__); })); | |||
| 795 | if (idec->state == STATE_ERROR) { | |||
| 796 | return VP8_STATUS_BITSTREAM_ERROR; | |||
| 797 | } | |||
| 798 | if (idec->state == STATE_DONE) { | |||
| 799 | return VP8_STATUS_OK; | |||
| 800 | } | |||
| 801 | return VP8_STATUS_SUSPENDED; | |||
| 802 | } | |||
| 803 | ||||
| 804 | VP8StatusCode WebPIAppend(WebPIDecoder* idec, | |||
| 805 | const uint8_t* data, size_t data_size) { | |||
| 806 | VP8StatusCode status; | |||
| 807 | if (idec == NULL((void*)0) || data == NULL((void*)0)) { | |||
| ||||
| 808 | return VP8_STATUS_INVALID_PARAM; | |||
| 809 | } | |||
| 810 | status = IDecCheckStatus(idec); | |||
| 811 | if (status
| |||
| 812 | return status; | |||
| 813 | } | |||
| 814 | // Check mixed calls between RemapMemBuffer and AppendToMemBuffer. | |||
| 815 | if (!CheckMemBufferMode(&idec->mem, MEM_MODE_APPEND)) { | |||
| 816 | return VP8_STATUS_INVALID_PARAM; | |||
| 817 | } | |||
| 818 | // Append data to memory buffer | |||
| 819 | if (!AppendToMemBuffer(idec, data, data_size)) { | |||
| 820 | return VP8_STATUS_OUT_OF_MEMORY; | |||
| 821 | } | |||
| 822 | return IDecode(idec); | |||
| 823 | } | |||
| 824 | ||||
| 825 | VP8StatusCode WebPIUpdate(WebPIDecoder* idec, | |||
| 826 | const uint8_t* data, size_t data_size) { | |||
| 827 | VP8StatusCode status; | |||
| 828 | if (idec == NULL((void*)0) || data == NULL((void*)0)) { | |||
| 829 | return VP8_STATUS_INVALID_PARAM; | |||
| 830 | } | |||
| 831 | status = IDecCheckStatus(idec); | |||
| 832 | if (status != VP8_STATUS_SUSPENDED) { | |||
| 833 | return status; | |||
| 834 | } | |||
| 835 | // Check mixed calls between RemapMemBuffer and AppendToMemBuffer. | |||
| 836 | if (!CheckMemBufferMode(&idec->mem, MEM_MODE_MAP)) { | |||
| 837 | return VP8_STATUS_INVALID_PARAM; | |||
| 838 | } | |||
| 839 | // Make the memory buffer point to the new buffer | |||
| 840 | if (!RemapMemBuffer(idec, data, data_size)) { | |||
| 841 | return VP8_STATUS_INVALID_PARAM; | |||
| 842 | } | |||
| 843 | return IDecode(idec); | |||
| 844 | } | |||
| 845 | ||||
| 846 | //------------------------------------------------------------------------------ | |||
| 847 | ||||
| 848 | static const WebPDecBuffer* GetOutputBuffer(const WebPIDecoder* const idec) { | |||
| 849 | if (idec == NULL((void*)0) || idec->dec == NULL((void*)0)) { | |||
| 850 | return NULL((void*)0); | |||
| 851 | } | |||
| 852 | if (idec->state <= STATE_VP8_PARTS0) { | |||
| 853 | return NULL((void*)0); | |||
| 854 | } | |||
| 855 | if (idec->final_output != NULL((void*)0)) { | |||
| 856 | return NULL((void*)0); // not yet slow-copied | |||
| 857 | } | |||
| 858 | return idec->params.output; | |||
| 859 | } | |||
| 860 | ||||
| 861 | const WebPDecBuffer* WebPIDecodedArea(const WebPIDecoder* idec, | |||
| 862 | int* left, int* top, | |||
| 863 | int* width, int* height) { | |||
| 864 | const WebPDecBuffer* const src = GetOutputBuffer(idec); | |||
| 865 | if (left != NULL((void*)0)) *left = 0; | |||
| 866 | if (top != NULL((void*)0)) *top = 0; | |||
| 867 | if (src != NULL((void*)0)) { | |||
| 868 | if (width != NULL((void*)0)) *width = src->width; | |||
| 869 | if (height != NULL((void*)0)) *height = idec->params.last_y; | |||
| 870 | } else { | |||
| 871 | if (width != NULL((void*)0)) *width = 0; | |||
| 872 | if (height != NULL((void*)0)) *height = 0; | |||
| 873 | } | |||
| 874 | return src; | |||
| 875 | } | |||
| 876 | ||||
| 877 | WEBP_NODISCARD uint8_t* WebPIDecGetRGB(const WebPIDecoder* idec, int* last_y, | |||
| 878 | int* width, int* height, int* stride) { | |||
| 879 | const WebPDecBuffer* const src = GetOutputBuffer(idec); | |||
| 880 | if (src == NULL((void*)0)) return NULL((void*)0); | |||
| 881 | if (src->colorspace >= MODE_YUV) { | |||
| 882 | return NULL((void*)0); | |||
| 883 | } | |||
| 884 | ||||
| 885 | if (last_y != NULL((void*)0)) *last_y = idec->params.last_y; | |||
| 886 | if (width != NULL((void*)0)) *width = src->width; | |||
| 887 | if (height != NULL((void*)0)) *height = src->height; | |||
| 888 | if (stride != NULL((void*)0)) *stride = src->u.RGBA.stride; | |||
| 889 | ||||
| 890 | return src->u.RGBA.rgba; | |||
| 891 | } | |||
| 892 | ||||
| 893 | WEBP_NODISCARD uint8_t* WebPIDecGetYUVA(const WebPIDecoder* idec, int* last_y, | |||
| 894 | uint8_t** u, uint8_t** v, uint8_t** a, | |||
| 895 | int* width, int* height, int* stride, | |||
| 896 | int* uv_stride, int* a_stride) { | |||
| 897 | const WebPDecBuffer* const src = GetOutputBuffer(idec); | |||
| 898 | if (src == NULL((void*)0)) return NULL((void*)0); | |||
| 899 | if (src->colorspace < MODE_YUV) { | |||
| 900 | return NULL((void*)0); | |||
| 901 | } | |||
| 902 | ||||
| 903 | if (last_y != NULL((void*)0)) *last_y = idec->params.last_y; | |||
| 904 | if (u != NULL((void*)0)) *u = src->u.YUVA.u; | |||
| 905 | if (v != NULL((void*)0)) *v = src->u.YUVA.v; | |||
| 906 | if (a != NULL((void*)0)) *a = src->u.YUVA.a; | |||
| 907 | if (width != NULL((void*)0)) *width = src->width; | |||
| 908 | if (height != NULL((void*)0)) *height = src->height; | |||
| 909 | if (stride != NULL((void*)0)) *stride = src->u.YUVA.y_stride; | |||
| 910 | if (uv_stride != NULL((void*)0)) *uv_stride = src->u.YUVA.u_stride; | |||
| 911 | if (a_stride != NULL((void*)0)) *a_stride = src->u.YUVA.a_stride; | |||
| 912 | ||||
| 913 | return src->u.YUVA.y; | |||
| 914 | } | |||
| 915 | ||||
| 916 | int WebPISetIOHooks(WebPIDecoder* const idec, | |||
| 917 | VP8IoPutHook put, | |||
| 918 | VP8IoSetupHook setup, | |||
| 919 | VP8IoTeardownHook teardown, | |||
| 920 | void* user_data) { | |||
| 921 | if (idec == NULL((void*)0) || idec->state > STATE_WEBP_HEADER) { | |||
| 922 | return 0; | |||
| 923 | } | |||
| 924 | ||||
| 925 | idec->io.put = put; | |||
| 926 | idec->io.setup = setup; | |||
| 927 | idec->io.teardown = teardown; | |||
| 928 | idec->io.opaque = user_data; | |||
| 929 | ||||
| 930 | return 1; | |||
| 931 | } |