Merge changes Ibeccffc3,Id1585b16

* changes:
  Use 'frgm' instead of 'tile' in webpmux parameters
  Design change in ANMF and FRGM chunks:
This commit is contained in:
pascal massimino 2012-11-06 22:43:43 -08:00 committed by Gerrit Code Review
commit d2ad4450ce
11 changed files with 356 additions and 261 deletions

View File

@ -8,9 +8,9 @@ Description:
============
WebP Mux: library to create a WebP container object for features like
color profile, metadata, animation and tiling. A reference command line
tool 'webpmux' and WebP container specification 'doc/webp-container-spec.txt'
are also provided in this package.
color profile, metadata, animation and fragmented images. A reference command
line tool 'webpmux' and WebP container specification
'doc/webp-container-spec.txt' are also provided in this package.
WebP Mux tool:
==============
@ -25,7 +25,7 @@ A list of options is available using the -help command line flag:
Usage: webpmux -get GET_OPTIONS INPUT -o OUTPUT
webpmux -set SET_OPTIONS INPUT -o OUTPUT
webpmux -strip STRIP_OPTIONS INPUT -o OUTPUT
webpmux -tile TILE_OPTIONS [-tile...] -o OUTPUT
webpmux -frgm FRAGMENT_OPTIONS [-frgm...] -o OUTPUT
webpmux -frame FRAME_OPTIONS [-frame...] -loop LOOP_COUNT -o OUTPUT
webpmux -info INPUT
webpmux [-h|-help]
@ -35,7 +35,7 @@ GET_OPTIONS:
icc Get ICC profile.
exif Get EXIF metadata.
xmp Get XMP metadata.
tile n Get nth tile.
frgm n Get nth fragment.
frame n Get nth frame.
SET_OPTIONS:
@ -53,11 +53,11 @@ STRIP_OPTIONS:
exif Strip EXIF metadata.
xmp Strip XMP metadata.
TILE_OPTIONS(i):
Create tiled image.
FRAGMENT_OPTIONS(i):
Create fragmented image.
file_i +xi+yi
where: 'file_i' is the i'th tile (WebP format),
'xi','yi' specify the image offset for this tile.
where: 'file_i' is the i'th fragment (WebP format),
'xi','yi' specify the image offset for this fragment.
FRAME_OPTIONS(i):
Create animation.
@ -75,8 +75,8 @@ WebP Mux API:
==============
The WebP Mux API contains methods for adding data to and reading data from
WebPMux (a WebP container object). This API currently supports XMP/EXIF
metadata, ICC profile, animation and tiling. Other features will be added
in subsequent releases.
metadata, ICC profile, animation and fragmented images. Other features
will be added in subsequent releases.
Example#1 (pseudo code): Creating a WebPMux object with image data, color
profile and XMP metadata.

View File

