From b402b1fbb9334f63cc814226dd95801651b043e1 Mon Sep 17 00:00:00 2001 From: James Zern Date: Sun, 10 Jun 2012 19:44:32 +0000 Subject: [PATCH] libwebp/demux: add Frame/Chunk iteration Change-Id: I2da68611b375de48391adcf446df31a93450c7d8 --- src/mux/demux.c | 206 ++++++++++++++++++++++++++++++++++++++++++++++-- src/webp/mux.h | 78 ++++++++++++++++++ 2 files changed, 278 insertions(+), 6 deletions(-) diff --git a/src/mux/demux.c b/src/mux/demux.c index 85a6daec..411befd6 100644 --- a/src/mux/demux.c +++ b/src/mux/demux.c @@ -393,7 +393,7 @@ static ParseStatus ParseSingleImage(WebPDemuxer* const dmux) { if (status != PARSE_ERROR) { const int has_alpha = !!(dmux->feature_flags_ & ALPHA_FLAG); // Clear any alpha when the alpha flag is missing. - if (!has_alpha && frame->img_components_[1].size_) { + if (!has_alpha && frame->img_components_[1].size_ > 0) { frame->img_components_[1].offset_ = 0; frame->img_components_[1].size_ = 0; } @@ -553,21 +553,26 @@ static int IsValidExtendedFormat(const WebPDemuxer* const dmux) { // Check frame properties and if the image is composed of tiles that each // fragment came from a 'TILE'. for (; f != NULL && f->frame_num_ == cur_frame_set; f = f->next_) { + const ChunkData* const image = f->img_components_; + const ChunkData* const alpha = f->img_components_ + 1; + if (!has_tiles && f->is_tile_) return 0; if (!has_frames && f->frame_num_ > 1) return 0; if (f->x_offset_ < 0 || f->y_offset_ < 0) return 0; if (f->complete_) { - const ChunkData* const img_components_ = f->img_components_; - - if (!img_components_[0].size_ && !img_components_[1].size_) return 0; + if (alpha->size_ == 0 && image->size_ == 0) return 0; // Ensure alpha precedes image bitstream. - if (img_components_[1].size_ && - img_components_[1].offset_ > img_components_[0].offset_) { + if (alpha->size_ > 0 && alpha->offset_ > image->offset_) { return 0; } if (f->width_ <= 0 || f->height_ <= 0) return 0; } else { + // Ensure alpha precedes image bitstream. + if (alpha->size_ > 0 && image->size_ > 0 && + alpha->offset_ > image->offset_) { + return 0; + } // There shouldn't be any frames after an incomplete one. if (f->next_ != NULL) return 0; } @@ -665,6 +670,195 @@ uint32_t WebPDemuxGetI(const WebPDemuxer* const dmux, return 0; } +// ----------------------------------------------------------------------------- +// Frame iteration + +// Find the first 'frame_num' frame. There may be multiple in a tiled frame. +static const Frame* GetFrame(const WebPDemuxer* const dmux, int frame_num) { + const Frame* f; + for (f = dmux->frames_; f != NULL; f = f->next_) { + if (frame_num == f->frame_num_) break; + } + return f; +} + +// Returns tile 'tile_num' and the total count. +static const Frame* GetTile( + const Frame* const frame_set, int tile_num, int* const count) { + const int this_frame = frame_set->frame_num_; + const Frame* f = frame_set; + const Frame* tile = NULL; + int total; + + for (total = 0; f != NULL && f->frame_num_ == this_frame; f = f->next_) { + if (++total == tile_num) tile = f; + } + *count = total; + return tile; +} + +static const uint8_t* GetFramePayload(const uint8_t* const mem_buf, + const Frame* const frame, + size_t* const data_size) { + *data_size = 0; + if (frame != NULL) { + const ChunkData* const image = frame->img_components_; + const ChunkData* const alpha = frame->img_components_ + 1; + size_t start_offset = image->offset_; + *data_size = image->size_; + + // if alpha exists it precedes image, update the size allowing for + // intervening chunks. + if (alpha->size_ > 0) { + const size_t inter_size = (image->offset_ > 0) + ? image->offset_ - (alpha->offset_ + alpha->size_) + : 0; + start_offset = alpha->offset_; + *data_size += alpha->size_ + inter_size; + } + return mem_buf + start_offset; + } + return NULL; +} + +// Create a whole 'frame' from VP8 (+ alpha) or lossless. +static int SynthesizeFrame(const WebPDemuxer* const dmux, + const Frame* const first_frame, + int tile_num, WebPIterator* const iter) { + const uint8_t* const mem_buf = dmux->mem_.buf_; + int num_tiles; + size_t payload_size = 0; + const Frame* const tile = GetTile(first_frame, tile_num, &num_tiles); + const uint8_t* const payload = GetFramePayload(mem_buf, tile, &payload_size); + if (payload == NULL) return 0; + + iter->frame_num_ = first_frame->frame_num_; + iter->num_frames_ = dmux->num_frames_; + iter->tile_num_ = tile_num; + iter->num_tiles_ = num_tiles; + iter->x_offset_ = tile->x_offset_; + iter->y_offset_ = tile->y_offset_; + iter->width_ = tile->width_; + iter->height_ = tile->height_; + iter->duration_ = tile->duration_; + iter->complete_ = tile->complete_; + iter->tile_.bytes_ = payload; + iter->tile_.size_ = payload_size; + // TODO(jzern): adjust offsets for 'TILE's embedded in 'FRM 's + return 1; +} + +static int SetFrame(int frame_num, WebPIterator* const iter) { + const Frame* frame; + const WebPDemuxer* const dmux = (WebPDemuxer*)iter->private_; + if (dmux == NULL || frame_num < 0) return 0; + if (frame_num > dmux->num_frames_) return 0; + if (frame_num == 0) frame_num = dmux->num_frames_; + + frame = GetFrame(dmux, frame_num); + return SynthesizeFrame(dmux, frame, 1, iter); +} + +int WebPDemuxGetFrame(const WebPDemuxer* const dmux, + int frame, WebPIterator* const iter) { + if (iter == NULL) return 0; + + memset(iter, 0, sizeof(*iter)); + iter->private_ = (void*)dmux; + return SetFrame(frame, iter); +} + +int WebPDemuxSetFrame(WebPIterator* const iter, int frame) { + if (iter == NULL) return 0; + return SetFrame(frame, iter); +} + +int WebPDemuxSetTile(WebPIterator* const iter, int tile) { + if (iter != NULL && iter->private_ != NULL && tile > 0) { + const WebPDemuxer* const dmux = (WebPDemuxer*)iter->private_; + const Frame* const frame = GetFrame(dmux, iter->frame_num_); + if (frame == NULL) return 0; + + return SynthesizeFrame(dmux, frame, tile, iter); + } + return 0; +} + +void WebPDemuxReleaseIterator(WebPIterator* const iter) { + (void)iter; +} + +// ----------------------------------------------------------------------------- +// Chunk iteration + +static int ChunkCount(const WebPDemuxer* const dmux, const char fourcc[4]) { + const uint8_t* const mem_buf = dmux->mem_.buf_; + const Chunk* c; + int count = 0; + for (c = dmux->chunks_; c != NULL; c = c->next_) { + const uint8_t* const header = mem_buf + c->data_.offset_; + if (!memcmp(header, fourcc, TAG_SIZE)) ++count; + } + return count; +} + +static const Chunk* GetChunk(const WebPDemuxer* const dmux, + const char fourcc[4], int chunk_num) { + const uint8_t* const mem_buf = dmux->mem_.buf_; + const Chunk* c; + int count = 0; + for (c = dmux->chunks_; c != NULL; c = c->next_) { + const uint8_t* const header = mem_buf + c->data_.offset_; + if (!memcmp(header, fourcc, TAG_SIZE)) ++count; + if (count == chunk_num) break; + } + return c; +} + +static int SetChunk(const char fourcc[4], int chunk_num, + WebPChunkIterator* const iter) { + const WebPDemuxer* const dmux = (WebPDemuxer*)iter->private_; + int count; + + if (dmux == NULL || fourcc == NULL || chunk_num < 0) return 0; + count = ChunkCount(dmux, fourcc); + if (count == 0) return 0; + if (chunk_num == 0) chunk_num = count; + + if (chunk_num <= count) { + const uint8_t* const mem_buf = dmux->mem_.buf_; + const Chunk* const chunk = GetChunk(dmux, fourcc, chunk_num); + iter->chunk_.bytes_ = mem_buf + chunk->data_.offset_ + CHUNK_HEADER_SIZE; + iter->chunk_.size_ = chunk->data_.size_ - CHUNK_HEADER_SIZE; + iter->num_chunks_ = count; + iter->chunk_num_ = chunk_num; + return 1; + } + return 0; +} + +int WebPDemuxGetChunk(const WebPDemuxer* const dmux, + const char fourcc[4], int chunk_num, + WebPChunkIterator* const iter) { + if (iter == NULL) return 0; + + memset(iter, 0, sizeof(*iter)); + iter->private_ = (void*)dmux; + return SetChunk(fourcc, chunk_num, iter); +} + +int WebPDemuxSetChunk(WebPChunkIterator* const iter, int chunk) { + if (iter != NULL) { + const uint8_t* const header = iter->chunk_.bytes_ - CHUNK_HEADER_SIZE; + return SetChunk((const char*)header, chunk, iter); + } + return 0; +} + +void WebPDemuxReleaseChunkIterator(WebPChunkIterator* const iter) { + (void)iter; +} + #if defined(__cplusplus) || defined(c_plusplus) } // extern "C" #endif diff --git a/src/webp/mux.h b/src/webp/mux.h index 20321676..ac2acf81 100644 --- a/src/webp/mux.h +++ b/src/webp/mux.h @@ -518,6 +518,84 @@ typedef enum { WEBP_EXTERN(uint32_t) WebPDemuxGetI( const WebPDemuxer* const dmux, WebPFormatFeature feature); +//------------------------------------------------------------------------------ +// Frame iteration. + +typedef struct { + int frame_num_; + int num_frames_; + int tile_num_; + int num_tiles_; + int x_offset_, y_offset_; // offset relative to the canvas. + int width_, height_; // dimensions of this frame or tile. + int duration_; // display duration in milliseconds. + int complete_; // true if 'tile_' contains a full frame. partial images may + // still be decoded with the WebP incremental decoder. + WebPData tile_; // The frame or tile given by 'frame_num_' and 'tile_num_'. + + void* private_; +} WebPIterator; + +// Retrieves frame 'frame_number' from 'dmux'. +// 'iter->tile_' points to the first tile on return from this function. +// Individual tiles may be extracted using WebPDemuxSetTile(). +// Setting 'frame_number' equal to 0 will return the last frame of the image. +// Returns false if 'dmux' is NULL or frame 'frame_number' is not present. +// Call WebPDemuxReleaseIterator() when use of the iterator is complete. +// NOTE: 'dmux' must persist for the lifetime of 'iter'. +WEBP_EXTERN(int) WebPDemuxGetFrame( + const WebPDemuxer* const dmux, int frame_number, WebPIterator* const iter); + +// Sets 'iter->tile_' to point to frame number 'frame_number'. +// Returns true on success, false otherwise. +WEBP_EXTERN(int) WebPDemuxSetFrame(WebPIterator* const iter, int frame_number); + +// Sets 'iter->tile_' to reflect tile number 'tile_number'. +// Returns true if tile 'tile_number' is present, false otherwise. +WEBP_EXTERN(int) WebPDemuxSetTile(WebPIterator* const iter, int tile_number); + +// Releases any memory associated with 'iter'. +// Must be called before destroying the associated WebPDemuxer with +// WebPDemuxDelete(). +WEBP_EXTERN(void) WebPDemuxReleaseIterator(WebPIterator* const iter); + +//------------------------------------------------------------------------------ +// Chunk iteration. + +typedef struct { + // The current and total number of chunks with the fourcc given to + // WebPDemuxGetChunk(). + int chunk_num_; + int num_chunks_; + // The payload of the chunk. + WebPData chunk_; + + void* private_; +} WebPChunkIterator; + +// Retrieves the 'chunk_number' instance of the chunk with id 'fourcc' from +// 'dmux'. +// 'fourcc' is a character array containing the fourcc of the chunk to return, +// e.g., "ICCP", "META", "EXIF", etc. +// Setting 'chunk_number' equal to 0 will return the last chunk in a set. +// Returns true if the chunk is found, false otherwise. Image related chunk +// payloads are accessed through WebPDemuxGetFrame() and related functions. +// Call WebPDemuxReleaseChunkIterator() when use of the iterator is complete. +// NOTE: 'dmux' must persist for the lifetime of the iterator. +WEBP_EXTERN(int) WebPDemuxGetChunk(const WebPDemuxer* const dmux, + const char fourcc[4], int chunk_number, + WebPChunkIterator* const iter); + +// Sets 'iter->chunk_' to point to chunk 'chunk_number'. +// Returns true on success, false otherwise. +WEBP_EXTERN(int) WebPDemuxSetChunk(WebPChunkIterator* const iter, + int chunk_number); + +// Releases any memory associated with 'iter'. +// Must be called before destroying the associated WebPDemuxer with +// WebPDemuxDelete(). +WEBP_EXTERN(void) WebPDemuxReleaseChunkIterator(WebPChunkIterator* const iter); + //------------------------------------------------------------------------------ #if defined(__cplusplus) || defined(c_plusplus)