OLD | NEW |
---|---|
1 // Copyright 2012 Google Inc. All Rights Reserved. | 1 // Copyright 2012 Google Inc. All Rights Reserved. |
2 // | 2 // |
3 // Use of this source code is governed by a BSD-style license | 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 | 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 | 5 // tree. An additional intellectual property rights grant can be found |
6 // in the file PATENTS. All contributing project authors may | 6 // in the file PATENTS. All contributing project authors may |
7 // be found in the AUTHORS file in the root of the source tree. | 7 // be found in the AUTHORS file in the root of the source tree. |
8 // ----------------------------------------------------------------------------- | 8 // ----------------------------------------------------------------------------- |
9 // | 9 // |
10 // WebP container demux. | 10 // WebP container demux. |
(...skipping 29 matching lines...) Expand all Loading... | |
40 } MemBuffer; | 40 } MemBuffer; |
41 | 41 |
42 typedef struct { | 42 typedef struct { |
43 size_t offset_; | 43 size_t offset_; |
44 size_t size_; | 44 size_t size_; |
45 } ChunkData; | 45 } ChunkData; |
46 | 46 |
47 typedef struct Frame { | 47 typedef struct Frame { |
48 int x_offset_, y_offset_; | 48 int x_offset_, y_offset_; |
49 int width_, height_; | 49 int width_, height_; |
50 int has_alpha_; | |
50 int duration_; | 51 int duration_; |
51 WebPMuxAnimDispose dispose_method_; | 52 WebPMuxAnimDispose dispose_method_; |
53 WebPMuxAnimBlend blend_method_; | |
52 int is_fragment_; // this is a frame fragment (and not a full frame). | 54 int is_fragment_; // this is a frame fragment (and not a full frame). |
53 int frame_num_; // the referent frame number for use in assembling fragments. | 55 int frame_num_; // the referent frame number for use in assembling fragments. |
54 int complete_; // img_components_ contains a full image. | 56 int complete_; // img_components_ contains a full image. |
55 ChunkData img_components_[2]; // 0=VP8{,L} 1=ALPH | 57 ChunkData img_components_[2]; // 0=VP8{,L} 1=ALPH |
56 struct Frame* next_; | 58 struct Frame* next_; |
57 } Frame; | 59 } Frame; |
58 | 60 |
59 typedef struct Chunk { | 61 typedef struct Chunk { |
60 ChunkData data_; | 62 ChunkData data_; |
61 struct Chunk* next_; | 63 struct Chunk* next_; |
(...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
189 const Frame* const last_frame = *dmux->frames_tail_; | 191 const Frame* const last_frame = *dmux->frames_tail_; |
190 if (last_frame != NULL && !last_frame->complete_) return 0; | 192 if (last_frame != NULL && !last_frame->complete_) return 0; |
191 | 193 |
192 *dmux->frames_tail_ = frame; | 194 *dmux->frames_tail_ = frame; |
193 frame->next_ = NULL; | 195 frame->next_ = NULL; |
194 dmux->frames_tail_ = &frame->next_; | 196 dmux->frames_tail_ = &frame->next_; |
195 return 1; | 197 return 1; |
196 } | 198 } |
197 | 199 |
198 // Store image bearing chunks to 'frame'. | 200 // 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, | 201 static ParseStatus StoreFrame(int frame_num, uint32_t min_size, |
202 MemBuffer* const mem, Frame* const frame, | 202 MemBuffer* const mem, Frame* const frame) { |
203 int* const has_vp8l_alpha) { | |
204 int alpha_chunks = 0; | 203 int alpha_chunks = 0; |
205 int image_chunks = 0; | 204 int image_chunks = 0; |
206 int done = (MemDataSize(mem) < min_size); | 205 int done = (MemDataSize(mem) < min_size); |
207 ParseStatus status = PARSE_OK; | 206 ParseStatus status = PARSE_OK; |
208 | 207 |
209 if (has_vp8l_alpha != NULL) *has_vp8l_alpha = 0; // Default. | |
210 | |
211 if (done) return PARSE_NEED_MORE_DATA; | 208 if (done) return PARSE_NEED_MORE_DATA; |
212 | 209 |
213 do { | 210 do { |
214 const size_t chunk_start_offset = mem->start_; | 211 const size_t chunk_start_offset = mem->start_; |
215 const uint32_t fourcc = ReadLE32(mem); | 212 const uint32_t fourcc = ReadLE32(mem); |
216 const uint32_t payload_size = ReadLE32(mem); | 213 const uint32_t payload_size = ReadLE32(mem); |
217 const uint32_t payload_size_padded = payload_size + (payload_size & 1); | 214 const uint32_t payload_size_padded = payload_size + (payload_size & 1); |
218 const size_t payload_available = (payload_size_padded > MemDataSize(mem)) | 215 const size_t payload_available = (payload_size_padded > MemDataSize(mem)) |
219 ? MemDataSize(mem) : payload_size_padded; | 216 ? MemDataSize(mem) : payload_size_padded; |
220 const size_t chunk_size = CHUNK_HEADER_SIZE + payload_available; | 217 const size_t chunk_size = CHUNK_HEADER_SIZE + payload_available; |
221 | 218 |
222 if (payload_size > MAX_CHUNK_PAYLOAD) return PARSE_ERROR; | 219 if (payload_size > MAX_CHUNK_PAYLOAD) return PARSE_ERROR; |
223 if (SizeIsInvalid(mem, payload_size_padded)) return PARSE_ERROR; | 220 if (SizeIsInvalid(mem, payload_size_padded)) return PARSE_ERROR; |
224 if (payload_size_padded > MemDataSize(mem)) status = PARSE_NEED_MORE_DATA; | 221 if (payload_size_padded > MemDataSize(mem)) status = PARSE_NEED_MORE_DATA; |
225 | 222 |
226 switch (fourcc) { | 223 switch (fourcc) { |
227 case MKFOURCC('A', 'L', 'P', 'H'): | 224 case MKFOURCC('A', 'L', 'P', 'H'): |
228 if (alpha_chunks == 0) { | 225 if (alpha_chunks == 0) { |
229 ++alpha_chunks; | 226 ++alpha_chunks; |
230 frame->img_components_[1].offset_ = chunk_start_offset; | 227 frame->img_components_[1].offset_ = chunk_start_offset; |
231 frame->img_components_[1].size_ = chunk_size; | 228 frame->img_components_[1].size_ = chunk_size; |
229 frame->has_alpha_ = 1; | |
232 frame->frame_num_ = frame_num; | 230 frame->frame_num_ = frame_num; |
233 Skip(mem, payload_available); | 231 Skip(mem, payload_available); |
234 } else { | 232 } else { |
235 goto Done; | 233 goto Done; |
236 } | 234 } |
237 break; | 235 break; |
238 case MKFOURCC('V', 'P', '8', 'L'): | 236 case MKFOURCC('V', 'P', '8', 'L'): |
239 if (alpha_chunks > 0) return PARSE_ERROR; // VP8L has its own alpha | 237 if (alpha_chunks > 0) return PARSE_ERROR; // VP8L has its own alpha |
240 // fall through | 238 // fall through |
241 case MKFOURCC('V', 'P', '8', ' '): | 239 case MKFOURCC('V', 'P', '8', ' '): |
242 if (image_chunks == 0) { | 240 if (image_chunks == 0) { |
243 // Extract the bitstream features, tolerating failures when the data | 241 // Extract the bitstream features, tolerating failures when the data |
244 // is incomplete. | 242 // is incomplete. |
245 WebPBitstreamFeatures features; | 243 WebPBitstreamFeatures features; |
246 const VP8StatusCode vp8_status = | 244 const VP8StatusCode vp8_status = |
247 WebPGetFeatures(mem->buf_ + chunk_start_offset, chunk_size, | 245 WebPGetFeatures(mem->buf_ + chunk_start_offset, chunk_size, |
248 &features); | 246 &features); |
249 if (status == PARSE_NEED_MORE_DATA && | 247 if (status == PARSE_NEED_MORE_DATA && |
250 vp8_status == VP8_STATUS_NOT_ENOUGH_DATA) { | 248 vp8_status == VP8_STATUS_NOT_ENOUGH_DATA) { |
251 return PARSE_NEED_MORE_DATA; | 249 return PARSE_NEED_MORE_DATA; |
252 } else if (vp8_status != VP8_STATUS_OK) { | 250 } else if (vp8_status != VP8_STATUS_OK) { |
253 // We have enough data, and yet WebPGetFeatures() failed. | 251 // We have enough data, and yet WebPGetFeatures() failed. |
254 return PARSE_ERROR; | 252 return PARSE_ERROR; |
255 } | 253 } |
256 ++image_chunks; | 254 ++image_chunks; |
257 frame->img_components_[0].offset_ = chunk_start_offset; | 255 frame->img_components_[0].offset_ = chunk_start_offset; |
258 frame->img_components_[0].size_ = chunk_size; | 256 frame->img_components_[0].size_ = chunk_size; |
259 frame->width_ = features.width; | 257 frame->width_ = features.width; |
260 frame->height_ = features.height; | 258 frame->height_ = features.height; |
261 if (has_vp8l_alpha != NULL) *has_vp8l_alpha = features.has_alpha; | 259 frame->has_alpha_ |= features.has_alpha; |
fbarchard
2013/08/21 02:00:13
if you dont check the pointer, to enable alpha, th
urvang (Google)
2013/08/21 02:16:51
Nope. StoreFrame() is a static method, so 'frame'
| |
262 frame->frame_num_ = frame_num; | 260 frame->frame_num_ = frame_num; |
263 frame->complete_ = (status == PARSE_OK); | 261 frame->complete_ = (status == PARSE_OK); |
264 Skip(mem, payload_available); | 262 Skip(mem, payload_available); |
265 } else { | 263 } else { |
266 goto Done; | 264 goto Done; |
267 } | 265 } |
268 break; | 266 break; |
269 Done: | 267 Done: |
270 default: | 268 default: |
271 // Restore fourcc/size when moving up one level in parsing. | 269 // Restore fourcc/size when moving up one level in parsing. |
(...skipping 27 matching lines...) Expand all Loading... | |
299 return (*frame == NULL) ? PARSE_ERROR : PARSE_OK; | 297 return (*frame == NULL) ? PARSE_ERROR : PARSE_OK; |
300 } | 298 } |
301 | 299 |
302 // Parse a 'ANMF' chunk and any image bearing chunks that immediately follow. | 300 // Parse a 'ANMF' chunk and any image bearing chunks that immediately follow. |
303 // 'frame_chunk_size' is the previously validated, padded chunk size. | 301 // 'frame_chunk_size' is the previously validated, padded chunk size. |
304 static ParseStatus ParseAnimationFrame( | 302 static ParseStatus ParseAnimationFrame( |
305 WebPDemuxer* const dmux, uint32_t frame_chunk_size) { | 303 WebPDemuxer* const dmux, uint32_t frame_chunk_size) { |
306 const int has_frames = !!(dmux->feature_flags_ & ANIMATION_FLAG); | 304 const int has_frames = !!(dmux->feature_flags_ & ANIMATION_FLAG); |
307 const uint32_t anmf_payload_size = frame_chunk_size - ANMF_CHUNK_SIZE; | 305 const uint32_t anmf_payload_size = frame_chunk_size - ANMF_CHUNK_SIZE; |
308 int added_frame = 0; | 306 int added_frame = 0; |
307 int bits; | |
309 MemBuffer* const mem = &dmux->mem_; | 308 MemBuffer* const mem = &dmux->mem_; |
310 Frame* frame; | 309 Frame* frame; |
311 ParseStatus status = | 310 ParseStatus status = |
312 NewFrame(mem, ANMF_CHUNK_SIZE, frame_chunk_size, &frame); | 311 NewFrame(mem, ANMF_CHUNK_SIZE, frame_chunk_size, &frame); |
313 if (status != PARSE_OK) return status; | 312 if (status != PARSE_OK) return status; |
314 | 313 |
315 frame->x_offset_ = 2 * ReadLE24s(mem); | 314 frame->x_offset_ = 2 * ReadLE24s(mem); |
316 frame->y_offset_ = 2 * ReadLE24s(mem); | 315 frame->y_offset_ = 2 * ReadLE24s(mem); |
317 frame->width_ = 1 + ReadLE24s(mem); | 316 frame->width_ = 1 + ReadLE24s(mem); |
318 frame->height_ = 1 + ReadLE24s(mem); | 317 frame->height_ = 1 + ReadLE24s(mem); |
319 frame->duration_ = ReadLE24s(mem); | 318 frame->duration_ = ReadLE24s(mem); |
320 frame->dispose_method_ = (WebPMuxAnimDispose)(ReadByte(mem) & 1); | 319 bits = ReadByte(mem); |
320 frame->dispose_method_ = | |
321 (bits & 1) ? WEBP_MUX_DISPOSE_BACKGROUND : WEBP_MUX_DISPOSE_NONE; | |
322 frame->blend_method_ = (bits & 2) ? WEBP_MUX_NO_BLEND : WEBP_MUX_BLEND; | |
321 if (frame->width_ * (uint64_t)frame->height_ >= MAX_IMAGE_AREA) { | 323 if (frame->width_ * (uint64_t)frame->height_ >= MAX_IMAGE_AREA) { |
322 free(frame); | 324 free(frame); |
323 return PARSE_ERROR; | 325 return PARSE_ERROR; |
324 } | 326 } |
325 | 327 |
326 // Store a frame only if the animation flag is set there is some data for | 328 // Store a frame only if the animation flag is set there is some data for |
327 // this frame is available. | 329 // this frame is available. |
328 status = StoreFrame(dmux->num_frames_ + 1, anmf_payload_size, mem, frame, | 330 status = StoreFrame(dmux->num_frames_ + 1, anmf_payload_size, mem, frame); |
329 NULL); | |
330 if (status != PARSE_ERROR && has_frames && frame->frame_num_ > 0) { | 331 if (status != PARSE_ERROR && has_frames && frame->frame_num_ > 0) { |
331 added_frame = AddFrame(dmux, frame); | 332 added_frame = AddFrame(dmux, frame); |
332 if (added_frame) { | 333 if (added_frame) { |
333 ++dmux->num_frames_; | 334 ++dmux->num_frames_; |
334 } else { | 335 } else { |
335 status = PARSE_ERROR; | 336 status = PARSE_ERROR; |
336 } | 337 } |
337 } | 338 } |
338 | 339 |
339 if (!added_frame) free(frame); | 340 if (!added_frame) free(frame); |
(...skipping 14 matching lines...) Expand all Loading... | |
354 ParseStatus status = | 355 ParseStatus status = |
355 NewFrame(mem, FRGM_CHUNK_SIZE, fragment_chunk_size, &frame); | 356 NewFrame(mem, FRGM_CHUNK_SIZE, fragment_chunk_size, &frame); |
356 if (status != PARSE_OK) return status; | 357 if (status != PARSE_OK) return status; |
357 | 358 |
358 frame->is_fragment_ = 1; | 359 frame->is_fragment_ = 1; |
359 frame->x_offset_ = 2 * ReadLE24s(mem); | 360 frame->x_offset_ = 2 * ReadLE24s(mem); |
360 frame->y_offset_ = 2 * ReadLE24s(mem); | 361 frame->y_offset_ = 2 * ReadLE24s(mem); |
361 | 362 |
362 // Store a fragment only if the fragments flag is set there is some data for | 363 // Store a fragment only if the fragments flag is set there is some data for |
363 // this fragment is available. | 364 // this fragment is available. |
364 status = StoreFrame(frame_num, frgm_payload_size, mem, frame, NULL); | 365 status = StoreFrame(frame_num, frgm_payload_size, mem, frame); |
365 if (status != PARSE_ERROR && has_fragments && frame->frame_num_ > 0) { | 366 if (status != PARSE_ERROR && has_fragments && frame->frame_num_ > 0) { |
366 added_fragment = AddFrame(dmux, frame); | 367 added_fragment = AddFrame(dmux, frame); |
367 if (!added_fragment) { | 368 if (!added_fragment) { |
368 status = PARSE_ERROR; | 369 status = PARSE_ERROR; |
369 } else { | 370 } else { |
370 dmux->num_frames_ = 1; | 371 dmux->num_frames_ = 1; |
371 } | 372 } |
372 } | 373 } |
373 | 374 |
374 if (!added_fragment) free(frame); | 375 if (!added_fragment) free(frame); |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
417 | 418 |
418 Skip(mem, RIFF_HEADER_SIZE); | 419 Skip(mem, RIFF_HEADER_SIZE); |
419 return 1; | 420 return 1; |
420 } | 421 } |
421 | 422 |
422 static ParseStatus ParseSingleImage(WebPDemuxer* const dmux) { | 423 static ParseStatus ParseSingleImage(WebPDemuxer* const dmux) { |
423 const size_t min_size = CHUNK_HEADER_SIZE; | 424 const size_t min_size = CHUNK_HEADER_SIZE; |
424 MemBuffer* const mem = &dmux->mem_; | 425 MemBuffer* const mem = &dmux->mem_; |
425 Frame* frame; | 426 Frame* frame; |
426 ParseStatus status; | 427 ParseStatus status; |
427 int has_vp8l_alpha = 0; // Frame contains a lossless image with alpha. | |
428 | 428 |
429 if (dmux->frames_ != NULL) return PARSE_ERROR; | 429 if (dmux->frames_ != NULL) return PARSE_ERROR; |
430 if (SizeIsInvalid(mem, min_size)) return PARSE_ERROR; | 430 if (SizeIsInvalid(mem, min_size)) return PARSE_ERROR; |
431 if (MemDataSize(mem) < min_size) return PARSE_NEED_MORE_DATA; | 431 if (MemDataSize(mem) < min_size) return PARSE_NEED_MORE_DATA; |
432 | 432 |
433 frame = (Frame*)calloc(1, sizeof(*frame)); | 433 frame = (Frame*)calloc(1, sizeof(*frame)); |
434 if (frame == NULL) return PARSE_ERROR; | 434 if (frame == NULL) return PARSE_ERROR; |
435 | 435 |
436 // For the single image case we allow parsing of a partial frame, but we need | 436 // For the single image case we allow parsing of a partial frame, but we need |
437 // at least CHUNK_HEADER_SIZE for parsing. | 437 // at least CHUNK_HEADER_SIZE for parsing. |
438 status = StoreFrame(1, CHUNK_HEADER_SIZE, &dmux->mem_, frame, | 438 status = StoreFrame(1, CHUNK_HEADER_SIZE, &dmux->mem_, frame); |
439 &has_vp8l_alpha); | |
440 if (status != PARSE_ERROR) { | 439 if (status != PARSE_ERROR) { |
441 const int has_alpha = !!(dmux->feature_flags_ & ALPHA_FLAG); | 440 const int has_alpha = !!(dmux->feature_flags_ & ALPHA_FLAG); |
442 // Clear any alpha when the alpha flag is missing. | 441 // Clear any alpha when the alpha flag is missing. |
443 if (!has_alpha && frame->img_components_[1].size_ > 0) { | 442 if (!has_alpha && frame->img_components_[1].size_ > 0) { |
444 frame->img_components_[1].offset_ = 0; | 443 frame->img_components_[1].offset_ = 0; |
445 frame->img_components_[1].size_ = 0; | 444 frame->img_components_[1].size_ = 0; |
445 frame->has_alpha_ = 0; | |
446 } | 446 } |
447 | 447 |
448 // Use the frame width/height as the canvas values for non-vp8x files. | 448 // Use the frame width/height as the canvas values for non-vp8x files. |
449 // Also, set ALPHA_FLAG if this is a lossless image with alpha. | 449 // Also, set ALPHA_FLAG if this is a lossless image with alpha. |
450 if (!dmux->is_ext_format_ && frame->width_ > 0 && frame->height_ > 0) { | 450 if (!dmux->is_ext_format_ && frame->width_ > 0 && frame->height_ > 0) { |
451 dmux->state_ = WEBP_DEMUX_PARSED_HEADER; | 451 dmux->state_ = WEBP_DEMUX_PARSED_HEADER; |
452 dmux->canvas_width_ = frame->width_; | 452 dmux->canvas_width_ = frame->width_; |
453 dmux->canvas_height_ = frame->height_; | 453 dmux->canvas_height_ = frame->height_; |
454 dmux->feature_flags_ |= has_vp8l_alpha ? ALPHA_FLAG : 0; | 454 dmux->feature_flags_ |= frame->has_alpha_ ? ALPHA_FLAG : 0; |
455 } | 455 } |
456 AddFrame(dmux, frame); | 456 AddFrame(dmux, frame); |
457 dmux->num_frames_ = 1; | 457 dmux->num_frames_ = 1; |
458 } else { | 458 } else { |
459 free(frame); | 459 free(frame); |
460 } | 460 } |
461 | 461 |
462 return status; | 462 return status; |
463 } | 463 } |
464 | 464 |
(...skipping 364 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
829 assert(first_frame != NULL); | 829 assert(first_frame != NULL); |
830 | 830 |
831 iter->frame_num = first_frame->frame_num_; | 831 iter->frame_num = first_frame->frame_num_; |
832 iter->num_frames = dmux->num_frames_; | 832 iter->num_frames = dmux->num_frames_; |
833 iter->fragment_num = fragment_num; | 833 iter->fragment_num = fragment_num; |
834 iter->num_fragments = num_fragments; | 834 iter->num_fragments = num_fragments; |
835 iter->x_offset = fragment->x_offset_; | 835 iter->x_offset = fragment->x_offset_; |
836 iter->y_offset = fragment->y_offset_; | 836 iter->y_offset = fragment->y_offset_; |
837 iter->width = fragment->width_; | 837 iter->width = fragment->width_; |
838 iter->height = fragment->height_; | 838 iter->height = fragment->height_; |
839 iter->has_alpha = fragment->has_alpha_; | |
839 iter->duration = fragment->duration_; | 840 iter->duration = fragment->duration_; |
840 iter->dispose_method = fragment->dispose_method_; | 841 iter->dispose_method = fragment->dispose_method_; |
842 iter->blend_method = fragment->blend_method_; | |
841 iter->complete = fragment->complete_; | 843 iter->complete = fragment->complete_; |
842 iter->fragment.bytes = payload; | 844 iter->fragment.bytes = payload; |
843 iter->fragment.size = payload_size; | 845 iter->fragment.size = payload_size; |
844 // TODO(jzern): adjust offsets for 'FRGM's embedded in 'ANMF's | 846 // TODO(jzern): adjust offsets for 'FRGM's embedded in 'ANMF's |
845 return 1; | 847 return 1; |
846 } | 848 } |
847 | 849 |
848 static int SetFrame(int frame_num, WebPIterator* const iter) { | 850 static int SetFrame(int frame_num, WebPIterator* const iter) { |
849 const Frame* frame; | 851 const Frame* frame; |
850 const WebPDemuxer* const dmux = (WebPDemuxer*)iter->private_; | 852 const WebPDemuxer* const dmux = (WebPDemuxer*)iter->private_; |
(...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
969 return 0; | 971 return 0; |
970 } | 972 } |
971 | 973 |
972 void WebPDemuxReleaseChunkIterator(WebPChunkIterator* iter) { | 974 void WebPDemuxReleaseChunkIterator(WebPChunkIterator* iter) { |
973 (void)iter; | 975 (void)iter; |
974 } | 976 } |
975 | 977 |
976 #if defined(__cplusplus) || defined(c_plusplus) | 978 #if defined(__cplusplus) || defined(c_plusplus) |
977 } // extern "C" | 979 } // extern "C" |
978 #endif | 980 #endif |
OLD | NEW |