Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(58)

Side by Side Diff: third_party/libwebp/demux/demux.c

Issue 12942006: libwebp: update snapshot to v0.3.0-rc6 (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: local webkit layout expectations Created 7 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright 2012 Google Inc. All Rights Reserved.
2 //
3 // This code is licensed under the same terms as WebM:
4 // Software License Agreement: http://www.webmproject.org/license/software/
5 // Additional IP Rights Grant: http://www.webmproject.org/license/additional/
6 // -----------------------------------------------------------------------------
7 //
8 // WebP container demux.
9 //
10
11 #ifdef HAVE_CONFIG_H
12 #include "config.h"
13 #endif
14
15 #include <assert.h>
16 #include <stdlib.h>
17 #include <string.h>
18
19 #include "../utils/utils.h"
20 #include "../webp/decode.h" // WebPGetFeatures
21 #include "../webp/demux.h"
22 #include "../webp/format_constants.h"
23
24 #if defined(__cplusplus) || defined(c_plusplus)
25 extern "C" {
26 #endif
27
28 #define DMUX_MAJ_VERSION 0
29 #define DMUX_MIN_VERSION 1
30 #define DMUX_REV_VERSION 0
31
32 typedef struct {
33 size_t start_; // start location of the data
34 size_t end_; // end location
35 size_t riff_end_; // riff chunk end location, can be > end_.
36 size_t buf_size_; // size of the buffer
37 const uint8_t* buf_;
38 } MemBuffer;
39
40 typedef struct {
41 size_t offset_;
42 size_t size_;
fbarchard 2013/03/22 19:08:27 size_t is 64 bit on -m64. Are you sure this is wh
jzern 2013/03/22 19:18:25 Yes it's just to avoid transitioning back and fort
43 } ChunkData;
44
45 typedef struct Frame {
46 int x_offset_, y_offset_;
47 int width_, height_;
48 int duration_;
49 WebPMuxAnimDispose dispose_method_;
50 int is_fragment_; // this is a frame fragment (and not a full frame).
51 int frame_num_; // the referent frame number for use in assembling fragments.
52 int complete_; // img_components_ contains a full image.
53 ChunkData img_components_[2]; // 0=VP8{,L} 1=ALPH
54 struct Frame* next_;
55 } Frame;
56
57 typedef struct Chunk {
58 ChunkData data_;
59 struct Chunk* next_;
60 } Chunk;
61
62 struct WebPDemuxer {
63 MemBuffer mem_;
64 WebPDemuxState state_;
65 int is_ext_format_;
66 uint32_t feature_flags_;
67 int canvas_width_, canvas_height_;
68 int loop_count_;
69 uint32_t bgcolor_;
70 int num_frames_;
71 Frame* frames_;
72 Chunk* chunks_; // non-image chunks
73 };
74
75 typedef enum {
76 PARSE_OK,
77 PARSE_NEED_MORE_DATA,
78 PARSE_ERROR
79 } ParseStatus;
80
81 typedef struct ChunkParser {
82 uint8_t id[4];
83 ParseStatus (*parse)(WebPDemuxer* const dmux);
84 int (*valid)(const WebPDemuxer* const dmux);
85 } ChunkParser;
86
87 static ParseStatus ParseSingleImage(WebPDemuxer* const dmux);
88 static ParseStatus ParseVP8X(WebPDemuxer* const dmux);
89 static int IsValidSimpleFormat(const WebPDemuxer* const dmux);
90 static int IsValidExtendedFormat(const WebPDemuxer* const dmux);
91
92 static const ChunkParser kMasterChunks[] = {
93 { { 'V', 'P', '8', ' ' }, ParseSingleImage, IsValidSimpleFormat },
94 { { 'V', 'P', '8', 'L' }, ParseSingleImage, IsValidSimpleFormat },
95 { { 'V', 'P', '8', 'X' }, ParseVP8X, IsValidExtendedFormat },
96 { { '0', '0', '0', '0' }, NULL, NULL },
97 };
98
99 //------------------------------------------------------------------------------
100
101 int WebPGetDemuxVersion(void) {
102 return (DMUX_MAJ_VERSION << 16) | (DMUX_MIN_VERSION << 8) | DMUX_REV_VERSION;
103 }
104
105 // -----------------------------------------------------------------------------
106 // MemBuffer
107
108 static int RemapMemBuffer(MemBuffer* const mem,
109 const uint8_t* data, size_t size) {
110 if (size < mem->buf_size_) return 0; // can't remap to a shorter buffer!
111
112 mem->buf_ = data;
113 mem->end_ = mem->buf_size_ = size;
114 return 1;
115 }
116
117 static int InitMemBuffer(MemBuffer* const mem,
118 const uint8_t* data, size_t size) {
119 memset(mem, 0, sizeof(*mem));
120 return RemapMemBuffer(mem, data, size);
121 }
122
123 // Return the remaining data size available in 'mem'.
124 static WEBP_INLINE size_t MemDataSize(const MemBuffer* const mem) {
125 return (mem->end_ - mem->start_);
126 }
127
128 // Return true if 'size' exceeds the end of the RIFF chunk.
129 static WEBP_INLINE int SizeIsInvalid(const MemBuffer* const mem, size_t size) {
130 return (size > mem->riff_end_ - mem->start_);
131 }
132
133 static WEBP_INLINE void Skip(MemBuffer* const mem, size_t size) {
134 mem->start_ += size;
135 }
136
137 static WEBP_INLINE void Rewind(MemBuffer* const mem, size_t size) {
138 mem->start_ -= size;
139 }
140
141 static WEBP_INLINE const uint8_t* GetBuffer(MemBuffer* const mem) {
142 return mem->buf_ + mem->start_;
143 }
144
145 // Read from 'mem' and skip the read bytes.
146 static WEBP_INLINE uint8_t ReadByte(MemBuffer* const mem) {
147 const uint8_t byte = mem->buf_[mem->start_];
148 Skip(mem, 1);
149 return byte;
150 }
151
152 static WEBP_INLINE int ReadLE16s(MemBuffer* const mem) {
153 const uint8_t* const data = mem->buf_ + mem->start_;
154 const int val = GetLE16(data);
155 Skip(mem, 2);
156 return val;
157 }
158
159 static WEBP_INLINE int ReadLE24s(MemBuffer* const mem) {
160 const uint8_t* const data = mem->buf_ + mem->start_;
161 const int val = GetLE24(data);
162 Skip(mem, 3);
163 return val;
164 }
165
166 static WEBP_INLINE uint32_t ReadLE32(MemBuffer* const mem) {
167 const uint8_t* const data = mem->buf_ + mem->start_;
168 const uint32_t val = GetLE32(data);
169 Skip(mem, 4);
170 return val;
171 }
172
173 // -----------------------------------------------------------------------------
174 // Secondary chunk parsing
175
176 static void AddChunk(WebPDemuxer* const dmux, Chunk* const chunk) {
177 Chunk** c = &dmux->chunks_;
178 while (*c != NULL) c = &(*c)->next_;
179 *c = chunk;
180 chunk->next_ = NULL;
181 }
182
183 // Add a frame to the end of the list, ensuring the last frame is complete.
184 // Returns true on success, false otherwise.
185 static int AddFrame(WebPDemuxer* const dmux, Frame* const frame) {
186 const Frame* last_frame = NULL;
187 Frame** f = &dmux->frames_;
188 while (*f != NULL) {
189 last_frame = *f;
190 f = &(*f)->next_;
191 }
192 if (last_frame != NULL && !last_frame->complete_) return 0;
193 *f = frame;
194 frame->next_ = NULL;
195 return 1;
196 }
197
198 // Store image bearing chunks to 'frame'.
199 // If 'has_vp8l_alpha' is not NULL, it will be set to true if the frame is a
200 // lossless image with alpha.
201 static ParseStatus StoreFrame(int frame_num, uint32_t min_size,
202 MemBuffer* const mem, Frame* const frame,
203 int* const has_vp8l_alpha) {
204 int alpha_chunks = 0;
205 int image_chunks = 0;
206 int done = (MemDataSize(mem) < min_size);
207 ParseStatus status = PARSE_OK;
208
209 if (has_vp8l_alpha != NULL) *has_vp8l_alpha = 0; // Default.
210
211 if (done) return PARSE_NEED_MORE_DATA;
212
213 do {
214 const size_t chunk_start_offset = mem->start_;
215 const uint32_t fourcc = ReadLE32(mem);
216 const uint32_t payload_size = ReadLE32(mem);
217 const uint32_t payload_size_padded = payload_size + (payload_size & 1);
218 const size_t payload_available = (payload_size_padded > MemDataSize(mem))
219 ? MemDataSize(mem) : payload_size_padded;
220 const size_t chunk_size = CHUNK_HEADER_SIZE + payload_available;
221
222 if (payload_size > MAX_CHUNK_PAYLOAD) return PARSE_ERROR;
223 if (SizeIsInvalid(mem, payload_size_padded)) return PARSE_ERROR;
224 if (payload_size_padded > MemDataSize(mem)) status = PARSE_NEED_MORE_DATA;
225
226 switch (fourcc) {
227 case MKFOURCC('A', 'L', 'P', 'H'):
228 if (alpha_chunks == 0) {
229 ++alpha_chunks;
230 frame->img_components_[1].offset_ = chunk_start_offset;
231 frame->img_components_[1].size_ = chunk_size;
232 frame->frame_num_ = frame_num;
233 Skip(mem, payload_available);
234 } else {
235 goto Done;
236 }
237 break;
238 case MKFOURCC('V', 'P', '8', 'L'):
239 if (alpha_chunks > 0) return PARSE_ERROR; // VP8L has its own alpha
240 // fall through
241 case MKFOURCC('V', 'P', '8', ' '):
242 if (image_chunks == 0) {
243 // Extract the bitstream features, tolerating failures when the data
244 // is incomplete.
245 WebPBitstreamFeatures features;
246 const VP8StatusCode vp8_status =
247 WebPGetFeatures(mem->buf_ + chunk_start_offset, chunk_size,
248 &features);
249 if (status == PARSE_NEED_MORE_DATA &&
250 vp8_status == VP8_STATUS_NOT_ENOUGH_DATA) {
251 return PARSE_NEED_MORE_DATA;
252 } else if (vp8_status != VP8_STATUS_OK) {
253 // We have enough data, and yet WebPGetFeatures() failed.
254 return PARSE_ERROR;
255 }
256 ++image_chunks;
257 frame->img_components_[0].offset_ = chunk_start_offset;
258 frame->img_components_[0].size_ = chunk_size;
259 frame->width_ = features.width;
260 frame->height_ = features.height;
261 if (has_vp8l_alpha != NULL) *has_vp8l_alpha = features.has_alpha;
262 frame->frame_num_ = frame_num;
263 frame->complete_ = (status == PARSE_OK);
264 Skip(mem, payload_available);
265 } else {
266 goto Done;
267 }
268 break;
269 Done:
270 default:
271 // Restore fourcc/size when moving up one level in parsing.
272 Rewind(mem, CHUNK_HEADER_SIZE);
273 done = 1;
274 break;
275 }
276
277 if (mem->start_ == mem->riff_end_) {
278 done = 1;
279 } else if (MemDataSize(mem) < CHUNK_HEADER_SIZE) {
280 status = PARSE_NEED_MORE_DATA;
281 }
282 } while (!done && status == PARSE_OK);
283
284 return status;
285 }
286
287 // Creates a new Frame if 'actual_size' is within bounds and 'mem' contains
288 // enough data ('min_size') to parse the payload.
289 // Returns PARSE_OK on success with *frame pointing to the new Frame.
290 // Returns PARSE_NEED_MORE_DATA with insufficient data, PARSE_ERROR otherwise.
291 static ParseStatus NewFrame(const MemBuffer* const mem,
292 uint32_t min_size, uint32_t actual_size,
293 Frame** frame) {
294 if (SizeIsInvalid(mem, min_size)) return PARSE_ERROR;
295 if (actual_size < min_size) return PARSE_ERROR;
296 if (MemDataSize(mem) < min_size) return PARSE_NEED_MORE_DATA;
297
298 *frame = (Frame*)calloc(1, sizeof(**frame));
299 return (*frame == NULL) ? PARSE_ERROR : PARSE_OK;
300 }
301
302 // Parse a 'ANMF' chunk and any image bearing chunks that immediately follow.
303 // 'frame_chunk_size' is the previously validated, padded chunk size.
304 static ParseStatus ParseAnimationFrame(
305 WebPDemuxer* const dmux, uint32_t frame_chunk_size) {
306 const int has_frames = !!(dmux->feature_flags_ & ANIMATION_FLAG);
307 const uint32_t anmf_payload_size = frame_chunk_size - ANMF_CHUNK_SIZE;
308 int added_frame = 0;
309 MemBuffer* const mem = &dmux->mem_;
310 Frame* frame;
311 ParseStatus status =
312 NewFrame(mem, ANMF_CHUNK_SIZE, frame_chunk_size, &frame);
313 if (status != PARSE_OK) return status;
314
315 frame->x_offset_ = 2 * ReadLE24s(mem);
316 frame->y_offset_ = 2 * ReadLE24s(mem);
317 frame->width_ = 1 + ReadLE24s(mem);
318 frame->height_ = 1 + ReadLE24s(mem);
319 frame->duration_ = ReadLE24s(mem);
320 frame->dispose_method_ = (WebPMuxAnimDispose)(ReadByte(mem) & 1);
321 if (frame->width_ * (uint64_t)frame->height_ >= MAX_IMAGE_AREA) {
322 return PARSE_ERROR;
323 }
324
325 // Store a frame only if the animation flag is set there is some data for
326 // this frame is available.
327 status = StoreFrame(dmux->num_frames_ + 1, anmf_payload_size, mem, frame,
328 NULL);
329 if (status != PARSE_ERROR && has_frames && frame->frame_num_ > 0) {
330 added_frame = AddFrame(dmux, frame);
331 if (added_frame) {
332 ++dmux->num_frames_;
333 } else {
334 status = PARSE_ERROR;
335 }
336 }
337
338 if (!added_frame) free(frame);
339 return status;
340 }
341
342 #ifdef WEBP_EXPERIMENTAL_FEATURES
343 // Parse a 'FRGM' chunk and any image bearing chunks that immediately follow.
344 // 'fragment_chunk_size' is the previously validated, padded chunk size.
345 static ParseStatus ParseFragment(WebPDemuxer* const dmux,
346 uint32_t fragment_chunk_size) {
347 const int frame_num = 1; // All fragments belong to the 1st (and only) frame.
348 const int has_fragments = !!(dmux->feature_flags_ & FRAGMENTS_FLAG);
349 const uint32_t frgm_payload_size = fragment_chunk_size - FRGM_CHUNK_SIZE;
350 int added_fragment = 0;
351 MemBuffer* const mem = &dmux->mem_;
352 Frame* frame;
353 ParseStatus status =
354 NewFrame(mem, FRGM_CHUNK_SIZE, fragment_chunk_size, &frame);
355 if (status != PARSE_OK) return status;
356
357 frame->is_fragment_ = 1;
358 frame->x_offset_ = 2 * ReadLE24s(mem);
359 frame->y_offset_ = 2 * ReadLE24s(mem);
360
361 // Store a fragment only if the fragments flag is set there is some data for
362 // this fragment is available.
363 status = StoreFrame(frame_num, frgm_payload_size, mem, frame, NULL);
364 if (status != PARSE_ERROR && has_fragments && frame->frame_num_ > 0) {
365 added_fragment = AddFrame(dmux, frame);
366 if (!added_fragment) {
367 status = PARSE_ERROR;
368 } else {
369 dmux->num_frames_ = 1;
370 }
371 }
372
373 if (!added_fragment) free(frame);
374 return status;
375 }
376 #endif // WEBP_EXPERIMENTAL_FEATURES
377
378 // General chunk storage, starting with the header at 'start_offset', allowing
379 // the user to request the payload via a fourcc string. 'size' includes the
380 // header and the unpadded payload size.
381 // Returns true on success, false otherwise.
382 static int StoreChunk(WebPDemuxer* const dmux,
383 size_t start_offset, uint32_t size) {
384 Chunk* const chunk = (Chunk*)calloc(1, sizeof(*chunk));
385 if (chunk == NULL) return 0;
386
387 chunk->data_.offset_ = start_offset;
388 chunk->data_.size_ = size;
389 AddChunk(dmux, chunk);
390 return 1;
391 }
392
393 // -----------------------------------------------------------------------------
394 // Primary chunk parsing
395
396 static int ReadHeader(MemBuffer* const mem) {
397 const size_t min_size = RIFF_HEADER_SIZE + CHUNK_HEADER_SIZE;
398 uint32_t riff_size;
399
400 // Basic file level validation.
401 if (MemDataSize(mem) < min_size) return 0;
402 if (memcmp(GetBuffer(mem), "RIFF", CHUNK_SIZE_BYTES) ||
403 memcmp(GetBuffer(mem) + CHUNK_HEADER_SIZE, "WEBP", CHUNK_SIZE_BYTES)) {
404 return 0;
405 }
406
407 riff_size = GetLE32(GetBuffer(mem) + TAG_SIZE);
408 if (riff_size < CHUNK_HEADER_SIZE) return 0;
409 if (riff_size > MAX_CHUNK_PAYLOAD) return 0;
410
411 // There's no point in reading past the end of the RIFF chunk
412 mem->riff_end_ = riff_size + CHUNK_HEADER_SIZE;
413 if (mem->buf_size_ > mem->riff_end_) {
414 mem->buf_size_ = mem->end_ = mem->riff_end_;
415 }
416
417 Skip(mem, RIFF_HEADER_SIZE);
418 return 1;
419 }
420
421 static ParseStatus ParseSingleImage(WebPDemuxer* const dmux) {
422 const size_t min_size = CHUNK_HEADER_SIZE;
423 MemBuffer* const mem = &dmux->mem_;
424 Frame* frame;
425 ParseStatus status;
426 int has_vp8l_alpha = 0; // Frame contains a lossless image with alpha.
427
428 if (dmux->frames_ != NULL) return PARSE_ERROR;
429 if (SizeIsInvalid(mem, min_size)) return PARSE_ERROR;
430 if (MemDataSize(mem) < min_size) return PARSE_NEED_MORE_DATA;
431
432 frame = (Frame*)calloc(1, sizeof(*frame));
433 if (frame == NULL) return PARSE_ERROR;
434
435 // For the single image case we allow parsing of a partial frame, but we need
436 // at least CHUNK_HEADER_SIZE for parsing.
437 status = StoreFrame(1, CHUNK_HEADER_SIZE, &dmux->mem_, frame,
438 &has_vp8l_alpha);
439 if (status != PARSE_ERROR) {
440 const int has_alpha = !!(dmux->feature_flags_ & ALPHA_FLAG);
441 // Clear any alpha when the alpha flag is missing.
442 if (!has_alpha && frame->img_components_[1].size_ > 0) {
443 frame->img_components_[1].offset_ = 0;
444 frame->img_components_[1].size_ = 0;
445 }
446
447 // Use the frame width/height as the canvas values for non-vp8x files.
448 // Also, set ALPHA_FLAG if this is a lossless image with alpha.
449 if (!dmux->is_ext_format_ && frame->width_ > 0 && frame->height_ > 0) {
450 dmux->state_ = WEBP_DEMUX_PARSED_HEADER;
451 dmux->canvas_width_ = frame->width_;
452 dmux->canvas_height_ = frame->height_;
453 dmux->feature_flags_ |= has_vp8l_alpha ? ALPHA_FLAG : 0;
454 }
455 AddFrame(dmux, frame);
456 dmux->num_frames_ = 1;
457 } else {
458 free(frame);
459 }
460
461 return status;
462 }
463
464 static ParseStatus ParseVP8X(WebPDemuxer* const dmux) {
465 MemBuffer* const mem = &dmux->mem_;
466 int anim_chunks = 0;
467 uint32_t vp8x_size;
468 ParseStatus status = PARSE_OK;
469
470 if (MemDataSize(mem) < CHUNK_HEADER_SIZE) return PARSE_NEED_MORE_DATA;
471
472 dmux->is_ext_format_ = 1;
473 Skip(mem, TAG_SIZE); // VP8X
474 vp8x_size = ReadLE32(mem);
475 if (vp8x_size > MAX_CHUNK_PAYLOAD) return PARSE_ERROR;
476 if (vp8x_size < VP8X_CHUNK_SIZE) return PARSE_ERROR;
477 vp8x_size += vp8x_size & 1;
478 if (SizeIsInvalid(mem, vp8x_size)) return PARSE_ERROR;
479 if (MemDataSize(mem) < vp8x_size) return PARSE_NEED_MORE_DATA;
480
481 dmux->feature_flags_ = ReadByte(mem);
482 Skip(mem, 3); // Reserved.
483 dmux->canvas_width_ = 1 + ReadLE24s(mem);
484 dmux->canvas_height_ = 1 + ReadLE24s(mem);
485 if (dmux->canvas_width_ * (uint64_t)dmux->canvas_height_ >= MAX_IMAGE_AREA) {
486 return PARSE_ERROR; // image final dimension is too large
487 }
488 Skip(mem, vp8x_size - VP8X_CHUNK_SIZE); // skip any trailing data.
489 dmux->state_ = WEBP_DEMUX_PARSED_HEADER;
490
491 if (SizeIsInvalid(mem, CHUNK_HEADER_SIZE)) return PARSE_ERROR;
492 if (MemDataSize(mem) < CHUNK_HEADER_SIZE) return PARSE_NEED_MORE_DATA;
493
494 do {
495 int store_chunk = 1;
496 const size_t chunk_start_offset = mem->start_;
497 const uint32_t fourcc = ReadLE32(mem);
498 const uint32_t chunk_size = ReadLE32(mem);
499 const uint32_t chunk_size_padded = chunk_size + (chunk_size & 1);
500
501 if (chunk_size > MAX_CHUNK_PAYLOAD) return PARSE_ERROR;
502 if (SizeIsInvalid(mem, chunk_size_padded)) return PARSE_ERROR;
503
504 switch (fourcc) {
505 case MKFOURCC('V', 'P', '8', 'X'): {
506 return PARSE_ERROR;
507 }
508 case MKFOURCC('A', 'L', 'P', 'H'):
509 case MKFOURCC('V', 'P', '8', ' '):
510 case MKFOURCC('V', 'P', '8', 'L'): {
511 // check that this isn't an animation (all frames should be in an ANMF).
512 if (anim_chunks > 0) return PARSE_ERROR;
513
514 Rewind(mem, CHUNK_HEADER_SIZE);
515 status = ParseSingleImage(dmux);
516 break;
517 }
518 case MKFOURCC('A', 'N', 'I', 'M'): {
519 if (chunk_size_padded < ANIM_CHUNK_SIZE) return PARSE_ERROR;
520
521 if (MemDataSize(mem) < chunk_size_padded) {
522 status = PARSE_NEED_MORE_DATA;
523 } else if (anim_chunks == 0) {
524 ++anim_chunks;
525 dmux->bgcolor_ = ReadLE32(mem);
526 dmux->loop_count_ = ReadLE16s(mem);
527 Skip(mem, chunk_size_padded - ANIM_CHUNK_SIZE);
528 } else {
529 store_chunk = 0;
530 goto Skip;
531 }
532 break;
533 }
534 case MKFOURCC('A', 'N', 'M', 'F'): {
535 if (anim_chunks == 0) return PARSE_ERROR; // 'ANIM' precedes frames.
536 status = ParseAnimationFrame(dmux, chunk_size_padded);
537 break;
538 }
539 #ifdef WEBP_EXPERIMENTAL_FEATURES
540 case MKFOURCC('F', 'R', 'G', 'M'): {
541 status = ParseFragment(dmux, chunk_size_padded);
542 break;
543 }
544 #endif
545 case MKFOURCC('I', 'C', 'C', 'P'): {
546 store_chunk = !!(dmux->feature_flags_ & ICCP_FLAG);
547 goto Skip;
548 }
549 case MKFOURCC('X', 'M', 'P', ' '): {
550 store_chunk = !!(dmux->feature_flags_ & XMP_FLAG);
551 goto Skip;
552 }
553 case MKFOURCC('E', 'X', 'I', 'F'): {
554 store_chunk = !!(dmux->feature_flags_ & EXIF_FLAG);
555 goto Skip;
556 }
557 Skip:
558 default: {
559 if (chunk_size_padded <= MemDataSize(mem)) {
560 if (store_chunk) {
561 // Store only the chunk header and unpadded size as only the payload
562 // will be returned to the user.
563 if (!StoreChunk(dmux, chunk_start_offset,
564 CHUNK_HEADER_SIZE + chunk_size)) {
565 return PARSE_ERROR;
566 }
567 }
568 Skip(mem, chunk_size_padded);
569 } else {
570 status = PARSE_NEED_MORE_DATA;
571 }
572 }
573 }
574
575 if (mem->start_ == mem->riff_end_) {
576 break;
577 } else if (MemDataSize(mem) < CHUNK_HEADER_SIZE) {
578 status = PARSE_NEED_MORE_DATA;
579 }
580 } while (status == PARSE_OK);
581
582 return status;
583 }
584
585 // -----------------------------------------------------------------------------
586 // Format validation
587
588 static int IsValidSimpleFormat(const WebPDemuxer* const dmux) {
589 const Frame* const frame = dmux->frames_;
590 if (dmux->state_ == WEBP_DEMUX_PARSING_HEADER) return 1;
591
592 if (dmux->canvas_width_ <= 0 || dmux->canvas_height_ <= 0) return 0;
593 if (dmux->state_ == WEBP_DEMUX_DONE && frame == NULL) return 0;
594
595 if (frame->width_ <= 0 || frame->height_ <= 0) return 0;
596 return 1;
597 }
598
599 static int IsValidExtendedFormat(const WebPDemuxer* const dmux) {
600 const int has_fragments = !!(dmux->feature_flags_ & FRAGMENTS_FLAG);
601 const int has_frames = !!(dmux->feature_flags_ & ANIMATION_FLAG);
602 const Frame* f;
603
604 if (dmux->state_ == WEBP_DEMUX_PARSING_HEADER) return 1;
605
606 if (dmux->canvas_width_ <= 0 || dmux->canvas_height_ <= 0) return 0;
607 if (dmux->loop_count_ < 0) return 0;
608 if (dmux->state_ == WEBP_DEMUX_DONE && dmux->frames_ == NULL) return 0;
609
610 for (f = dmux->frames_; f != NULL; f = f->next_) {
611 const int cur_frame_set = f->frame_num_;
612 int frame_count = 0, fragment_count = 0;
613
614 // Check frame properties and if the image is composed of fragments that
615 // each fragment came from a fragment.
616 for (; f != NULL && f->frame_num_ == cur_frame_set; f = f->next_) {
617 const ChunkData* const image = f->img_components_;
618 const ChunkData* const alpha = f->img_components_ + 1;
619
620 if (!has_fragments && f->is_fragment_) return 0;
621 if (!has_frames && f->frame_num_ > 1) return 0;
622 if (f->x_offset_ < 0 || f->y_offset_ < 0) return 0;
623 if (f->complete_) {
624 if (alpha->size_ == 0 && image->size_ == 0) return 0;
625 // Ensure alpha precedes image bitstream.
626 if (alpha->size_ > 0 && alpha->offset_ > image->offset_) {
627 return 0;
628 }
629
630 if (f->width_ <= 0 || f->height_ <= 0) return 0;
631 } else {
632 // There shouldn't be a partial frame in a complete file.
633 if (dmux->state_ == WEBP_DEMUX_DONE) return 0;
634
635 // Ensure alpha precedes image bitstream.
636 if (alpha->size_ > 0 && image->size_ > 0 &&
637 alpha->offset_ > image->offset_) {
638 return 0;
639 }
640 // There shouldn't be any frames after an incomplete one.
641 if (f->next_ != NULL) return 0;
642 }
643
644 fragment_count += f->is_fragment_;
645 ++frame_count;
646 }
647 if (!has_fragments && frame_count > 1) return 0;
648 if (fragment_count > 0 && frame_count != fragment_count) return 0;
649 if (f == NULL) break;
650 }
651 return 1;
652 }
653
654 // -----------------------------------------------------------------------------
655 // WebPDemuxer object
656
657 static void InitDemux(WebPDemuxer* const dmux, const MemBuffer* const mem) {
658 dmux->state_ = WEBP_DEMUX_PARSING_HEADER;
659 dmux->loop_count_ = 1;
660 dmux->bgcolor_ = 0xFFFFFFFF; // White background by default.
661 dmux->canvas_width_ = -1;
662 dmux->canvas_height_ = -1;
663 dmux->mem_ = *mem;
664 }
665
666 WebPDemuxer* WebPDemuxInternal(const WebPData* data, int allow_partial,
667 WebPDemuxState* state, int version) {
668 const ChunkParser* parser;
669 int partial;
670 ParseStatus status = PARSE_ERROR;
671 MemBuffer mem;
672 WebPDemuxer* dmux;
673
674 if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_DEMUX_ABI_VERSION)) return NULL;
675 if (data == NULL || data->bytes == NULL || data->size == 0) return NULL;
676
677 if (!InitMemBuffer(&mem, data->bytes, data->size)) return NULL;
678 if (!ReadHeader(&mem)) return NULL;
679
680 partial = (mem.buf_size_ < mem.riff_end_);
681 if (!allow_partial && partial) return NULL;
682
683 dmux = (WebPDemuxer*)calloc(1, sizeof(*dmux));
684 if (dmux == NULL) return NULL;
685 InitDemux(dmux, &mem);
686
687 for (parser = kMasterChunks; parser->parse != NULL; ++parser) {
688 if (!memcmp(parser->id, GetBuffer(&dmux->mem_), TAG_SIZE)) {
689 status = parser->parse(dmux);
690 if (status == PARSE_OK) dmux->state_ = WEBP_DEMUX_DONE;
691 if (status == PARSE_NEED_MORE_DATA && !partial) status = PARSE_ERROR;
692 if (status != PARSE_ERROR && !parser->valid(dmux)) status = PARSE_ERROR;
693 break;
694 }
695 }
696 if (state) *state = dmux->state_;
697
698 if (status == PARSE_ERROR) {
699 WebPDemuxDelete(dmux);
700 return NULL;
701 }
702 return dmux;
703 }
704
705 void WebPDemuxDelete(WebPDemuxer* dmux) {
706 Chunk* c;
707 Frame* f;
708 if (dmux == NULL) return;
709
710 for (f = dmux->frames_; f != NULL;) {
711 Frame* const cur_frame = f;
712 f = f->next_;
713 free(cur_frame);
714 }
715 for (c = dmux->chunks_; c != NULL;) {
716 Chunk* const cur_chunk = c;
717 c = c->next_;
718 free(cur_chunk);
719 }
720 free(dmux);
721 }
722
723 // -----------------------------------------------------------------------------
724
725 uint32_t WebPDemuxGetI(const WebPDemuxer* dmux, WebPFormatFeature feature) {
726 if (dmux == NULL) return 0;
727
728 switch (feature) {
729 case WEBP_FF_FORMAT_FLAGS: return dmux->feature_flags_;
730 case WEBP_FF_CANVAS_WIDTH: return (uint32_t)dmux->canvas_width_;
731 case WEBP_FF_CANVAS_HEIGHT: return (uint32_t)dmux->canvas_height_;
732 case WEBP_FF_LOOP_COUNT: return (uint32_t)dmux->loop_count_;
733 case WEBP_FF_BACKGROUND_COLOR: return dmux->bgcolor_;
734 case WEBP_FF_FRAME_COUNT: return (uint32_t)dmux->num_frames_;
735 }
736 return 0;
737 }
738
739 // -----------------------------------------------------------------------------
740 // Frame iteration
741
742 // Find the first 'frame_num' frame. There may be multiple such frames in a
743 // fragmented frame.
744 static const Frame* GetFrame(const WebPDemuxer* const dmux, int frame_num) {
745 const Frame* f;
746 for (f = dmux->frames_; f != NULL; f = f->next_) {
747 if (frame_num == f->frame_num_) break;
748 }
749 return f;
750 }
751
752 // Returns fragment 'fragment_num' and the total count.
753 static const Frame* GetFragment(
754 const Frame* const frame_set, int fragment_num, int* const count) {
755 const int this_frame = frame_set->frame_num_;
756 const Frame* f = frame_set;
757 const Frame* fragment = NULL;
758 int total;
759
760 for (total = 0; f != NULL && f->frame_num_ == this_frame; f = f->next_) {
761 if (++total == fragment_num) fragment = f;
762 }
763 *count = total;
764 return fragment;
765 }
766
767 static const uint8_t* GetFramePayload(const uint8_t* const mem_buf,
768 const Frame* const frame,
769 size_t* const data_size) {
770 *data_size = 0;
771 if (frame != NULL) {
772 const ChunkData* const image = frame->img_components_;
773 const ChunkData* const alpha = frame->img_components_ + 1;
774 size_t start_offset = image->offset_;
775 *data_size = image->size_;
776
777 // if alpha exists it precedes image, update the size allowing for
778 // intervening chunks.
779 if (alpha->size_ > 0) {
780 const size_t inter_size = (image->offset_ > 0)
781 ? image->offset_ - (alpha->offset_ + alpha->size_)
782 : 0;
783 start_offset = alpha->offset_;
784 *data_size += alpha->size_ + inter_size;
785 }
786 return mem_buf + start_offset;
787 }
788 return NULL;
789 }
790
791 // Create a whole 'frame' from VP8 (+ alpha) or lossless.
792 static int SynthesizeFrame(const WebPDemuxer* const dmux,
793 const Frame* const first_frame,
794 int fragment_num, WebPIterator* const iter) {
795 const uint8_t* const mem_buf = dmux->mem_.buf_;
796 int num_fragments;
797 size_t payload_size = 0;
798 const Frame* const fragment =
799 GetFragment(first_frame, fragment_num, &num_fragments);
800 const uint8_t* const payload =
801 GetFramePayload(mem_buf, fragment, &payload_size);
802 if (payload == NULL) return 0;
803 assert(first_frame != NULL);
804
805 iter->frame_num = first_frame->frame_num_;
806 iter->num_frames = dmux->num_frames_;
807 iter->fragment_num = fragment_num;
808 iter->num_fragments = num_fragments;
809 iter->x_offset = fragment->x_offset_;
810 iter->y_offset = fragment->y_offset_;
811 iter->width = fragment->width_;
812 iter->height = fragment->height_;
813 iter->duration = fragment->duration_;
814 iter->dispose_method = fragment->dispose_method_;
815 iter->complete = fragment->complete_;
816 iter->fragment.bytes = payload;
817 iter->fragment.size = payload_size;
818 // TODO(jzern): adjust offsets for 'FRGM's embedded in 'ANMF's
819 return 1;
820 }
821
822 static int SetFrame(int frame_num, WebPIterator* const iter) {
823 const Frame* frame;
824 const WebPDemuxer* const dmux = (WebPDemuxer*)iter->private_;
825 if (dmux == NULL || frame_num < 0) return 0;
826 if (frame_num > dmux->num_frames_) return 0;
827 if (frame_num == 0) frame_num = dmux->num_frames_;
828
829 frame = GetFrame(dmux, frame_num);
830 if (frame == NULL) return 0;
831
832 return SynthesizeFrame(dmux, frame, 1, iter);
833 }
834
835 int WebPDemuxGetFrame(const WebPDemuxer* dmux, int frame, WebPIterator* iter) {
836 if (iter == NULL) return 0;
837
838 memset(iter, 0, sizeof(*iter));
839 iter->private_ = (void*)dmux;
840 return SetFrame(frame, iter);
841 }
842
843 int WebPDemuxNextFrame(WebPIterator* iter) {
844 if (iter == NULL) return 0;
845 return SetFrame(iter->frame_num + 1, iter);
846 }
847
848 int WebPDemuxPrevFrame(WebPIterator* iter) {
849 if (iter == NULL) return 0;
850 if (iter->frame_num <= 1) return 0;
851 return SetFrame(iter->frame_num - 1, iter);
852 }
853
854 int WebPDemuxSelectFragment(WebPIterator* iter, int fragment_num) {
855 if (iter != NULL && iter->private_ != NULL && fragment_num > 0) {
856 const WebPDemuxer* const dmux = (WebPDemuxer*)iter->private_;
857 const Frame* const frame = GetFrame(dmux, iter->frame_num);
858 if (frame == NULL) return 0;
859
860 return SynthesizeFrame(dmux, frame, fragment_num, iter);
861 }
862 return 0;
863 }
864
865 void WebPDemuxReleaseIterator(WebPIterator* iter) {
866 (void)iter;
867 }
868
869 // -----------------------------------------------------------------------------
870 // Chunk iteration
871
872 static int ChunkCount(const WebPDemuxer* const dmux, const char fourcc[4]) {
873 const uint8_t* const mem_buf = dmux->mem_.buf_;
874 const Chunk* c;
875 int count = 0;
876 for (c = dmux->chunks_; c != NULL; c = c->next_) {
877 const uint8_t* const header = mem_buf + c->data_.offset_;
878 if (!memcmp(header, fourcc, TAG_SIZE)) ++count;
879 }
880 return count;
881 }
882
883 static const Chunk* GetChunk(const WebPDemuxer* const dmux,
884 const char fourcc[4], int chunk_num) {
885 const uint8_t* const mem_buf = dmux->mem_.buf_;
886 const Chunk* c;
887 int count = 0;
888 for (c = dmux->chunks_; c != NULL; c = c->next_) {
889 const uint8_t* const header = mem_buf + c->data_.offset_;
890 if (!memcmp(header, fourcc, TAG_SIZE)) ++count;
891 if (count == chunk_num) break;
892 }
893 return c;
894 }
895
896 static int SetChunk(const char fourcc[4], int chunk_num,
897 WebPChunkIterator* const iter) {
898 const WebPDemuxer* const dmux = (WebPDemuxer*)iter->private_;
899 int count;
900
901 if (dmux == NULL || fourcc == NULL || chunk_num < 0) return 0;
902 count = ChunkCount(dmux, fourcc);
903 if (count == 0) return 0;
904 if (chunk_num == 0) chunk_num = count;
905
906 if (chunk_num <= count) {
907 const uint8_t* const mem_buf = dmux->mem_.buf_;
908 const Chunk* const chunk = GetChunk(dmux, fourcc, chunk_num);
909 iter->chunk.bytes = mem_buf + chunk->data_.offset_ + CHUNK_HEADER_SIZE;
910 iter->chunk.size = chunk->data_.size_ - CHUNK_HEADER_SIZE;
911 iter->num_chunks = count;
912 iter->chunk_num = chunk_num;
913 return 1;
914 }
915 return 0;
916 }
917
918 int WebPDemuxGetChunk(const WebPDemuxer* dmux,
919 const char fourcc[4], int chunk_num,
920 WebPChunkIterator* iter) {
921 if (iter == NULL) return 0;
922
923 memset(iter, 0, sizeof(*iter));
924 iter->private_ = (void*)dmux;
925 return SetChunk(fourcc, chunk_num, iter);
926 }
927
928 int WebPDemuxNextChunk(WebPChunkIterator* iter) {
929 if (iter != NULL) {
930 const char* const fourcc =
931 (const char*)iter->chunk.bytes - CHUNK_HEADER_SIZE;
932 return SetChunk(fourcc, iter->chunk_num + 1, iter);
933 }
934 return 0;
935 }
936
937 int WebPDemuxPrevChunk(WebPChunkIterator* iter) {
938 if (iter != NULL && iter->chunk_num > 1) {
939 const char* const fourcc =
940 (const char*)iter->chunk.bytes - CHUNK_HEADER_SIZE;
941 return SetChunk(fourcc, iter->chunk_num - 1, iter);
942 }
943 return 0;
944 }
945
946 void WebPDemuxReleaseChunkIterator(WebPChunkIterator* iter) {
947 (void)iter;
948 }
949
950 #if defined(__cplusplus) || defined(c_plusplus)
951 } // extern "C"
952 #endif
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698