@ -183,7 +183,7 @@ static int Decode(int* const duration) {
"Frame offsets not yet supported! Forcing offset to 0,0\n");
}
output_buffer->colorspace = MODE_RGBA;
ok = (WebPDecode(iter->tile.bytes, iter->tile.size,
ok = (WebPDecode(iter->fragment.bytes, iter->fragment.size,
config) == VP8_STATUS_OK);
if (!ok) {
fprintf(stderr, "Decoding of frame #%d failed!\n", iter->frame_num);
@ -298,8 +298,8 @@ int main(int argc, char *argv[]) {
goto Error;
}
if (WebPDemuxGetI(kParams.dmux, WEBP_FF_FORMAT_FLAGS) & TILE_FLAG) {
fprintf(stderr, "Tiling is not supported for now!\n");
if (WebPDemuxGetI(kParams.dmux, WEBP_FF_FORMAT_FLAGS) & FRAGMENTS_FLAG) {
fprintf(stderr, "Image fragments are not supported for now!\n");
goto Error;
}

View File

@ -17,11 +17,11 @@
/* Usage examples:
Create container WebP file:
webpmux -tile tile_1.webp +0+0 \
-tile tile_2.webp +960+0 \
-tile tile_3.webp +0+576 \
-tile tile_4.webp +960+576 \
-o out_tile_container.webp
webpmux -frgm fragment_1.webp +0+0 \
-frgm fragment_2.webp +960+0 \
-frgm fragment_3.webp +0+576 \
-frgm fragment_4.webp +960+576 \
-o out_fragment_container.webp
webpmux -frame anim_1.webp +0+0+0 \
-frame anim_2.webp +25+25+100 \
@ -35,7 +35,7 @@
webpmux -set xmp image_metadata.xmp in.webp -o out_xmp_container.webp
Extract relevant data from WebP container file:
webpmux -get tile n in.webp -o out_tile.webp
webpmux -get frgm n in.webp -o out_fragment.webp
webpmux -get frame n in.webp -o out_frame.webp
webpmux -get icc in.webp -o image_profile.icc
webpmux -get exif in.webp -o image_metadata.exif
@ -87,8 +87,8 @@ typedef enum {
FEATURE_EXIF,
FEATURE_XMP,
FEATURE_ICCP,
FEATURE_FRM,
FEATURE_TILE,
FEATURE_ANMF,
FEATURE_FRGM,
LAST_FEATURE
} FeatureType;
@ -98,7 +98,7 @@ static const char* const kFourccList[LAST_FEATURE] = {
static const char* const kDescriptions[LAST_FEATURE] = {
NULL, "EXIF metadata", "XMP metadata", "ICC profile",
"Animation frame", "Tile fragment"
"Animation frame", "Image fragment"
};
typedef struct {
@ -197,17 +197,17 @@ static WebPMuxError DisplayInfo(const WebPMux* mux) {
// Print the features present.
printf("Features present:");
if (flag & ANIMATION_FLAG) printf(" animation");
if (flag & TILE_FLAG) printf(" tiling");
if (flag & FRAGMENTS_FLAG) printf(" image fragments");
if (flag & ICCP_FLAG) printf(" icc profile");
if (flag & EXIF_FLAG) printf(" EXIF metadata");
if (flag & XMP_FLAG) printf(" XMP metadata");
if (flag & ALPHA_FLAG) printf(" transparency");
printf("\n");
if ((flag & ANIMATION_FLAG) || (flag & TILE_FLAG)) {
if ((flag & ANIMATION_FLAG) || (flag & FRAGMENTS_FLAG)) {
const int is_anim = !!(flag & ANIMATION_FLAG);
const WebPChunkId id = is_anim ? WEBP_CHUNK_ANMF : WEBP_CHUNK_FRGM;
const char* const type_str = is_anim ? "frame" : "tile";
const char* const type_str = is_anim ? "frame" : "fragment";
int nFrames;
if (is_anim) {
@ -259,7 +259,7 @@ static WebPMuxError DisplayInfo(const WebPMux* mux) {
printf("Size of the XMP metadata: %zu\n", xmp.size);
}
if ((flag & ALPHA_FLAG) && !(flag & (ANIMATION_FLAG | TILE_FLAG))) {
if ((flag & ALPHA_FLAG) && !(flag & (ANIMATION_FLAG | FRAGMENTS_FLAG))) {
WebPMuxFrameInfo image;
err = WebPMuxGetFrame(mux, 1, &image);
RETURN_IF_ERROR("Failed to retrieve the image\n");
@ -273,7 +273,7 @@ static void PrintHelp(void) {
printf("Usage: webpmux -get GET_OPTIONS INPUT -o OUTPUT\n");
printf(" webpmux -set SET_OPTIONS INPUT -o OUTPUT\n");
printf(" webpmux -strip STRIP_OPTIONS INPUT -o OUTPUT\n");
printf(" webpmux -tile TILE_OPTIONS [-tile...] -o OUTPUT\n");
printf(" webpmux -frgm FRAGMENT_OPTIONS [-frgm...] -o OUTPUT\n");
printf(" webpmux -frame FRAME_OPTIONS [-frame...]");
printf(" -loop LOOP_COUNT -o OUTPUT\n");
printf(" webpmux -info INPUT\n");
@ -285,7 +285,7 @@ static void PrintHelp(void) {
printf(" icc Get ICC profile.\n");
printf(" exif Get EXIF metadata.\n");
printf(" xmp Get XMP metadata.\n");
printf(" tile n Get nth tile.\n");
printf(" frgm n Get nth fragment.\n");
printf(" frame n Get nth frame.\n");
printf("\n");
@ -306,11 +306,12 @@ static void PrintHelp(void) {
printf(" xmp Strip XMP metadata.\n");
printf("\n");
printf("TILE_OPTIONS(i):\n");
printf(" Create tiled image.\n");
printf("FRAGMENT_OPTIONS(i):\n");
printf(" Create fragmented image.\n");
printf(" file_i +xi+yi\n");
printf(" where: 'file_i' is the i'th tile (WebP format),\n");
printf(" 'xi','yi' specify the image offset for this tile.\n");
printf(" where: 'file_i' is the i'th fragment (WebP format),\n");
printf(" 'xi','yi' specify the image offset for this fragment."
"\n");
printf("\n");
printf("FRAME_OPTIONS(i):\n");
@ -382,7 +383,7 @@ static int ParseFrameArgs(const char* args, WebPMuxFrameInfo* const info) {
&info->x_offset, &info->y_offset, &info->duration) == 3);
}
static int ParseTileArgs(const char* args, WebPMuxFrameInfo* const info) {
static int ParseFragmentArgs(const char* args, WebPMuxFrameInfo* const info) {
return (sscanf(args, "+%d+%d", &info->x_offset, &info->y_offset) == 2);
}
@ -406,7 +407,7 @@ static void DeleteConfig(WebPMuxConfig* config) {
static int ValidateCommandLine(int argc, const char* argv[],
int* num_feature_args) {
int num_frame_args;
int num_tile_args;
int num_frgm_args;
int num_loop_args;
int ok = 1;
@ -432,7 +433,7 @@ static int ValidateCommandLine(int argc, const char* argv[],
// Compound checks.
num_frame_args = CountOccurrences(argv, argc, "-frame");
num_tile_args = CountOccurrences(argv, argc, "-tile");
num_frgm_args = CountOccurrences(argv, argc, "-frgm");
num_loop_args = CountOccurrences(argv, argc, "-loop");
if (num_loop_args > 1) {
@ -443,21 +444,21 @@ static int ValidateCommandLine(int argc, const char* argv[],
ERROR_GOTO1("ERROR: Both frames and loop count have to be specified.\n",
ErrValidate);
}
if (num_frame_args > 0 && num_tile_args > 0) {
ERROR_GOTO1("ERROR: Only one of frames & tiles can be specified at a time."
"\n", ErrValidate);
if (num_frame_args > 0 && num_frgm_args > 0) {
ERROR_GOTO1("ERROR: Only one of frames & fragments can be specified at a "
"time.\n", ErrValidate);
}
assert(ok == 1);
if (num_frame_args == 0 && num_tile_args == 0) {
if (num_frame_args == 0 && num_frgm_args == 0) {
// Single argument ('set' action for ICCP/EXIF/XMP, OR a 'get' action).
*num_feature_args = 1;
} else {
// Multiple arguments ('set' action for animation or tiling).
// Multiple arguments ('set' action for animation or fragmented image).
if (num_frame_args > 0) {
*num_feature_args = num_frame_args + num_loop_args;
} else {
*num_feature_args = num_tile_args;
*num_feature_args = num_frgm_args;
}
}
@ -522,8 +523,8 @@ static int ParseCommandLine(int argc, const char* argv[],
} else {
ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
}
if (FEATURETYPE_IS_NIL || feature->type_ == FEATURE_FRM) {
feature->type_ = FEATURE_FRM;
if (FEATURETYPE_IS_NIL || feature->type_ == FEATURE_ANMF) {
feature->type_ = FEATURE_ANMF;
} else {
ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
}
@ -539,8 +540,8 @@ static int ParseCommandLine(int argc, const char* argv[],
} else {
ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
}
if (FEATURETYPE_IS_NIL || feature->type_ == FEATURE_FRM) {
feature->type_ = FEATURE_FRM;
if (FEATURETYPE_IS_NIL || feature->type_ == FEATURE_ANMF) {
feature->type_ = FEATURE_ANMF;
} else {
ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
}
@ -548,15 +549,15 @@ static int ParseCommandLine(int argc, const char* argv[],
arg->params_ = argv[i + 1];
++feature_arg_index;
i += 2;
} else if (!strcmp(argv[i], "-tile")) {
} else if (!strcmp(argv[i], "-frgm")) {
CHECK_NUM_ARGS_LESS(3, ErrParse);
if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) {
config->action_type_ = ACTION_SET;
} else {
ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
}
if (FEATURETYPE_IS_NIL || feature->type_ == FEATURE_TILE) {
feature->type_ = FEATURE_TILE;
if (FEATURETYPE_IS_NIL || feature->type_ == FEATURE_FRGM) {
feature->type_ = FEATURE_FRGM;
} else {
ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
}
@ -607,11 +608,11 @@ static int ParseCommandLine(int argc, const char* argv[],
++i;
}
} else if ((!strcmp(argv[i], "frame") ||
!strcmp(argv[i], "tile")) &&
!strcmp(argv[i], "frgm")) &&
(config->action_type_ == ACTION_GET)) {
CHECK_NUM_ARGS_LESS(2, ErrParse);
feature->type_ = (!strcmp(argv[i], "frame")) ? FEATURE_FRM :
FEATURE_TILE;
feature->type_ = (!strcmp(argv[i], "frame")) ? FEATURE_ANMF :
FEATURE_FRGM;
arg->params_ = argv[i + 1];
++feature_arg_index;
i += 2;
@ -649,8 +650,8 @@ static int ValidateConfig(WebPMuxConfig* config) {
if (config->input_ == NULL) {
if (config->action_type_ != ACTION_SET) {
ERROR_GOTO1("ERROR: No input file specified.\n", ErrValidate2);
} else if (feature->type_ != FEATURE_FRM &&
feature->type_ != FEATURE_TILE) {
} else if (feature->type_ != FEATURE_ANMF &&
feature->type_ != FEATURE_FRGM) {
ERROR_GOTO1("ERROR: No input file specified.\n", ErrValidate2);
}
}
@ -708,8 +709,8 @@ static int InitializeConfig(int argc, const char* argv[],
//------------------------------------------------------------------------------
// Processing.
static int GetFrameTile(const WebPMux* mux,
const WebPMuxConfig* config, int isFrame) {
static int GetFrameFragment(const WebPMux* mux,
const WebPMuxConfig* config, int isFrame) {
WebPMuxError err = WEBP_MUX_OK;
WebPMux* mux_single = NULL;
long num = 0;
@ -720,7 +721,7 @@ static int GetFrameTile(const WebPMux* mux,
num = strtol(config->feature_.args_[0].params_, NULL, 10);
if (num < 0) {
ERROR_GOTO1("ERROR: Frame/Tile index must be non-negative.\n", ErrGet);
ERROR_GOTO1("ERROR: Frame/Fragment index must be non-negative.\n", ErrGet);
}
err = WebPMuxGetFrame(mux, num, &info);
@ -764,10 +765,10 @@ static int Process(const WebPMuxConfig* config) {
ok = CreateMux(config->input_, &mux);
if (!ok) goto Err2;
switch (feature->type_) {
case FEATURE_FRM:
case FEATURE_TILE:
ok = GetFrameTile(mux, config,
(feature->type_ == FEATURE_FRM) ? 1 : 0);
case FEATURE_ANMF:
case FEATURE_FRGM:
ok = GetFrameFragment(mux, config,
(feature->type_ == FEATURE_ANMF) ? 1 : 0);
break;
case FEATURE_ICCP:
@ -789,7 +790,7 @@ static int Process(const WebPMuxConfig* config) {
case ACTION_SET:
switch (feature->type_) {
case FEATURE_FRM:
case FEATURE_ANMF:
mux = WebPMuxNew();
if (mux == NULL) {
ERROR_GOTO2("ERROR (%s): Could not allocate a mux object.\n",
@ -829,27 +830,28 @@ static int Process(const WebPMuxConfig* config) {
}
break;
case FEATURE_TILE:
case FEATURE_FRGM:
mux = WebPMuxNew();
if (mux == NULL) {
ERROR_GOTO2("ERROR (%s): Could not allocate a mux object.\n",
ErrorString(WEBP_MUX_MEMORY_ERROR), Err2);
}
for (index = 0; index < feature->arg_count_; ++index) {
WebPMuxFrameInfo tile;
WebPMuxFrameInfo frgm;
frgm.id = WEBP_CHUNK_FRGM;
ok = ReadFileToWebPData(feature->args_[index].filename_,
&tile.bitstream);
&frgm.bitstream);
if (!ok) goto Err2;
ok = ParseTileArgs(feature->args_[index].params_, &tile);
ok = ParseFragmentArgs(feature->args_[index].params_, &frgm);
if (!ok) {
WebPDataClear(&tile.bitstream);
ERROR_GOTO1("ERROR: Could not parse tile properties.\n", Err2);
WebPDataClear(&frgm.bitstream);
ERROR_GOTO1("ERROR: Could not parse fragment properties.\n",
Err2);
}
tile.id = WEBP_CHUNK_FRGM;
err = WebPMuxPushFrame(mux, &tile, 1);
WebPDataClear(&tile.bitstream);
err = WebPMuxPushFrame(mux, &frgm, 1);
WebPDataClear(&frgm.bitstream);
if (err != WEBP_MUX_OK) {
ERROR_GOTO3("ERROR (%s): Could not add a tile at index %d.\n",
ERROR_GOTO3("ERROR (%s): Could not add a fragment at index %d.\n",
ErrorString(err), index, Err2);
}
}

View File

@ -21,9 +21,9 @@ webpmux \- command line tool to create WebP Mux/container file.
.B \-o
.I OUTPUT
.br
.B webpmux \-tile
.I TILE_OPTIONS
.B [\-tile...] \-o
.B webpmux \-frgm
.I FRAGMENT_OPTIONS
.B [\-frgm...] \-o
.I OUTPUT
.br
.B webpmux \-frame
@ -56,8 +56,8 @@ Get EXIF metadata.
.B xmp
Get XMP metadata.
.TP
.B tile n
Get nth tile.
.B frgm n
Get nth fragment.
.TP
.B frame n
Get nth frame.
@ -90,11 +90,11 @@ Strip EXIF metadata.
.B xmp
Strip XMP metadata.
.SS TILE_OPTIONS (\-tile)
.SS FRAGMENT_OPTIONS (\-frgm)
.TP
.B file_i +xi+yi
Where: 'file_i' is the i'th tile (WebP format) and 'xi','yi' specify the image
offset for this tile.
Where: 'file_i' is the i'th fragment (WebP format) and 'xi','yi' specify the
image offset for this fragment.
.SS FRAME_OPTIONS (\-frame)
.TP

View File

@ -40,8 +40,8 @@ extern "C" {
// 20..23 VP8X flags bit-map corresponding to the chunk-types present.
// 24..26 Width of the Canvas Image.
// 27..29 Height of the Canvas Image.
// There can be extra chunks after the "VP8X" chunk (ICCP, TILE, FRM, VP8,
// XMP, EXIF ...)
// There can be extra chunks after the "VP8X" chunk (ICCP, FRGM, ANMF, VP8,
// VP8L, XMP, EXIF ...)
// All sizes are in little-endian order.
// Note: chunk data size must be padded to multiple of 2 when written.

View File

@ -39,8 +39,8 @@ typedef struct Frame {
int x_offset_, y_offset_;
int width_, height_;
int duration_;
int is_tile_; // this is an image fragment from a tile.
int frame_num_; // the referent frame number for use in assembling tiles.
int is_fragment_; // this is a frame fragment (and not a full frame).
int frame_num_; // the referent frame number for use in assembling fragments.
int complete_; // img_components_ contains a full image.
ChunkData img_components_[2]; // 0=VP8{,L} 1=ALPH
struct Frame* next_;
@ -194,11 +194,11 @@ static int AddFrame(WebPDemuxer* const dmux, Frame* const frame) {
}
// Store image bearing chunks to 'frame'.
static ParseStatus StoreFrame(int frame_num, MemBuffer* const mem,
Frame* const frame) {
static ParseStatus StoreFrame(int frame_num, uint32_t min_size,
MemBuffer* const mem, Frame* const frame) {
int alpha_chunks = 0;
int image_chunks = 0;
int done = (MemDataSize(mem) < CHUNK_HEADER_SIZE);
int done = (MemDataSize(mem) < min_size);
ParseStatus status = PARSE_OK;
if (done) return PARSE_NEED_MORE_DATA;
@ -275,10 +275,10 @@ static ParseStatus StoreFrame(int frame_num, MemBuffer* const mem,
// Returns PARSE_OK on success with *frame pointing to the new Frame.
// Returns PARSE_NEED_MORE_DATA with insufficient data, PARSE_ERROR otherwise.
static ParseStatus NewFrame(const MemBuffer* const mem,
uint32_t min_size, uint32_t expected_size,
uint32_t actual_size, Frame** frame) {
uint32_t min_size, uint32_t actual_size,
Frame** frame) {
if (SizeIsInvalid(mem, min_size)) return PARSE_ERROR;
if (actual_size < expected_size) return PARSE_ERROR;
if (actual_size < min_size) return PARSE_ERROR;
if (MemDataSize(mem) < min_size) return PARSE_NEED_MORE_DATA;
*frame = (Frame*)calloc(1, sizeof(**frame));
@ -290,12 +290,14 @@ static ParseStatus NewFrame(const MemBuffer* const mem,
static ParseStatus ParseFrame(
WebPDemuxer* const dmux, uint32_t frame_chunk_size) {
const int has_frames = !!(dmux->feature_flags_ & ANIMATION_FLAG);
const uint32_t min_size = frame_chunk_size + CHUNK_HEADER_SIZE;
const uint32_t padding = (ANMF_CHUNK_SIZE & 1);
const uint32_t anmf_payload_size =
frame_chunk_size - (ANMF_CHUNK_SIZE + padding);
int added_frame = 0;
MemBuffer* const mem = &dmux->mem_;
Frame* frame;
ParseStatus status =
NewFrame(mem, min_size, ANMF_CHUNK_SIZE, frame_chunk_size, &frame);
NewFrame(mem, ANMF_CHUNK_SIZE, frame_chunk_size, &frame);
if (status != PARSE_OK) return status;
frame->x_offset_ = 2 * GetLE24s(mem);
@ -303,14 +305,14 @@ static ParseStatus ParseFrame(
frame->width_ = 1 + GetLE24s(mem);
frame->height_ = 1 + GetLE24s(mem);
frame->duration_ = 1 + GetLE24s(mem);
Skip(mem, frame_chunk_size - ANMF_CHUNK_SIZE); // skip any trailing data.
Skip(mem, padding);
if (frame->width_ * (uint64_t)frame->height_ >= MAX_IMAGE_AREA) {
return PARSE_ERROR;
}
// Store a (potentially partial) frame only if the animation flag is set
// and there is some data in 'frame'.
status = StoreFrame(dmux->num_frames_ + 1, mem, frame);
// Store a frame only if the animation flag is set and all data for this frame
// is available.
status = StoreFrame(dmux->num_frames_ + 1, anmf_payload_size, mem, frame);
if (status != PARSE_ERROR && has_frames && frame->frame_num_ > 0) {
added_frame = AddFrame(dmux, frame);
if (added_frame) {
@ -325,33 +327,36 @@ static ParseStatus ParseFrame(
}
// Parse a 'FRGM' chunk and any image bearing chunks that immediately follow.
// 'tile_chunk_size' is the previously validated, padded chunk size.
static ParseStatus ParseTile(WebPDemuxer* const dmux,
uint32_t tile_chunk_size) {
const int has_tiles = !!(dmux->feature_flags_ & TILE_FLAG);
const uint32_t min_size = tile_chunk_size + CHUNK_HEADER_SIZE;
int added_tile = 0;
// 'fragment_chunk_size' is the previously validated, padded chunk size.
static ParseStatus ParseFragment(WebPDemuxer* const dmux,
uint32_t fragment_chunk_size) {
const int has_fragments = !!(dmux->feature_flags_ & FRAGMENTS_FLAG);
const uint32_t padding = (FRGM_CHUNK_SIZE & 1);
const uint32_t frgm_payload_size =
fragment_chunk_size - (FRGM_CHUNK_SIZE + padding);
int added_fragment = 0;
MemBuffer* const mem = &dmux->mem_;
Frame* frame;
ParseStatus status =
NewFrame(mem, min_size, FRGM_CHUNK_SIZE, tile_chunk_size, &frame);
NewFrame(mem, FRGM_CHUNK_SIZE, fragment_chunk_size, &frame);
if (status != PARSE_OK) return status;
frame->is_tile_ = 1;
frame->is_fragment_ = 1;
frame->x_offset_ = 2 * GetLE24s(mem);
frame->y_offset_ = 2 * GetLE24s(mem);
Skip(mem, tile_chunk_size - FRGM_CHUNK_SIZE); // skip any trailing data.
Skip(mem, padding);
// Store a (potentially partial) tile only if the tile flag is set
// and the tile contains some data.
status = StoreFrame(dmux->num_frames_, mem, frame);
if (status != PARSE_ERROR && has_tiles && frame->frame_num_ > 0) {
// Note num_frames_ is incremented only when all tiles have been consumed.
added_tile = AddFrame(dmux, frame);
if (!added_tile) status = PARSE_ERROR;
// Store a fragment only if the fragments flag is set and all data for this
// fragment is available.
status = StoreFrame(dmux->num_frames_, frgm_payload_size, mem, frame);
if (status != PARSE_ERROR && has_fragments && frame->frame_num_ > 0) {
// Note num_frames_ is incremented only when all fragments have been
// consumed.
added_fragment = AddFrame(dmux, frame);
if (!added_fragment) status = PARSE_ERROR;
}
if (!added_tile) free(frame);
if (!added_fragment) free(frame);
return status;
}
@ -411,7 +416,9 @@ static ParseStatus ParseSingleImage(WebPDemuxer* const dmux) {
frame = (Frame*)calloc(1, sizeof(*frame));
if (frame == NULL) return PARSE_ERROR;
status = StoreFrame(1, &dmux->mem_, frame);
// For the single image case, we allow parsing of a partial frame. But we need
// at least CHUNK_HEADER_SIZE for parsing.
status = StoreFrame(1, CHUNK_HEADER_SIZE, &dmux->mem_, frame);
if (status != PARSE_ERROR) {
const int has_alpha = !!(dmux->feature_flags_ & ALPHA_FLAG);
// Clear any alpha when the alpha flag is missing.
@ -507,7 +514,7 @@ static ParseStatus ParseVP8X(WebPDemuxer* const dmux) {
}
case MKFOURCC('F', 'R', 'G', 'M'): {
if (dmux->num_frames_ == 0) dmux->num_frames_ = 1;
status = ParseTile(dmux, chunk_size_padded);
status = ParseFragment(dmux, chunk_size_padded);
break;
}
case MKFOURCC('I', 'C', 'C', 'P'): {
@ -565,7 +572,7 @@ static int IsValidSimpleFormat(const WebPDemuxer* const dmux) {
}
static int IsValidExtendedFormat(const WebPDemuxer* const dmux) {
const int has_tiles = !!(dmux->feature_flags_ & TILE_FLAG);
const int has_fragments = !!(dmux->feature_flags_ & FRAGMENTS_FLAG);
const int has_frames = !!(dmux->feature_flags_ & ANIMATION_FLAG);
const Frame* f;
@ -577,15 +584,15 @@ static int IsValidExtendedFormat(const WebPDemuxer* const dmux) {
for (f = dmux->frames_; f != NULL; f = f->next_) {
const int cur_frame_set = f->frame_num_;
int frame_count = 0, tile_count = 0;
int frame_count = 0, fragment_count = 0;
// Check frame properties and if the image is composed of tiles that each
// fragment came from a tile.
// Check frame properties and if the image is composed of fragments that
// each fragment came from a fragment.
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_fragments && f->is_fragment_) 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_) {
@ -606,11 +613,11 @@ static int IsValidExtendedFormat(const WebPDemuxer* const dmux) {
if (f->next_ != NULL) return 0;
}
tile_count += f->is_tile_;
fragment_count += f->is_fragment_;
++frame_count;
}
if (!has_tiles && frame_count > 1) return 0;
if (tile_count > 0 && frame_count != tile_count) return 0;
if (!has_fragments && frame_count > 1) return 0;
if (fragment_count > 0 && frame_count != fragment_count) return 0;
if (f == NULL) break;
}
return 1;
@ -700,7 +707,8 @@ uint32_t WebPDemuxGetI(const WebPDemuxer* dmux, WebPFormatFeature feature) {
// -----------------------------------------------------------------------------
// Frame iteration
// Find the first 'frame_num' frame. There may be multiple in a tiled frame.
// Find the first 'frame_num' frame. There may be multiple such frames in a
// fragmented frame.
static const Frame* GetFrame(const WebPDemuxer* const dmux, int frame_num) {
const Frame* f;
for (f = dmux->frames_; f != NULL; f = f->next_) {
@ -709,19 +717,19 @@ static const Frame* GetFrame(const WebPDemuxer* const dmux, int frame_num) {
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) {
// Returns fragment 'fragment_num' and the total count.
static const Frame* GetFragment(
const Frame* const frame_set, int fragment_num, int* const count) {
const int this_frame = frame_set->frame_num_;
const Frame* f = frame_set;
const Frame* tile = NULL;
const Frame* fragment = NULL;
int total;
for (total = 0; f != NULL && f->frame_num_ == this_frame; f = f->next_) {
if (++total == tile_num) tile = f;
if (++total == fragment_num) fragment = f;
}
*count = total;
return tile;
return fragment;
}
static const uint8_t* GetFramePayload(const uint8_t* const mem_buf,
@ -751,26 +759,28 @@ static const uint8_t* GetFramePayload(const uint8_t* const mem_buf,
// 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) {
int fragment_num, WebPIterator* const iter) {
const uint8_t* const mem_buf = dmux->mem_.buf_;
int num_tiles;
int num_fragments;
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);
const Frame* const fragment =
GetFragment(first_frame, fragment_num, &num_fragments);
const uint8_t* const payload =
GetFramePayload(mem_buf, fragment, &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;
iter->frame_num = first_frame->frame_num_;
iter->num_frames = dmux->num_frames_;
iter->fragment_num = fragment_num;
iter->num_fragments = num_fragments;
iter->x_offset = fragment->x_offset_;
iter->y_offset = fragment->y_offset_;
iter->width = fragment->width_;
iter->height = fragment->height_;
iter->duration = fragment->duration_;
iter->complete = fragment->complete_;
iter->fragment.bytes = payload;
iter->fragment.size = payload_size;
// TODO(jzern): adjust offsets for 'FRGM's embedded in 'ANMF's
return 1;
}
@ -805,13 +815,13 @@ int WebPDemuxPrevFrame(WebPIterator* iter) {
return SetFrame(iter->frame_num - 1, iter);
}
int WebPDemuxSelectTile(WebPIterator* iter, int tile) {
if (iter != NULL && iter->private_ != NULL && tile > 0) {
int WebPDemuxSelectFragment(WebPIterator* iter, int fragment_num) {
if (iter != NULL && iter->private_ != NULL && fragment_num > 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 SynthesizeFrame(dmux, frame, fragment_num, iter);
}
return 0;
}

View File

@ -109,15 +109,15 @@ static WebPMuxError MuxAddChunk(WebPMux* const mux, uint32_t nth, uint32_t tag,
}
// Create data for frame/tile given image data, offsets and duration.
static WebPMuxError CreateFrameTileData(const WebPData* const image,
int x_offset, int y_offset,
int duration, int is_lossless,
int is_frame,
WebPData* const frame_tile) {
static WebPMuxError CreateFrameFragmentData(const WebPData* const image,
int x_offset, int y_offset,
int duration, int is_lossless,
int is_frame,
WebPData* const frame_frgm) {
int width;
int height;
uint8_t* frame_tile_bytes;
const size_t frame_tile_size = kChunks[is_frame ? IDX_ANMF : IDX_FRGM].size;
uint8_t* frame_frgm_bytes;
const size_t frame_frgm_size = kChunks[is_frame ? IDX_ANMF : IDX_FRGM].size;
const int ok = is_lossless ?
VP8LGetInfo(image->bytes, image->size, &width, &height, NULL) :
@ -127,20 +127,20 @@ static WebPMuxError CreateFrameTileData(const WebPData* const image,
assert(width > 0 && height > 0 && duration > 0);
// Note: assertion on upper bounds is done in PutLE24().
frame_tile_bytes = (uint8_t*)malloc(frame_tile_size);
if (frame_tile_bytes == NULL) return WEBP_MUX_MEMORY_ERROR;
frame_frgm_bytes = (uint8_t*)malloc(frame_frgm_size);
if (frame_frgm_bytes == NULL) return WEBP_MUX_MEMORY_ERROR;
PutLE24(frame_tile_bytes + 0, x_offset / 2);
PutLE24(frame_tile_bytes + 3, y_offset / 2);
PutLE24(frame_frgm_bytes + 0, x_offset / 2);
PutLE24(frame_frgm_bytes + 3, y_offset / 2);
if (is_frame) {
PutLE24(frame_tile_bytes + 6, width - 1);
PutLE24(frame_tile_bytes + 9, height - 1);
PutLE24(frame_tile_bytes + 12, duration - 1);
PutLE24(frame_frgm_bytes + 6, width - 1);
PutLE24(frame_frgm_bytes + 9, height - 1);
PutLE24(frame_frgm_bytes + 12, duration - 1);
}
frame_tile->bytes = frame_tile_bytes;
frame_tile->size = frame_tile_size;
frame_frgm->bytes = frame_frgm_bytes;
frame_frgm->size = frame_frgm_size;
return WEBP_MUX_OK;
}
@ -334,19 +334,19 @@ WebPMuxError WebPMuxPushFrame(WebPMux* mux, const WebPMuxFrameInfo* frame,
const int y_offset = frame->y_offset & ~1;
const int duration = is_frame ? frame->duration : 1 /* unused */;
const uint32_t tag = kChunks[is_frame ? IDX_ANMF : IDX_FRGM].tag;
WebPData frame_tile;
WebPData frame_frgm;
if (x_offset < 0 || x_offset >= MAX_POSITION_OFFSET ||
y_offset < 0 || y_offset >= MAX_POSITION_OFFSET ||
(duration <= 0 || duration > MAX_DURATION)) {
err = WEBP_MUX_INVALID_ARGUMENT;
goto Err;
}
err = CreateFrameTileData(&wpi.img_->data_, x_offset, y_offset, duration,
is_lossless, is_frame, &frame_tile);
err = CreateFrameFragmentData(&wpi.img_->data_, x_offset, y_offset,
duration, is_lossless, is_frame, &frame_frgm);
if (err != WEBP_MUX_OK) goto Err;
// Add frame/tile chunk (with copy_data = 1).
err = AddDataToChunkList(&frame_tile, 1, tag, &wpi.header_);
WebPDataClear(&frame_tile); // frame_tile owned by wpi.header_ now.
// Add frame/fragment chunk (with copy_data = 1).
err = AddDataToChunkList(&frame_frgm, 1, tag, &wpi.header_);
WebPDataClear(&frame_frgm); // frame_frgm owned by wpi.header_ now.
if (err != WEBP_MUX_OK) goto Err;
}
@ -394,15 +394,15 @@ WebPMuxError WebPMuxDeleteFrame(WebPMux* mux, uint32_t nth) {
//------------------------------------------------------------------------------
// Assembly of the WebP RIFF file.
static WebPMuxError GetFrameTileInfo(const WebPChunk* const frame_tile_chunk,
int* const x_offset, int* const y_offset,
int* const duration) {
const uint32_t tag = frame_tile_chunk->tag_;
static WebPMuxError GetFrameFragmentInfo(
const WebPChunk* const frame_frgm_chunk,
int* const x_offset, int* const y_offset, int* const duration) {
const uint32_t tag = frame_frgm_chunk->tag_;
const int is_frame = (tag == kChunks[IDX_ANMF].tag);
const WebPData* const data = &frame_tile_chunk->data_;
const WebPData* const data = &frame_frgm_chunk->data_;
const size_t expected_data_size =
is_frame ? ANMF_CHUNK_SIZE : FRGM_CHUNK_SIZE;
assert(frame_tile_chunk != NULL);
assert(frame_frgm_chunk != NULL);
assert(tag == kChunks[IDX_ANMF].tag || tag == kChunks[IDX_FRGM].tag);
if (data->size != expected_data_size) return WEBP_MUX_INVALID_ARGUMENT;
@ -437,11 +437,11 @@ static WebPMuxError GetImageInfo(const WebPMuxImage* const wpi,
int* const duration,
int* const width, int* const height) {
const WebPChunk* const image_chunk = wpi->img_;
const WebPChunk* const frame_tile_chunk = wpi->header_;
const WebPChunk* const frame_frgm_chunk = wpi->header_;
// Get offsets and duration from ANMF/FRGM chunk.
const WebPMuxError err =
GetFrameTileInfo(frame_tile_chunk, x_offset, y_offset, duration);
GetFrameFragmentInfo(frame_frgm_chunk, x_offset, y_offset, duration);
if (err != WEBP_MUX_OK) return err;
// Get width and height from VP8/VP8L chunk.
@ -463,7 +463,7 @@ static WebPMuxError GetImageCanvasWidthHeight(
int max_x = 0;
int max_y = 0;
int64_t image_area = 0;
// Aggregate the bounding box for animation frames & tiled images.
// Aggregate the bounding box for animation frames & fragmented images.
for (; wpi != NULL; wpi = wpi->next_) {
int x_offset, y_offset, duration, w, h;
const WebPMuxError err = GetImageInfo(wpi, &x_offset, &y_offset,
@ -480,11 +480,11 @@ static WebPMuxError GetImageCanvasWidthHeight(
}
*width = max_x;
*height = max_y;
// Crude check to validate that there are no image overlaps/holes for tile
// images. Check that the aggregated image area for individual tiles exactly
// matches the image area of the constructed canvas. However, the area-match
// is necessary but not sufficient condition.
if ((flags & TILE_FLAG) && (image_area != (max_x * max_y))) {
// Crude check to validate that there are no image overlaps/holes for
// fragmented images. Check that the aggregated image area for individual
// fragments exactly matches the image area of the constructed canvas.
// However, the area-match is necessary but not sufficient condition.
if ((flags & FRAGMENTS_FLAG) && (image_area != (max_x * max_y))) {
*width = 0;
*height = 0;
return WEBP_MUX_INVALID_ARGUMENT;
@ -539,8 +539,8 @@ static WebPMuxError CreateVP8XChunk(WebPMux* const mux) {
}
if (images->header_ != NULL) {
if (images->header_->tag_ == kChunks[IDX_FRGM].tag) {
// This is a tiled image.
flags |= TILE_FLAG;
// This is a fragmented image.
flags |= FRAGMENTS_FLAG;
} else if (images->header_->tag_ == kChunks[IDX_ANMF].tag) {
// This is an image with animation.
flags |= ANIMATION_FLAG;

View File

@ -399,13 +399,32 @@ size_t MuxImageListDiskSize(const WebPMuxImage* wpi_list) {
return size;
}
// Special case as ANMF/FRGM chunk encapsulates other image chunks.
static uint8_t* ChunkEmitSpecial(const WebPChunk* const header,
size_t total_size, uint8_t* dst) {
const size_t header_size = header->data_.size;
const size_t offset_to_next = total_size - CHUNK_HEADER_SIZE;
assert(header->tag_ == kChunks[IDX_ANMF].tag ||
header->tag_ == kChunks[IDX_FRGM].tag);
PutLE32(dst + 0, header->tag_);
PutLE32(dst + TAG_SIZE, (uint32_t)offset_to_next);
assert(header_size == (uint32_t)header_size);
memcpy(dst + CHUNK_HEADER_SIZE, header->data_.bytes, header_size);
if (header_size & 1) {
dst[CHUNK_HEADER_SIZE + header_size] = 0; // Add padding.
}
return dst + ChunkDiskSize(header);
}
uint8_t* MuxImageEmit(const WebPMuxImage* const wpi, uint8_t* dst) {
// Ordering of chunks to be emitted is strictly as follows:
// 1. ANMF/FRGM chunk (if present).
// 2. ALPH chunk (if present).
// 3. VP8/VP8L chunk.
assert(wpi);
if (wpi->header_ != NULL) dst = ChunkEmit(wpi->header_, dst);
if (wpi->header_ != NULL) {
dst = ChunkEmitSpecial(wpi->header_, MuxImageDiskSize(wpi), dst);
}
if (wpi->alpha_ != NULL) dst = ChunkEmit(wpi->alpha_, dst);
if (wpi->img_ != NULL) dst = ChunkEmit(wpi->img_, dst);
return dst;
@ -457,16 +476,16 @@ WebPChunk** MuxGetChunkListFromId(const WebPMux* mux, WebPChunkId id) {
WebPMuxError MuxValidateForImage(const WebPMux* const mux) {
const int num_images = MuxImageCount(mux->images_, WEBP_CHUNK_IMAGE);
const int num_frames = MuxImageCount(mux->images_, WEBP_CHUNK_ANMF);
const int num_tiles = MuxImageCount(mux->images_, WEBP_CHUNK_FRGM);
const int num_fragments = MuxImageCount(mux->images_, WEBP_CHUNK_FRGM);
if (num_images == 0) {
// No images in mux.
return WEBP_MUX_NOT_FOUND;
} else if (num_images == 1 && num_frames == 0 && num_tiles == 0) {
} else if (num_images == 1 && num_frames == 0 && num_fragments == 0) {
// Valid case (single image).
return WEBP_MUX_OK;
} else {
// Frame/Tile case OR an invalid mux.
// Frame/Fragment case OR an invalid mux.
return WEBP_MUX_INVALID_ARGUMENT;
}
}
@ -501,7 +520,7 @@ WebPMuxError MuxValidate(const WebPMux* const mux) {
int num_xmp;
int num_loop_chunks;
int num_frames;
int num_tiles;
int num_fragments;
int num_vp8x;
int num_images;
int num_alpha;
@ -546,8 +565,8 @@ WebPMuxError MuxValidate(const WebPMux* const mux) {
}
}
// Tiling: TILE_FLAG and tile chunk(s) are consistent.
err = ValidateChunk(mux, IDX_FRGM, TILE_FLAG, flags, -1, &num_tiles);
// Fragmentation: FRAGMENTS_FLAG and FRGM chunk(s) are consistent.
err = ValidateChunk(mux, IDX_FRGM, FRAGMENTS_FLAG, flags, -1, &num_fragments);
if (err != WEBP_MUX_OK) return err;
// Verify either VP8X chunk is present OR there is only one elem in
@ -567,8 +586,8 @@ WebPMuxError MuxValidate(const WebPMux* const mux) {
if (err != WEBP_MUX_OK) return err;
}
// num_tiles & num_images are consistent.
if (num_tiles > 0 && num_images != num_tiles) {
// num_fragments & num_images are consistent.
if (num_fragments > 0 && num_images != num_fragments) {
return WEBP_MUX_INVALID_ARGUMENT;
}

View File

@ -51,10 +51,9 @@ static WebPMuxError MuxGet(const WebPMux* const mux, CHUNK_INDEX idx,
// Fill the chunk with the given data (includes chunk header bytes), after some
// verifications.
static WebPMuxError ChunkVerifyAndAssignData(WebPChunk* chunk,
const uint8_t* data,
size_t data_size, size_t riff_size,
int copy_data) {
static WebPMuxError ChunkVerifyAndAssign(WebPChunk* chunk,
const uint8_t* data, size_t data_size,
size_t riff_size, int copy_data) {
uint32_t chunk_size;
WebPData chunk_data;
@ -74,6 +73,66 @@ static WebPMuxError ChunkVerifyAndAssignData(WebPChunk* chunk,
return ChunkAssignData(chunk, &chunk_data, copy_data, GetLE32(data + 0));
}
static int MuxImageParse(const WebPChunk* const chunk, int copy_data,
WebPMuxImage* const wpi) {
const uint8_t* bytes = chunk->data_.bytes;
size_t size = chunk->data_.size;
const uint8_t* const last = bytes + size;
WebPChunk subchunk;
size_t subchunk_size;
ChunkInit(&subchunk);
assert(chunk->tag_ == kChunks[IDX_ANMF].tag ||
chunk->tag_ == kChunks[IDX_FRGM].tag);
assert(!wpi->is_partial_);
// ANMF/FRGM.
{
const size_t hdr_size = (chunk->tag_ == kChunks[IDX_ANMF].tag) ?
ANMF_CHUNK_SIZE : FRGM_CHUNK_SIZE;
const WebPData temp = { bytes, hdr_size };
ChunkAssignData(&subchunk, &temp, copy_data, chunk->tag_);
}
ChunkSetNth(&subchunk, &wpi->header_, 1);
wpi->is_partial_ = 1; // Waiting for ALPH and/or VP8/VP8L chunks.
// Rest of the chunks.
subchunk_size = ChunkDiskSize(&subchunk) - CHUNK_HEADER_SIZE;
bytes += subchunk_size;
size -= subchunk_size;
while (bytes != last) {
ChunkInit(&subchunk);
if (ChunkVerifyAndAssign(&subchunk, bytes, size, size,
copy_data) != WEBP_MUX_OK) {
goto Fail;
}
switch (ChunkGetIdFromTag(subchunk.tag_)) {
case WEBP_CHUNK_ALPHA:
if (wpi->alpha_ != NULL) goto Fail; // Consecutive ALPH chunks.
if (ChunkSetNth(&subchunk, &wpi->alpha_, 1) != WEBP_MUX_OK) goto Fail;
wpi->is_partial_ = 1; // Waiting for a VP8 chunk.
break;
case WEBP_CHUNK_IMAGE:
if (ChunkSetNth(&subchunk, &wpi->img_, 1) != WEBP_MUX_OK) goto Fail;
wpi->is_partial_ = 0; // wpi is completely filled.
break;
default:
goto Fail;
break;
}
subchunk_size = ChunkDiskSize(&subchunk);
bytes += subchunk_size;
size -= subchunk_size;
}
if (wpi->is_partial_) goto Fail;
return 1;
Fail:
ChunkRelease(&subchunk);
return 0;
}
//------------------------------------------------------------------------------
// Create a mux object from WebP-RIFF data.
@ -136,42 +195,46 @@ WebPMux* WebPMuxCreateInternal(const WebPData* bitstream, int copy_data,
// Loop over chunks.
while (data != end) {
size_t data_size;
WebPChunkId id;
WebPMuxError err;
err = ChunkVerifyAndAssignData(&chunk, data, size, riff_size, copy_data);
if (err != WEBP_MUX_OK) goto Err;
WebPChunk** chunk_list;
if (ChunkVerifyAndAssign(&chunk, data, size, riff_size,
copy_data) != WEBP_MUX_OK) {
goto Err;
}
data_size = ChunkDiskSize(&chunk);
id = ChunkGetIdFromTag(chunk.tag_);
if (IsWPI(id)) { // An image chunk (frame/tile/alpha/vp8).
WebPChunk** wpi_chunk_ptr =
MuxImageGetListFromId(wpi, id); // Image chunk to set.
assert(wpi_chunk_ptr != NULL);
if (*wpi_chunk_ptr != NULL) goto Err; // Consecutive alpha chunks or
// consecutive frame/tile chunks.
if (ChunkSetNth(&chunk, wpi_chunk_ptr, 1) != WEBP_MUX_OK) goto Err;
if (id == WEBP_CHUNK_IMAGE) {
switch (id) {
case WEBP_CHUNK_ALPHA:
if (wpi->alpha_ != NULL) goto Err; // Consecutive ALPH chunks.
if (ChunkSetNth(&chunk, &wpi->alpha_, 1) != WEBP_MUX_OK) goto Err;
wpi->is_partial_ = 1; // Waiting for a VP8 chunk.
break;
case WEBP_CHUNK_IMAGE:
if (ChunkSetNth(&chunk, &wpi->img_, 1) != WEBP_MUX_OK) goto Err;
wpi->is_partial_ = 0; // wpi is completely filled.
PushImage:
// Add this to mux->images_ list.
if (MuxImagePush(wpi, &mux->images_) != WEBP_MUX_OK) goto Err;
MuxImageInit(wpi); // Reset for reading next image.
} else {
wpi->is_partial_ = 1; // wpi is only partially filled.
}
} else { // A non-image chunk.
WebPChunk** chunk_list;
if (wpi->is_partial_) goto Err; // Encountered a non-image chunk before
// getting all chunks of an image.
chunk_list = MuxGetChunkListFromId(mux, id); // List to add this chunk.
if (chunk_list == NULL) chunk_list = &mux->unknown_;
if (ChunkSetNth(&chunk, chunk_list, 0) != WEBP_MUX_OK) goto Err;
}
{
const size_t data_size = ChunkDiskSize(&chunk);
data += data_size;
size -= data_size;
break;
case WEBP_CHUNK_ANMF:
case WEBP_CHUNK_FRGM:
if (wpi->is_partial_) goto Err; // Previous wpi is still incomplete.
if (!MuxImageParse(&chunk, copy_data, wpi)) goto Err;
ChunkRelease(&chunk);
goto PushImage;
break;
default: // A non-image chunk.
if (wpi->is_partial_) goto Err; // Encountered a non-image chunk before
// getting all chunks of an image.
chunk_list = MuxGetChunkListFromId(mux, id); // List to add this chunk.
if (chunk_list == NULL) chunk_list = &mux->unknown_;
if (ChunkSetNth(&chunk, chunk_list, 0) != WEBP_MUX_OK) goto Err;
break;
}
data += data_size;
size -= data_size;
ChunkInit(&chunk);
}
@ -301,19 +364,19 @@ static WebPMuxError MuxGetImageInternal(const WebPMuxImage* const wpi,
return SynthesizeBitstream(wpi, &info->bitstream);
}
static WebPMuxError MuxGetFrameTileInternal(const WebPMuxImage* const wpi,
WebPMuxFrameInfo* const frame) {
static WebPMuxError MuxGetFrameFragmentInternal(const WebPMuxImage* const wpi,
WebPMuxFrameInfo* const frame) {
const int is_frame = (wpi->header_->tag_ == kChunks[IDX_ANMF].tag);
const CHUNK_INDEX idx = is_frame ? IDX_ANMF : IDX_FRGM;
const WebPData* frame_tile_data;
const WebPData* frame_frgm_data;
assert(wpi->header_ != NULL); // Already checked by WebPMuxGetFrame().
// Get frame/tile chunk.
frame_tile_data = &wpi->header_->data_;
if (frame_tile_data->size < kChunks[idx].size) return WEBP_MUX_BAD_DATA;
// Get frame/fragment chunk.
frame_frgm_data = &wpi->header_->data_;
if (frame_frgm_data->size < kChunks[idx].size) return WEBP_MUX_BAD_DATA;
// Extract info.
frame->x_offset = 2 * GetLE24(frame_tile_data->bytes + 0);
frame->y_offset = 2 * GetLE24(frame_tile_data->bytes + 3);
frame->duration = is_frame ? 1 + GetLE24(frame_tile_data->bytes + 12) : 1;
frame->x_offset = 2 * GetLE24(frame_frgm_data->bytes + 0);
frame->y_offset = 2 * GetLE24(frame_frgm_data->bytes + 3);
frame->duration = is_frame ? 1 + GetLE24(frame_frgm_data->bytes + 12) : 1;
frame->id = ChunkGetIdFromTag(wpi->header_->tag_);
return SynthesizeBitstream(wpi, &frame->bitstream);
}
@ -336,7 +399,7 @@ WebPMuxError WebPMuxGetFrame(
if (wpi->header_ == NULL) {
return MuxGetImageInternal(wpi, frame);
} else {
return MuxGetFrameTileInternal(wpi, frame);
return MuxGetFrameFragmentInternal(wpi, frame);
}
}

View File

@ -83,7 +83,7 @@ typedef enum {
#define MAX_IMAGE_AREA (1ULL << 32) // 32-bit max for width x height.
#define MAX_LOOP_COUNT (1 << 16) // maximum value for loop-count
#define MAX_DURATION (1 << 24) // maximum duration
#define MAX_POSITION_OFFSET (1 << 24) // maximum frame/tile x/y offset
#define MAX_POSITION_OFFSET (1 << 24) // maximum frame/fragment x/y offset
// Maximum chunk payload is such that adding the header and padding won't
// overflow a uint32_t.

View File

@ -11,7 +11,7 @@
// Vikas (vikasa@google.com)
// This API allows manipulation of WebP container images containing features
// like color profile, metadata, animation and tiling.
// like color profile, metadata, animation and fragmented images.
//
// Code Example#1: Creating a MUX with image data, color profile and XMP
// metadata.
@ -82,7 +82,7 @@ enum WebPMuxError {
// Flag values for different features used in VP8X chunk.
enum WebPFeatureFlags {
TILE_FLAG = 0x00000001,
FRAGMENTS_FLAG = 0x00000001,
ANIMATION_FLAG = 0x00000002,
XMP_FLAG = 0x00000004,
EXIF_FLAG = 0x00000008,
@ -233,12 +233,12 @@ struct WebPMuxFrameInfo {
uint32_t pad[3]; // padding for later use
};
// Sets the (non-animated and non-tiled) image in the mux object.
// Note: Any existing images (including frames/tiles) will be removed.
// Sets the (non-animated and non-fragmented) image in the mux object.
// Note: Any existing images (including frames/fragments) will be removed.
// Parameters:
// mux - (in/out) object in which the image is to be set
// bitstream - (in) can either be a raw VP8/VP8L bitstream or a single-image
// WebP file (non-animated and non-tiled)
// WebP file (non-animated and non-fragmented)
// copy_data - (in) value 1 indicates given data WILL be copied to the mux
// and value 0 indicates data will NOT be copied.
// Returns:
@ -250,8 +250,8 @@ WEBP_EXTERN(WebPMuxError) WebPMuxSetImage(
// Adds a frame at the end of the mux object.
// Notes: (1) frame.id should be one of WEBP_CHUNK_ANMF or WEBP_CHUNK_FRGM
// (2) For setting a non-animated non-tiled image, use WebPMuxSetImage()
// instead.
// (2) For setting a non-animated non-fragmented image, use
// WebPMuxSetImage() instead.
// (3) Type of frame being pushed must be same as the frames in mux.
// (4) As WebP only supports even offsets, any odd offset will be snapped
// to an even location using: offset &= ~1
@ -428,22 +428,23 @@ WEBP_EXTERN(uint32_t) WebPDemuxGetI(
struct WebPIterator {
int frame_num;
int num_frames;
int tile_num;
int num_tiles;
int fragment_num;
int num_fragments;
int x_offset, y_offset; // offset relative to the canvas.
int width, height; // dimensions of this frame or tile.
int width, height; // dimensions of this frame or fragment.
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_'.
int complete; // true if 'fragment' contains a full frame. partial images
// may still be decoded with the WebP incremental decoder.
WebPData fragment; // The frame or fragment given by 'frame_num' and
// 'fragment_num'.
uint32_t pad[4]; // padding for later use
void* private_; // for internal use only.
};
// 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().
// 'iter->fragment' points to the first fragment on return from this function.
// Individual fragments may be extracted using WebPDemuxSetFragment().
// 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.
@ -451,14 +452,14 @@ struct WebPIterator {
WEBP_EXTERN(int) WebPDemuxGetFrame(
const WebPDemuxer* dmux, int frame_number, WebPIterator* iter);
// Sets 'iter->tile_' to point to the next ('iter->frame_num_' + 1) or previous
// ('iter->frame_num_' - 1) frame. These functions do not loop.
// Sets 'iter->fragment' to point to the next ('iter->frame_num' + 1) or
// previous ('iter->frame_num' - 1) frame. These functions do not loop.
// Returns true on success, false otherwise.
WEBP_EXTERN(int) WebPDemuxNextFrame(WebPIterator* iter);
WEBP_EXTERN(int) WebPDemuxPrevFrame(WebPIterator* iter);
// Sets 'iter->tile_' to reflect tile number 'tile_number'.
// Returns true if tile 'tile_number' is present, false otherwise.
// Sets 'iter->fragment' to reflect fragment number 'fragment_num'.
// Returns true if fragment 'fragment_num' is present, false otherwise.
WEBP_EXTERN(int) WebPDemuxSelectTile(WebPIterator* iter, int tile_number);
// Releases any memory associated with 'iter'.
@ -493,8 +494,8 @@ WEBP_EXTERN(int) WebPDemuxGetChunk(const WebPDemuxer* dmux,
const char fourcc[4], int chunk_number,
WebPChunkIterator* iter);
// Sets 'iter->chunk_' to point to the next ('iter->chunk_num_' + 1) or previous
// ('iter->chunk_num_' - 1) chunk. These functions do not loop.
// Sets 'iter->chunk' to point to the next ('iter->chunk_num' + 1) or previous
// ('iter->chunk_num' - 1) chunk. These functions do not loop.
// Returns true on success, false otherwise.
WEBP_EXTERN(int) WebPDemuxNextChunk(WebPChunkIterator* iter);
WEBP_EXTERN(int) WebPDemuxPrevChunk(WebPChunkIterator* iter);