Merge "libwebp/demux: add Frame/Chunk iteration"

This commit is contained in:
pascal massimino 2012-06-13 00:29:29 -07:00 committed by Gerrit Code Review
commit f180df2afd
2 changed files with 278 additions and 6 deletions

View File

@ -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

View File

@ -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)