mirror of
https://github.com/webmproject/libwebp.git
synced 2024-12-25 13:18:22 +01:00
Update mux code to match the spec wrt animation
- Allow a duration of 0 - Rename LOOP chunk to ANIM and add the background color field to it. - Add a disposal method field for each animation frame. - Modify webpmux.c binary interface to allow the input of background color and disposal methods. Also make '-loop' and '-bgcolor' arguments optional with some default values. Change-Id: I807372a61cdb8a0d3080ae3552caf2848070bf4d
This commit is contained in:
parent
d9c5fbefa4
commit
fa30c86323
19
README.mux
19
README.mux
@ -26,7 +26,8 @@ Usage: webpmux -get GET_OPTIONS INPUT -o OUTPUT
|
|||||||
webpmux -set SET_OPTIONS INPUT -o OUTPUT
|
webpmux -set SET_OPTIONS INPUT -o OUTPUT
|
||||||
webpmux -strip STRIP_OPTIONS INPUT -o OUTPUT
|
webpmux -strip STRIP_OPTIONS INPUT -o OUTPUT
|
||||||
webpmux -frgm FRAGMENT_OPTIONS [-frgm...] -o OUTPUT
|
webpmux -frgm FRAGMENT_OPTIONS [-frgm...] -o OUTPUT
|
||||||
webpmux -frame FRAME_OPTIONS [-frame...] -loop LOOP_COUNT -o OUTPUT
|
webpmux -frame FRAME_OPTIONS [-frame...] [-loop LOOP_COUNT]
|
||||||
|
[-bgcolor BACKGROUND_COLOR] -o OUTPUT
|
||||||
webpmux -info INPUT
|
webpmux -info INPUT
|
||||||
webpmux [-h|-help]
|
webpmux [-h|-help]
|
||||||
|
|
||||||
@ -61,12 +62,24 @@ FRAGMENT_OPTIONS(i):
|
|||||||
|
|
||||||
FRAME_OPTIONS(i):
|
FRAME_OPTIONS(i):
|
||||||
Create animation.
|
Create animation.
|
||||||
file_i +xi+yi+di
|
file_i +xi+yi+di+mi
|
||||||
where: 'file_i' is the i'th animation frame (WebP format),
|
where: 'file_i' is the i'th animation frame (WebP format),
|
||||||
'xi','yi' specify the image offset for this frame.
|
'xi','yi' specify the image offset for this frame.
|
||||||
'di' is the pause duration before next frame.
|
'di' is the pause duration before next frame.
|
||||||
|
'mi' is the dispose method for this frame (0 or 1).
|
||||||
|
|
||||||
INPUT and OUTPUT are in WebP format.
|
LOOP_COUNT:
|
||||||
|
Number of times to repeat the animation.
|
||||||
|
Valid range is 0 to 65535 [Default: 0 (infinite)].
|
||||||
|
|
||||||
|
BACKGROUND_COLOR:
|
||||||
|
Background color of the canvas.
|
||||||
|
A,R,G,B
|
||||||
|
where: 'A', 'R', 'G' and 'B' are integers in the range 0 to 255 specifying
|
||||||
|
the Alpha, Red, Green and Blue component values respectively
|
||||||
|
[Default: 255,255,255,255].
|
||||||
|
|
||||||
|
INPUT & OUTPUT are in WebP format.
|
||||||
|
|
||||||
Note: The nature of EXIF, XMP and ICC data is not checked and is assumed to be
|
Note: The nature of EXIF, XMP and ICC data is not checked and is assumed to be
|
||||||
valid.
|
valid.
|
||||||
|
@ -23,11 +23,11 @@
|
|||||||
-frgm fragment_4.webp +960+576 \
|
-frgm fragment_4.webp +960+576 \
|
||||||
-o out_fragment_container.webp
|
-o out_fragment_container.webp
|
||||||
|
|
||||||
webpmux -frame anim_1.webp +0+0+0 \
|
webpmux -frame anim_1.webp +0+0+0+0 \
|
||||||
-frame anim_2.webp +25+25+100 \
|
-frame anim_2.webp +25+25+100+1 \
|
||||||
-frame anim_3.webp +50+50+100 \
|
-frame anim_3.webp +50+50+100+1 \
|
||||||
-frame anim_4.webp +0+0+100 \
|
-frame anim_4.webp +0+0+100+0 \
|
||||||
-loop 10 \
|
-loop 10 -bgcolor 255,255,255,255 \
|
||||||
-o out_animation_container.webp
|
-o out_animation_container.webp
|
||||||
|
|
||||||
webpmux -set icc image_profile.icc in.webp -o out_icc_container.webp
|
webpmux -set icc image_profile.icc in.webp -o out_icc_container.webp
|
||||||
@ -72,8 +72,9 @@ typedef enum {
|
|||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
NIL_SUBTYPE = 0,
|
NIL_SUBTYPE = 0,
|
||||||
SUBTYPE_FRM,
|
SUBTYPE_ANMF,
|
||||||
SUBTYPE_LOOP
|
SUBTYPE_LOOP,
|
||||||
|
SUBTYPE_BGCOLOR
|
||||||
} FeatureSubType;
|
} FeatureSubType;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@ -140,10 +141,6 @@ static const char* ErrorString(WebPMuxError err) {
|
|||||||
return kErrorMessages[-err];
|
return kErrorMessages[-err];
|
||||||
}
|
}
|
||||||
|
|
||||||
static int IsNotCompatible(int count1, int count2) {
|
|
||||||
return (count1 > 0) != (count2 > 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#define RETURN_IF_ERROR(ERR_MSG) \
|
#define RETURN_IF_ERROR(ERR_MSG) \
|
||||||
if (err != WEBP_MUX_OK) { \
|
if (err != WEBP_MUX_OK) { \
|
||||||
fprintf(stderr, ERR_MSG); \
|
fprintf(stderr, ERR_MSG); \
|
||||||
@ -211,10 +208,11 @@ static WebPMuxError DisplayInfo(const WebPMux* mux) {
|
|||||||
int nFrames;
|
int nFrames;
|
||||||
|
|
||||||
if (is_anim) {
|
if (is_anim) {
|
||||||
int loop_count;
|
WebPMuxAnimParams params;
|
||||||
err = WebPMuxGetLoopCount(mux, &loop_count);
|
err = WebPMuxGetAnimationParams(mux, ¶ms);
|
||||||
RETURN_IF_ERROR("Failed to retrieve loop count\n");
|
RETURN_IF_ERROR("Failed to retrieve animation parameters\n");
|
||||||
printf("Loop Count : %d\n", loop_count);
|
printf("Background color : 0x%.8X Loop Count : %d\n",
|
||||||
|
params.bgcolor, params.loop_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
err = WebPMuxNumChunks(mux, id, &nFrames);
|
err = WebPMuxNumChunks(mux, id, &nFrames);
|
||||||
@ -224,14 +222,14 @@ static WebPMuxError DisplayInfo(const WebPMux* mux) {
|
|||||||
if (nFrames > 0) {
|
if (nFrames > 0) {
|
||||||
int i;
|
int i;
|
||||||
printf("No.: x_offset y_offset ");
|
printf("No.: x_offset y_offset ");
|
||||||
if (is_anim) printf("duration ");
|
if (is_anim) printf("duration dispose ");
|
||||||
printf("image_size\n");
|
printf("image_size\n");
|
||||||
for (i = 1; i <= nFrames; i++) {
|
for (i = 1; i <= nFrames; i++) {
|
||||||
WebPMuxFrameInfo frame;
|
WebPMuxFrameInfo frame;
|
||||||
err = WebPMuxGetFrame(mux, i, &frame);
|
err = WebPMuxGetFrame(mux, i, &frame);
|
||||||
RETURN_IF_ERROR3("Failed to retrieve %s#%d\n", type_str, i);
|
RETURN_IF_ERROR3("Failed to retrieve %s#%d\n", type_str, i);
|
||||||
printf("%3d: %8d %8d ", i, frame.x_offset, frame.y_offset);
|
printf("%3d: %8d %8d ", i, frame.x_offset, frame.y_offset);
|
||||||
if (is_anim) printf("%8d ", frame.duration);
|
if (is_anim) printf("%8d %7d ", frame.duration, frame.dispose_method);
|
||||||
printf("%10zu\n", frame.bitstream.size);
|
printf("%10zu\n", frame.bitstream.size);
|
||||||
WebPDataClear(&frame.bitstream);
|
WebPDataClear(&frame.bitstream);
|
||||||
}
|
}
|
||||||
@ -274,8 +272,9 @@ static void PrintHelp(void) {
|
|||||||
printf(" webpmux -set SET_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 -strip STRIP_OPTIONS INPUT -o OUTPUT\n");
|
||||||
printf(" webpmux -frgm FRAGMENT_OPTIONS [-frgm...] -o OUTPUT\n");
|
printf(" webpmux -frgm FRAGMENT_OPTIONS [-frgm...] -o OUTPUT\n");
|
||||||
printf(" webpmux -frame FRAME_OPTIONS [-frame...]");
|
printf(" webpmux -frame FRAME_OPTIONS [-frame...] [-loop LOOP_COUNT]"
|
||||||
printf(" -loop LOOP_COUNT -o OUTPUT\n");
|
"\n");
|
||||||
|
printf(" [-bgcolor BACKGROUND_COLOR] -o OUTPUT\n");
|
||||||
printf(" webpmux -info INPUT\n");
|
printf(" webpmux -info INPUT\n");
|
||||||
printf(" webpmux [-h|-help]\n");
|
printf(" webpmux [-h|-help]\n");
|
||||||
|
|
||||||
@ -316,15 +315,31 @@ static void PrintHelp(void) {
|
|||||||
printf("\n");
|
printf("\n");
|
||||||
printf("FRAME_OPTIONS(i):\n");
|
printf("FRAME_OPTIONS(i):\n");
|
||||||
printf(" Create animation.\n");
|
printf(" Create animation.\n");
|
||||||
printf(" file_i +xi+yi+di\n");
|
printf(" file_i +xi+yi+di+mi\n");
|
||||||
printf(" where: 'file_i' is the i'th animation frame (WebP format),\n");
|
printf(" where: 'file_i' is the i'th animation frame (WebP format),\n");
|
||||||
printf(" 'xi','yi' specify the image offset for this frame.\n");
|
printf(" 'xi','yi' specify the image offset for this frame.\n");
|
||||||
printf(" 'di' is the pause duration before next frame.\n");
|
printf(" 'di' is the pause duration before next frame.\n");
|
||||||
|
printf(" 'mi' is the dispose method for this frame (0 or 1).\n");
|
||||||
|
|
||||||
|
printf("\n");
|
||||||
|
printf("LOOP_COUNT:\n");
|
||||||
|
printf(" Number of times to repeat the animation.\n");
|
||||||
|
printf(" Valid range is 0 to 65535 [Default: 0 (infinite)].\n");
|
||||||
|
|
||||||
|
printf("\n");
|
||||||
|
printf("BACKGROUND_COLOR:\n");
|
||||||
|
printf(" Background color of the canvas.\n");
|
||||||
|
printf(" A,R,G,B\n");
|
||||||
|
printf(" where: 'A', 'R', 'G' and 'B' are integers in the range 0 to 255 "
|
||||||
|
"specifying\n");
|
||||||
|
printf(" the Alpha, Red, Green and Blue component values "
|
||||||
|
"respectively\n");
|
||||||
|
printf(" [Default: 255,255,255,255].\n");
|
||||||
|
|
||||||
printf("\nINPUT & OUTPUT are in WebP format.\n");
|
printf("\nINPUT & OUTPUT are in WebP format.\n");
|
||||||
|
|
||||||
printf("\nNote: The nature of EXIF, XMP and ICC data is not checked and is "
|
printf("\nNote: The nature of EXIF, XMP and ICC data is not checked");
|
||||||
"assumed to be valid.\n");
|
printf(" and is assumed to be\nvalid.\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ReadFileToWebPData(const char* const filename,
|
static int ReadFileToWebPData(const char* const filename,
|
||||||
@ -379,14 +394,29 @@ static int WriteWebP(WebPMux* const mux, const char* filename) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int ParseFrameArgs(const char* args, WebPMuxFrameInfo* const info) {
|
static int ParseFrameArgs(const char* args, WebPMuxFrameInfo* const info) {
|
||||||
return (sscanf(args, "+%d+%d+%d",
|
int dispose_method;
|
||||||
&info->x_offset, &info->y_offset, &info->duration) == 3);
|
if (sscanf(args, "+%d+%d+%d+%d", &info->x_offset, &info->y_offset,
|
||||||
|
&info->duration, &dispose_method) != 4) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// Note: The sanity of the following conversion is checked by
|
||||||
|
// WebPMuxSetAnimationParams().
|
||||||
|
info->dispose_method = (WebPMuxAnimDispose)dispose_method;
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ParseFragmentArgs(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);
|
return (sscanf(args, "+%d+%d", &info->x_offset, &info->y_offset) == 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int ParseBgcolorArgs(const char* args, uint32_t* const bgcolor) {
|
||||||
|
uint32_t a, r, g, b;
|
||||||
|
if (sscanf(args, "%u,%u,%u,%u", &a, &r, &g, &b) != 4) return 0;
|
||||||
|
if (a >= 256 || r >= 256 || g >= 256 || b >= 256) return 0;
|
||||||
|
*bgcolor = (a << 24) | (r << 16) | (g << 8) | (b << 0);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// Clean-up.
|
// Clean-up.
|
||||||
|
|
||||||
@ -409,6 +439,7 @@ static int ValidateCommandLine(int argc, const char* argv[],
|
|||||||
int num_frame_args;
|
int num_frame_args;
|
||||||
int num_frgm_args;
|
int num_frgm_args;
|
||||||
int num_loop_args;
|
int num_loop_args;
|
||||||
|
int num_bgcolor_args;
|
||||||
int ok = 1;
|
int ok = 1;
|
||||||
|
|
||||||
assert(num_feature_args != NULL);
|
assert(num_feature_args != NULL);
|
||||||
@ -435,14 +466,18 @@ static int ValidateCommandLine(int argc, const char* argv[],
|
|||||||
num_frame_args = CountOccurrences(argv, argc, "-frame");
|
num_frame_args = CountOccurrences(argv, argc, "-frame");
|
||||||
num_frgm_args = CountOccurrences(argv, argc, "-frgm");
|
num_frgm_args = CountOccurrences(argv, argc, "-frgm");
|
||||||
num_loop_args = CountOccurrences(argv, argc, "-loop");
|
num_loop_args = CountOccurrences(argv, argc, "-loop");
|
||||||
|
num_bgcolor_args = CountOccurrences(argv, argc, "-bgcolor");
|
||||||
|
|
||||||
if (num_loop_args > 1) {
|
if (num_loop_args > 1) {
|
||||||
ERROR_GOTO1("ERROR: Multiple loop counts specified.\n", ErrValidate);
|
ERROR_GOTO1("ERROR: Multiple loop counts specified.\n", ErrValidate);
|
||||||
}
|
}
|
||||||
|
if (num_bgcolor_args > 1) {
|
||||||
|
ERROR_GOTO1("ERROR: Multiple background colors specified.\n", ErrValidate);
|
||||||
|
}
|
||||||
|
|
||||||
if (IsNotCompatible(num_frame_args, num_loop_args)) {
|
if ((num_frame_args == 0) && (num_loop_args + num_bgcolor_args > 0)) {
|
||||||
ERROR_GOTO1("ERROR: Both frames and loop count have to be specified.\n",
|
ERROR_GOTO1("ERROR: Loop count and background color are relevant only in "
|
||||||
ErrValidate);
|
"case of animation.\n", ErrValidate);
|
||||||
}
|
}
|
||||||
if (num_frame_args > 0 && num_frgm_args > 0) {
|
if (num_frame_args > 0 && num_frgm_args > 0) {
|
||||||
ERROR_GOTO1("ERROR: Only one of frames & fragments can be specified at a "
|
ERROR_GOTO1("ERROR: Only one of frames & fragments can be specified at a "
|
||||||
@ -456,7 +491,7 @@ static int ValidateCommandLine(int argc, const char* argv[],
|
|||||||
} else {
|
} else {
|
||||||
// Multiple arguments ('set' action for animation or fragmented image).
|
// Multiple arguments ('set' action for animation or fragmented image).
|
||||||
if (num_frame_args > 0) {
|
if (num_frame_args > 0) {
|
||||||
*num_feature_args = num_frame_args + num_loop_args;
|
*num_feature_args = num_frame_args + num_loop_args + num_bgcolor_args;
|
||||||
} else {
|
} else {
|
||||||
*num_feature_args = num_frgm_args;
|
*num_feature_args = num_frgm_args;
|
||||||
}
|
}
|
||||||
@ -528,12 +563,12 @@ static int ParseCommandLine(int argc, const char* argv[],
|
|||||||
} else {
|
} else {
|
||||||
ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
|
ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
|
||||||
}
|
}
|
||||||
arg->subtype_ = SUBTYPE_FRM;
|
arg->subtype_ = SUBTYPE_ANMF;
|
||||||
arg->filename_ = argv[i + 1];
|
arg->filename_ = argv[i + 1];
|
||||||
arg->params_ = argv[i + 2];
|
arg->params_ = argv[i + 2];
|
||||||
++feature_arg_index;
|
++feature_arg_index;
|
||||||
i += 3;
|
i += 3;
|
||||||
} else if (!strcmp(argv[i], "-loop")) {
|
} else if (!strcmp(argv[i], "-loop") || !strcmp(argv[i], "-bgcolor")) {
|
||||||
CHECK_NUM_ARGS_LESS(2, ErrParse);
|
CHECK_NUM_ARGS_LESS(2, ErrParse);
|
||||||
if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) {
|
if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) {
|
||||||
config->action_type_ = ACTION_SET;
|
config->action_type_ = ACTION_SET;
|
||||||
@ -545,7 +580,8 @@ static int ParseCommandLine(int argc, const char* argv[],
|
|||||||
} else {
|
} else {
|
||||||
ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
|
ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
|
||||||
}
|
}
|
||||||
arg->subtype_ = SUBTYPE_LOOP;
|
arg->subtype_ =
|
||||||
|
!strcmp(argv[i], "-loop") ? SUBTYPE_LOOP : SUBTYPE_BGCOLOR;
|
||||||
arg->params_ = argv[i + 1];
|
arg->params_ = argv[i + 1];
|
||||||
++feature_arg_index;
|
++feature_arg_index;
|
||||||
i += 2;
|
i += 2;
|
||||||
@ -790,45 +826,71 @@ static int Process(const WebPMuxConfig* config) {
|
|||||||
|
|
||||||
case ACTION_SET:
|
case ACTION_SET:
|
||||||
switch (feature->type_) {
|
switch (feature->type_) {
|
||||||
case FEATURE_ANMF:
|
case FEATURE_ANMF: {
|
||||||
|
WebPMuxAnimParams params = { 0xFFFFFFFF, 0 };
|
||||||
mux = WebPMuxNew();
|
mux = WebPMuxNew();
|
||||||
if (mux == NULL) {
|
if (mux == NULL) {
|
||||||
ERROR_GOTO2("ERROR (%s): Could not allocate a mux object.\n",
|
ERROR_GOTO2("ERROR (%s): Could not allocate a mux object.\n",
|
||||||
ErrorString(WEBP_MUX_MEMORY_ERROR), Err2);
|
ErrorString(WEBP_MUX_MEMORY_ERROR), Err2);
|
||||||
}
|
}
|
||||||
for (index = 0; index < feature->arg_count_; ++index) {
|
for (index = 0; index < feature->arg_count_; ++index) {
|
||||||
if (feature->args_[index].subtype_ == SUBTYPE_LOOP) {
|
switch (feature->args_[index].subtype_) {
|
||||||
const long num = strtol(feature->args_[index].params_, NULL, 10);
|
case SUBTYPE_BGCOLOR: {
|
||||||
if (num < 0) {
|
uint32_t bgcolor;
|
||||||
ERROR_GOTO1("ERROR: Loop count must be non-negative.\n", Err2);
|
ok = ParseBgcolorArgs(feature->args_[index].params_, &bgcolor);
|
||||||
|
if (!ok) {
|
||||||
|
ERROR_GOTO1("ERROR: Could not parse the background color \n",
|
||||||
|
Err2);
|
||||||
}
|
}
|
||||||
err = WebPMuxSetLoopCount(mux, num);
|
params.bgcolor = bgcolor;
|
||||||
if (err != WEBP_MUX_OK) {
|
break;
|
||||||
ERROR_GOTO2("ERROR (%s): Could not set loop count.\n",
|
|
||||||
ErrorString(err), Err2);
|
|
||||||
}
|
}
|
||||||
} else if (feature->args_[index].subtype_ == SUBTYPE_FRM) {
|
case SUBTYPE_LOOP: {
|
||||||
|
const long loop_count =
|
||||||
|
strtol(feature->args_[index].params_, NULL, 10);
|
||||||
|
if (loop_count != (int)loop_count) {
|
||||||
|
// Note: This is only a 'necessary' condition for loop_count
|
||||||
|
// to be valid. The 'sufficient' conditioned in checked in
|
||||||
|
// WebPMuxSetAnimationParams() method called later.
|
||||||
|
ERROR_GOTO1("ERROR: Loop count must be in the range 0 to "
|
||||||
|
"65535.\n", Err2);
|
||||||
|
}
|
||||||
|
params.loop_count = (int)loop_count;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SUBTYPE_ANMF: {
|
||||||
WebPMuxFrameInfo frame;
|
WebPMuxFrameInfo frame;
|
||||||
|
frame.id = WEBP_CHUNK_ANMF;
|
||||||
ok = ReadFileToWebPData(feature->args_[index].filename_,
|
ok = ReadFileToWebPData(feature->args_[index].filename_,
|
||||||
&frame.bitstream);
|
&frame.bitstream);
|
||||||
if (!ok) goto Err2;
|
if (!ok) goto Err2;
|
||||||
ok = ParseFrameArgs(feature->args_[index].params_, &frame);
|
ok = ParseFrameArgs(feature->args_[index].params_, &frame);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
WebPDataClear(&frame.bitstream);
|
WebPDataClear(&frame.bitstream);
|
||||||
ERROR_GOTO1("ERROR: Could not parse frame properties.\n", Err2);
|
ERROR_GOTO1("ERROR: Could not parse frame properties.\n",
|
||||||
|
Err2);
|
||||||
}
|
}
|
||||||
frame.id = WEBP_CHUNK_ANMF;
|
|
||||||
err = WebPMuxPushFrame(mux, &frame, 1);
|
err = WebPMuxPushFrame(mux, &frame, 1);
|
||||||
WebPDataClear(&frame.bitstream);
|
WebPDataClear(&frame.bitstream);
|
||||||
if (err != WEBP_MUX_OK) {
|
if (err != WEBP_MUX_OK) {
|
||||||
ERROR_GOTO3("ERROR (%s): Could not add a frame at index %d.\n",
|
ERROR_GOTO3("ERROR (%s): Could not add a frame at index %d."
|
||||||
ErrorString(err), index, Err2);
|
"\n", ErrorString(err), index, Err2);
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ERROR_GOTO1("ERROR: Invalid subtype for 'frame'", Err2);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
ERROR_GOTO1("ERROR: Invalid subtype for 'frame'", Err2);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = WebPMuxSetAnimationParams(mux, ¶ms);
|
||||||
|
if (err != WEBP_MUX_OK) {
|
||||||
|
ERROR_GOTO2("ERROR (%s): Could not set animation parameters.\n",
|
||||||
|
ErrorString(err), Err2);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case FEATURE_FRGM:
|
case FEATURE_FRGM:
|
||||||
mux = WebPMuxNew();
|
mux = WebPMuxNew();
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
.\" Hey, EMACS: -*- nroff -*-
|
.\" Hey, EMACS: -*- nroff -*-
|
||||||
.TH WEBPMUX 1 "January 24, 2012"
|
.TH WEBPMUX 1 "October 23, 2012"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
webpmux \- command line tool to create WebP Mux/container file.
|
webpmux \- command line tool to create WebP Mux/container file.
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
@ -28,10 +28,16 @@ webpmux \- command line tool to create WebP Mux/container file.
|
|||||||
.br
|
.br
|
||||||
.B webpmux \-frame
|
.B webpmux \-frame
|
||||||
.I FRAME_OPTIONS
|
.I FRAME_OPTIONS
|
||||||
.B [\-frame...] \-loop
|
.B [ \-frame ... ] [ \-loop
|
||||||
.I LOOP_COUNT
|
.I LOOP_COUNT
|
||||||
.B \-o
|
.B ]
|
||||||
|
.br
|
||||||
|
.RS 8
|
||||||
|
.B [ \-bgcolor
|
||||||
|
.I BACKGROUND_COLOR
|
||||||
|
.B ] \-o
|
||||||
.I OUTPUT
|
.I OUTPUT
|
||||||
|
.RE
|
||||||
.br
|
.br
|
||||||
.B webpmux \-info
|
.B webpmux \-info
|
||||||
.I INPUT
|
.I INPUT
|
||||||
@ -98,12 +104,21 @@ image offset for this fragment.
|
|||||||
|
|
||||||
.SS FRAME_OPTIONS (\-frame)
|
.SS FRAME_OPTIONS (\-frame)
|
||||||
.TP
|
.TP
|
||||||
.B file_i +xi+yi+di
|
.B file_i +xi+yi+di+mi
|
||||||
Where: 'file_i' is the i'th frame (WebP format), 'xi','yi' specify the image
|
Where: 'file_i' is the i'th frame (WebP format), 'xi','yi' specify the image
|
||||||
offset for this frame and 'di' is the pause duration before next frame.
|
offset for this frame, 'di' is the pause duration before next frame and 'mi' is
|
||||||
|
the dispose method for this frame (0 or 1).
|
||||||
.TP
|
.TP
|
||||||
.B \-loop n
|
.B \-loop n
|
||||||
Loop the frames n number of times. 0 indicates the frames should loop forever.
|
Loop the frames n number of times. 0 indicates the frames should loop forever.
|
||||||
|
Valid range is 0 to 65535 [Default: 0 (infinite)].
|
||||||
|
.TP
|
||||||
|
.B \-bgcolor A,R,G,B
|
||||||
|
Background color of the canvas.
|
||||||
|
.br
|
||||||
|
where: 'A', 'R', 'G' and 'B' are integers in the range 0 to 255 specifying the
|
||||||
|
Alpha, Red, Green and Blue component values respectively
|
||||||
|
[Default: 255,255,255,255].
|
||||||
|
|
||||||
.SS INPUT
|
.SS INPUT
|
||||||
.TP
|
.TP
|
||||||
@ -144,7 +159,10 @@ webpmux \-get exif exif_container.webp \-o image_metadata.exif
|
|||||||
webpmux \-strip exif exif_container.webp \-o without_exif.webp
|
webpmux \-strip exif exif_container.webp \-o without_exif.webp
|
||||||
.br
|
.br
|
||||||
webpmux \-frame anim_1.webp +0+0+0 \-frame anim_2.webp +50+50+0 \-loop 10
|
webpmux \-frame anim_1.webp +0+0+0 \-frame anim_2.webp +50+50+0 \-loop 10
|
||||||
\-o anim_container.webp
|
.br
|
||||||
|
.RS 8
|
||||||
|
\-bgcolor 255,255,255,255 \-o anim_container.webp
|
||||||
|
.RE
|
||||||
.br
|
.br
|
||||||
webpmux \-get frame 2 anim_container.webp \-o frame_2.webp
|
webpmux \-get frame 2 anim_container.webp \-o frame_2.webp
|
||||||
.br
|
.br
|
||||||
|
@ -39,6 +39,7 @@ typedef struct Frame {
|
|||||||
int x_offset_, y_offset_;
|
int x_offset_, y_offset_;
|
||||||
int width_, height_;
|
int width_, height_;
|
||||||
int duration_;
|
int duration_;
|
||||||
|
WebPMuxAnimDispose dispose_method_;
|
||||||
int is_fragment_; // this is a frame fragment (and not a full frame).
|
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 frame_num_; // the referent frame number for use in assembling fragments.
|
||||||
int complete_; // img_components_ contains a full image.
|
int complete_; // img_components_ contains a full image.
|
||||||
@ -58,6 +59,7 @@ struct WebPDemuxer {
|
|||||||
uint32_t feature_flags_;
|
uint32_t feature_flags_;
|
||||||
int canvas_width_, canvas_height_;
|
int canvas_width_, canvas_height_;
|
||||||
int loop_count_;
|
int loop_count_;
|
||||||
|
uint32_t bgcolor_;
|
||||||
int num_frames_;
|
int num_frames_;
|
||||||
Frame* frames_;
|
Frame* frames_;
|
||||||
Chunk* chunks_; // non-image chunks
|
Chunk* chunks_; // non-image chunks
|
||||||
@ -290,9 +292,7 @@ static ParseStatus NewFrame(const MemBuffer* const mem,
|
|||||||
static ParseStatus ParseFrame(
|
static ParseStatus ParseFrame(
|
||||||
WebPDemuxer* const dmux, uint32_t frame_chunk_size) {
|
WebPDemuxer* const dmux, uint32_t frame_chunk_size) {
|
||||||
const int has_frames = !!(dmux->feature_flags_ & ANIMATION_FLAG);
|
const int has_frames = !!(dmux->feature_flags_ & ANIMATION_FLAG);
|
||||||
const uint32_t padding = (ANMF_CHUNK_SIZE & 1);
|
const uint32_t anmf_payload_size = frame_chunk_size - ANMF_CHUNK_SIZE;
|
||||||
const uint32_t anmf_payload_size =
|
|
||||||
frame_chunk_size - (ANMF_CHUNK_SIZE + padding);
|
|
||||||
int added_frame = 0;
|
int added_frame = 0;
|
||||||
MemBuffer* const mem = &dmux->mem_;
|
MemBuffer* const mem = &dmux->mem_;
|
||||||
Frame* frame;
|
Frame* frame;
|
||||||
@ -304,8 +304,8 @@ static ParseStatus ParseFrame(
|
|||||||
frame->y_offset_ = 2 * GetLE24s(mem);
|
frame->y_offset_ = 2 * GetLE24s(mem);
|
||||||
frame->width_ = 1 + GetLE24s(mem);
|
frame->width_ = 1 + GetLE24s(mem);
|
||||||
frame->height_ = 1 + GetLE24s(mem);
|
frame->height_ = 1 + GetLE24s(mem);
|
||||||
frame->duration_ = 1 + GetLE24s(mem);
|
frame->duration_ = GetLE24s(mem);
|
||||||
Skip(mem, padding);
|
frame->dispose_method_ = (WebPMuxAnimDispose)(GetByte(mem) & 1);
|
||||||
if (frame->width_ * (uint64_t)frame->height_ >= MAX_IMAGE_AREA) {
|
if (frame->width_ * (uint64_t)frame->height_ >= MAX_IMAGE_AREA) {
|
||||||
return PARSE_ERROR;
|
return PARSE_ERROR;
|
||||||
}
|
}
|
||||||
@ -331,9 +331,7 @@ static ParseStatus ParseFrame(
|
|||||||
static ParseStatus ParseFragment(WebPDemuxer* const dmux,
|
static ParseStatus ParseFragment(WebPDemuxer* const dmux,
|
||||||
uint32_t fragment_chunk_size) {
|
uint32_t fragment_chunk_size) {
|
||||||
const int has_fragments = !!(dmux->feature_flags_ & FRAGMENTS_FLAG);
|
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;
|
||||||
const uint32_t frgm_payload_size =
|
|
||||||
fragment_chunk_size - (FRGM_CHUNK_SIZE + padding);
|
|
||||||
int added_fragment = 0;
|
int added_fragment = 0;
|
||||||
MemBuffer* const mem = &dmux->mem_;
|
MemBuffer* const mem = &dmux->mem_;
|
||||||
Frame* frame;
|
Frame* frame;
|
||||||
@ -344,7 +342,6 @@ static ParseStatus ParseFragment(WebPDemuxer* const dmux,
|
|||||||
frame->is_fragment_ = 1;
|
frame->is_fragment_ = 1;
|
||||||
frame->x_offset_ = 2 * GetLE24s(mem);
|
frame->x_offset_ = 2 * GetLE24s(mem);
|
||||||
frame->y_offset_ = 2 * GetLE24s(mem);
|
frame->y_offset_ = 2 * GetLE24s(mem);
|
||||||
Skip(mem, padding);
|
|
||||||
|
|
||||||
// Store a fragment only if the fragments flag is set and all data for this
|
// Store a fragment only if the fragments flag is set and all data for this
|
||||||
// fragment is available.
|
// fragment is available.
|
||||||
@ -444,7 +441,7 @@ static ParseStatus ParseSingleImage(WebPDemuxer* const dmux) {
|
|||||||
|
|
||||||
static ParseStatus ParseVP8X(WebPDemuxer* const dmux) {
|
static ParseStatus ParseVP8X(WebPDemuxer* const dmux) {
|
||||||
MemBuffer* const mem = &dmux->mem_;
|
MemBuffer* const mem = &dmux->mem_;
|
||||||
int loop_chunks = 0;
|
int anim_chunks = 0;
|
||||||
uint32_t vp8x_size;
|
uint32_t vp8x_size;
|
||||||
ParseStatus status = PARSE_OK;
|
ParseStatus status = PARSE_OK;
|
||||||
|
|
||||||
@ -493,15 +490,16 @@ static ParseStatus ParseVP8X(WebPDemuxer* const dmux) {
|
|||||||
status = ParseSingleImage(dmux);
|
status = ParseSingleImage(dmux);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case MKFOURCC('L', 'O', 'O', 'P'): {
|
case MKFOURCC('A', 'N', 'I', 'M'): {
|
||||||
if (chunk_size_padded < LOOP_CHUNK_SIZE) return PARSE_ERROR;
|
if (chunk_size_padded < ANIM_CHUNK_SIZE) return PARSE_ERROR;
|
||||||
|
|
||||||
if (MemDataSize(mem) < chunk_size_padded) {
|
if (MemDataSize(mem) < chunk_size_padded) {
|
||||||
status = PARSE_NEED_MORE_DATA;
|
status = PARSE_NEED_MORE_DATA;
|
||||||
} else if (loop_chunks == 0) {
|
} else if (anim_chunks == 0) {
|
||||||
++loop_chunks;
|
++anim_chunks;
|
||||||
|
dmux->bgcolor_ = GetLE32(mem);
|
||||||
dmux->loop_count_ = GetLE16s(mem);
|
dmux->loop_count_ = GetLE16s(mem);
|
||||||
Skip(mem, chunk_size_padded - LOOP_CHUNK_SIZE);
|
Skip(mem, chunk_size_padded - ANIM_CHUNK_SIZE);
|
||||||
} else {
|
} else {
|
||||||
store_chunk = 0;
|
store_chunk = 0;
|
||||||
goto Skip;
|
goto Skip;
|
||||||
@ -629,6 +627,7 @@ static int IsValidExtendedFormat(const WebPDemuxer* const dmux) {
|
|||||||
static void InitDemux(WebPDemuxer* const dmux, const MemBuffer* const mem) {
|
static void InitDemux(WebPDemuxer* const dmux, const MemBuffer* const mem) {
|
||||||
dmux->state_ = WEBP_DEMUX_PARSING_HEADER;
|
dmux->state_ = WEBP_DEMUX_PARSING_HEADER;
|
||||||
dmux->loop_count_ = 1;
|
dmux->loop_count_ = 1;
|
||||||
|
dmux->bgcolor_ = 0xFFFFFFFF; // White background by default.
|
||||||
dmux->canvas_width_ = -1;
|
dmux->canvas_width_ = -1;
|
||||||
dmux->canvas_height_ = -1;
|
dmux->canvas_height_ = -1;
|
||||||
dmux->mem_ = *mem;
|
dmux->mem_ = *mem;
|
||||||
@ -700,6 +699,7 @@ uint32_t WebPDemuxGetI(const WebPDemuxer* dmux, WebPFormatFeature feature) {
|
|||||||
case WEBP_FF_CANVAS_WIDTH: return (uint32_t)dmux->canvas_width_;
|
case WEBP_FF_CANVAS_WIDTH: return (uint32_t)dmux->canvas_width_;
|
||||||
case WEBP_FF_CANVAS_HEIGHT: return (uint32_t)dmux->canvas_height_;
|
case WEBP_FF_CANVAS_HEIGHT: return (uint32_t)dmux->canvas_height_;
|
||||||
case WEBP_FF_LOOP_COUNT: return (uint32_t)dmux->loop_count_;
|
case WEBP_FF_LOOP_COUNT: return (uint32_t)dmux->loop_count_;
|
||||||
|
case WEBP_FF_BACKGROUND_COLOR: return dmux->bgcolor_;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -778,6 +778,7 @@ static int SynthesizeFrame(const WebPDemuxer* const dmux,
|
|||||||
iter->width = fragment->width_;
|
iter->width = fragment->width_;
|
||||||
iter->height = fragment->height_;
|
iter->height = fragment->height_;
|
||||||
iter->duration = fragment->duration_;
|
iter->duration = fragment->duration_;
|
||||||
|
iter->dispose_method = fragment->dispose_method_;
|
||||||
iter->complete = fragment->complete_;
|
iter->complete = fragment->complete_;
|
||||||
iter->fragment.bytes = payload;
|
iter->fragment.bytes = payload;
|
||||||
iter->fragment.size = payload_size;
|
iter->fragment.size = payload_size;
|
||||||
|
@ -47,7 +47,7 @@ static void MuxRelease(WebPMux* const mux) {
|
|||||||
MuxImageDeleteAll(&mux->images_);
|
MuxImageDeleteAll(&mux->images_);
|
||||||
DeleteAllChunks(&mux->vp8x_);
|
DeleteAllChunks(&mux->vp8x_);
|
||||||
DeleteAllChunks(&mux->iccp_);
|
DeleteAllChunks(&mux->iccp_);
|
||||||
DeleteAllChunks(&mux->loop_);
|
DeleteAllChunks(&mux->anim_);
|
||||||
DeleteAllChunks(&mux->exif_);
|
DeleteAllChunks(&mux->exif_);
|
||||||
DeleteAllChunks(&mux->xmp_);
|
DeleteAllChunks(&mux->xmp_);
|
||||||
DeleteAllChunks(&mux->unknown_);
|
DeleteAllChunks(&mux->unknown_);
|
||||||
@ -82,7 +82,7 @@ static WebPMuxError MuxSet(WebPMux* const mux, CHUNK_INDEX idx, uint32_t nth,
|
|||||||
ChunkInit(&chunk);
|
ChunkInit(&chunk);
|
||||||
SWITCH_ID_LIST(IDX_VP8X, &mux->vp8x_);
|
SWITCH_ID_LIST(IDX_VP8X, &mux->vp8x_);
|
||||||
SWITCH_ID_LIST(IDX_ICCP, &mux->iccp_);
|
SWITCH_ID_LIST(IDX_ICCP, &mux->iccp_);
|
||||||
SWITCH_ID_LIST(IDX_LOOP, &mux->loop_);
|
SWITCH_ID_LIST(IDX_ANIM, &mux->anim_);
|
||||||
SWITCH_ID_LIST(IDX_EXIF, &mux->exif_);
|
SWITCH_ID_LIST(IDX_EXIF, &mux->exif_);
|
||||||
SWITCH_ID_LIST(IDX_XMP, &mux->xmp_);
|
SWITCH_ID_LIST(IDX_XMP, &mux->xmp_);
|
||||||
if (idx == IDX_UNKNOWN && data->size > TAG_SIZE) {
|
if (idx == IDX_UNKNOWN && data->size > TAG_SIZE) {
|
||||||
@ -109,10 +109,9 @@ static WebPMuxError MuxAddChunk(WebPMux* const mux, uint32_t nth, uint32_t tag,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create data for frame/tile given image data, offsets and duration.
|
// Create data for frame/tile given image data, offsets and duration.
|
||||||
static WebPMuxError CreateFrameFragmentData(const WebPData* const image,
|
static WebPMuxError CreateFrameFragmentData(
|
||||||
int x_offset, int y_offset,
|
const WebPData* const image, int x_offset, int y_offset, int duration,
|
||||||
int duration, int is_lossless,
|
WebPMuxAnimDispose dispose_method, int is_lossless, int is_frame,
|
||||||
int is_frame,
|
|
||||||
WebPData* const frame_frgm) {
|
WebPData* const frame_frgm) {
|
||||||
int width;
|
int width;
|
||||||
int height;
|
int height;
|
||||||
@ -124,7 +123,8 @@ static WebPMuxError CreateFrameFragmentData(const WebPData* const image,
|
|||||||
VP8GetInfo(image->bytes, image->size, image->size, &width, &height);
|
VP8GetInfo(image->bytes, image->size, image->size, &width, &height);
|
||||||
if (!ok) return WEBP_MUX_INVALID_ARGUMENT;
|
if (!ok) return WEBP_MUX_INVALID_ARGUMENT;
|
||||||
|
|
||||||
assert(width > 0 && height > 0 && duration > 0);
|
assert(width > 0 && height > 0 && duration >= 0);
|
||||||
|
assert(dispose_method == (dispose_method & 1));
|
||||||
// Note: assertion on upper bounds is done in PutLE24().
|
// Note: assertion on upper bounds is done in PutLE24().
|
||||||
|
|
||||||
frame_frgm_bytes = (uint8_t*)malloc(frame_frgm_size);
|
frame_frgm_bytes = (uint8_t*)malloc(frame_frgm_size);
|
||||||
@ -136,7 +136,8 @@ static WebPMuxError CreateFrameFragmentData(const WebPData* const image,
|
|||||||
if (is_frame) {
|
if (is_frame) {
|
||||||
PutLE24(frame_frgm_bytes + 6, width - 1);
|
PutLE24(frame_frgm_bytes + 6, width - 1);
|
||||||
PutLE24(frame_frgm_bytes + 9, height - 1);
|
PutLE24(frame_frgm_bytes + 9, height - 1);
|
||||||
PutLE24(frame_frgm_bytes + 12, duration - 1);
|
PutLE24(frame_frgm_bytes + 12, duration);
|
||||||
|
frame_frgm_bytes[15] = (dispose_method & 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
frame_frgm->bytes = frame_frgm_bytes;
|
frame_frgm->bytes = frame_frgm_bytes;
|
||||||
@ -200,11 +201,6 @@ static WebPMuxError MuxDeleteAllNamedData(WebPMux* const mux, uint32_t tag) {
|
|||||||
return DeleteChunks(chunk_list, tag);
|
return DeleteChunks(chunk_list, tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
static WebPMuxError DeleteLoopCount(WebPMux* const mux) {
|
|
||||||
if (mux == NULL) return WEBP_MUX_INVALID_ARGUMENT;
|
|
||||||
return MuxDeleteAllNamedData(mux, kChunks[IDX_LOOP].tag);
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// Set API(s).
|
// Set API(s).
|
||||||
|
|
||||||
@ -333,16 +329,20 @@ WebPMuxError WebPMuxPushFrame(WebPMux* mux, const WebPMuxFrameInfo* frame,
|
|||||||
const int x_offset = frame->x_offset & ~1; // Snap offsets to even.
|
const int x_offset = frame->x_offset & ~1; // Snap offsets to even.
|
||||||
const int y_offset = frame->y_offset & ~1;
|
const int y_offset = frame->y_offset & ~1;
|
||||||
const int duration = is_frame ? frame->duration : 1 /* unused */;
|
const int duration = is_frame ? frame->duration : 1 /* unused */;
|
||||||
|
const WebPMuxAnimDispose dispose_method =
|
||||||
|
is_frame ? frame->dispose_method : 0 /* unused */;
|
||||||
const uint32_t tag = kChunks[is_frame ? IDX_ANMF : IDX_FRGM].tag;
|
const uint32_t tag = kChunks[is_frame ? IDX_ANMF : IDX_FRGM].tag;
|
||||||
WebPData frame_frgm;
|
WebPData frame_frgm;
|
||||||
if (x_offset < 0 || x_offset >= MAX_POSITION_OFFSET ||
|
if (x_offset < 0 || x_offset >= MAX_POSITION_OFFSET ||
|
||||||
y_offset < 0 || y_offset >= MAX_POSITION_OFFSET ||
|
y_offset < 0 || y_offset >= MAX_POSITION_OFFSET ||
|
||||||
(duration <= 0 || duration > MAX_DURATION)) {
|
(duration < 0 || duration >= MAX_DURATION) ||
|
||||||
|
dispose_method != (dispose_method & 1)) {
|
||||||
err = WEBP_MUX_INVALID_ARGUMENT;
|
err = WEBP_MUX_INVALID_ARGUMENT;
|
||||||
goto Err;
|
goto Err;
|
||||||
}
|
}
|
||||||
err = CreateFrameFragmentData(&wpi.img_->data_, x_offset, y_offset,
|
err = CreateFrameFragmentData(&wpi.img_->data_, x_offset, y_offset,
|
||||||
duration, is_lossless, is_frame, &frame_frgm);
|
duration, dispose_method, is_lossless,
|
||||||
|
is_frame, &frame_frgm);
|
||||||
if (err != WEBP_MUX_OK) goto Err;
|
if (err != WEBP_MUX_OK) goto Err;
|
||||||
// Add frame/fragment chunk (with copy_data = 1).
|
// Add frame/fragment chunk (with copy_data = 1).
|
||||||
err = AddDataToChunkList(&frame_frgm, 1, tag, &wpi.header_);
|
err = AddDataToChunkList(&frame_frgm, 1, tag, &wpi.header_);
|
||||||
@ -362,20 +362,24 @@ WebPMuxError WebPMuxPushFrame(WebPMux* mux, const WebPMuxFrameInfo* frame,
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
WebPMuxError WebPMuxSetLoopCount(WebPMux* mux, int loop_count) {
|
WebPMuxError WebPMuxSetAnimationParams(WebPMux* mux,
|
||||||
|
const WebPMuxAnimParams* params) {
|
||||||
WebPMuxError err;
|
WebPMuxError err;
|
||||||
uint8_t data[LOOP_CHUNK_SIZE];
|
uint8_t data[ANIM_CHUNK_SIZE];
|
||||||
|
|
||||||
if (mux == NULL) return WEBP_MUX_INVALID_ARGUMENT;
|
if (mux == NULL || params == NULL) return WEBP_MUX_INVALID_ARGUMENT;
|
||||||
if (loop_count >= MAX_LOOP_COUNT) return WEBP_MUX_INVALID_ARGUMENT;
|
if (params->loop_count < 0 || params->loop_count >= MAX_LOOP_COUNT) {
|
||||||
|
return WEBP_MUX_INVALID_ARGUMENT;
|
||||||
|
}
|
||||||
|
|
||||||
// Delete the existing LOOP chunk(s).
|
// Delete any existing ANIM chunk(s).
|
||||||
err = DeleteLoopCount(mux);
|
err = MuxDeleteAllNamedData(mux, kChunks[IDX_ANIM].tag);
|
||||||
if (err != WEBP_MUX_OK && err != WEBP_MUX_NOT_FOUND) return err;
|
if (err != WEBP_MUX_OK && err != WEBP_MUX_NOT_FOUND) return err;
|
||||||
|
|
||||||
// Add the given loop count.
|
// Set the animation parameters.
|
||||||
PutLE16(data, loop_count);
|
PutLE32(data, params->bgcolor);
|
||||||
return MuxAddChunk(mux, 1, kChunks[IDX_LOOP].tag, data, sizeof(data), 1);
|
PutLE16(data + 4, params->loop_count);
|
||||||
|
return MuxAddChunk(mux, 1, kChunks[IDX_ANIM].tag, data, sizeof(data), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
@ -408,7 +412,7 @@ static WebPMuxError GetFrameFragmentInfo(
|
|||||||
|
|
||||||
*x_offset = 2 * GetLE24(data->bytes + 0);
|
*x_offset = 2 * GetLE24(data->bytes + 0);
|
||||||
*y_offset = 2 * GetLE24(data->bytes + 3);
|
*y_offset = 2 * GetLE24(data->bytes + 3);
|
||||||
if (is_frame) *duration = 1 + GetLE24(data->bytes + 12);
|
if (is_frame) *duration = GetLE24(data->bytes + 12);
|
||||||
return WEBP_MUX_OK;
|
return WEBP_MUX_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -586,21 +590,21 @@ WebPMuxError WebPMuxAssemble(WebPMux* mux, WebPData* assembled_data) {
|
|||||||
uint8_t* data = NULL;
|
uint8_t* data = NULL;
|
||||||
uint8_t* dst = NULL;
|
uint8_t* dst = NULL;
|
||||||
int num_frames;
|
int num_frames;
|
||||||
int num_loop_chunks;
|
int num_anim_chunks;
|
||||||
WebPMuxError err;
|
WebPMuxError err;
|
||||||
|
|
||||||
if (mux == NULL || assembled_data == NULL) {
|
if (mux == NULL || assembled_data == NULL) {
|
||||||
return WEBP_MUX_INVALID_ARGUMENT;
|
return WEBP_MUX_INVALID_ARGUMENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove LOOP chunk if unnecessary.
|
// Remove ANIM chunk if unnecessary.
|
||||||
err = WebPMuxNumChunks(mux, kChunks[IDX_LOOP].id, &num_loop_chunks);
|
err = WebPMuxNumChunks(mux, kChunks[IDX_ANIM].id, &num_anim_chunks);
|
||||||
if (err != WEBP_MUX_OK) return err;
|
if (err != WEBP_MUX_OK) return err;
|
||||||
if (num_loop_chunks >= 1) {
|
if (num_anim_chunks >= 1) {
|
||||||
err = WebPMuxNumChunks(mux, kChunks[IDX_ANMF].id, &num_frames);
|
err = WebPMuxNumChunks(mux, kChunks[IDX_ANMF].id, &num_frames);
|
||||||
if (err != WEBP_MUX_OK) return err;
|
if (err != WEBP_MUX_OK) return err;
|
||||||
if (num_frames == 0) {
|
if (num_frames == 0) {
|
||||||
err = DeleteLoopCount(mux);
|
err = MuxDeleteAllNamedData(mux, kChunks[IDX_ANIM].tag);
|
||||||
if (err != WEBP_MUX_OK) return err;
|
if (err != WEBP_MUX_OK) return err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -611,7 +615,7 @@ WebPMuxError WebPMuxAssemble(WebPMux* mux, WebPData* assembled_data) {
|
|||||||
|
|
||||||
// Allocate data.
|
// Allocate data.
|
||||||
size = ChunksListDiskSize(mux->vp8x_) + ChunksListDiskSize(mux->iccp_)
|
size = ChunksListDiskSize(mux->vp8x_) + ChunksListDiskSize(mux->iccp_)
|
||||||
+ ChunksListDiskSize(mux->loop_) + MuxImageListDiskSize(mux->images_)
|
+ ChunksListDiskSize(mux->anim_) + MuxImageListDiskSize(mux->images_)
|
||||||
+ ChunksListDiskSize(mux->exif_) + ChunksListDiskSize(mux->xmp_)
|
+ ChunksListDiskSize(mux->exif_) + ChunksListDiskSize(mux->xmp_)
|
||||||
+ ChunksListDiskSize(mux->unknown_) + RIFF_HEADER_SIZE;
|
+ ChunksListDiskSize(mux->unknown_) + RIFF_HEADER_SIZE;
|
||||||
|
|
||||||
@ -622,7 +626,7 @@ WebPMuxError WebPMuxAssemble(WebPMux* mux, WebPData* assembled_data) {
|
|||||||
dst = MuxEmitRiffHeader(data, size);
|
dst = MuxEmitRiffHeader(data, size);
|
||||||
dst = ChunkListEmit(mux->vp8x_, dst);
|
dst = ChunkListEmit(mux->vp8x_, dst);
|
||||||
dst = ChunkListEmit(mux->iccp_, dst);
|
dst = ChunkListEmit(mux->iccp_, dst);
|
||||||
dst = ChunkListEmit(mux->loop_, dst);
|
dst = ChunkListEmit(mux->anim_, dst);
|
||||||
dst = MuxImageListEmit(mux->images_, dst);
|
dst = MuxImageListEmit(mux->images_, dst);
|
||||||
dst = ChunkListEmit(mux->exif_, dst);
|
dst = ChunkListEmit(mux->exif_, dst);
|
||||||
dst = ChunkListEmit(mux->xmp_, dst);
|
dst = ChunkListEmit(mux->xmp_, dst);
|
||||||
|
@ -30,7 +30,7 @@ typedef struct WebPChunk WebPChunk;
|
|||||||
struct WebPChunk {
|
struct WebPChunk {
|
||||||
uint32_t tag_;
|
uint32_t tag_;
|
||||||
int owner_; // True if *data_ memory is owned internally.
|
int owner_; // True if *data_ memory is owned internally.
|
||||||
// VP8X, Loop, and other internally created chunks
|
// VP8X, ANIM, and other internally created chunks
|
||||||
// like ANMF/FRGM are always owned.
|
// like ANMF/FRGM are always owned.
|
||||||
WebPData data_;
|
WebPData data_;
|
||||||
WebPChunk* next_;
|
WebPChunk* next_;
|
||||||
@ -53,7 +53,7 @@ struct WebPMux {
|
|||||||
WebPChunk* iccp_;
|
WebPChunk* iccp_;
|
||||||
WebPChunk* exif_;
|
WebPChunk* exif_;
|
||||||
WebPChunk* xmp_;
|
WebPChunk* xmp_;
|
||||||
WebPChunk* loop_;
|
WebPChunk* anim_;
|
||||||
WebPChunk* vp8x_;
|
WebPChunk* vp8x_;
|
||||||
|
|
||||||
WebPChunk* unknown_;
|
WebPChunk* unknown_;
|
||||||
@ -66,7 +66,7 @@ struct WebPMux {
|
|||||||
typedef enum {
|
typedef enum {
|
||||||
IDX_VP8X = 0,
|
IDX_VP8X = 0,
|
||||||
IDX_ICCP,
|
IDX_ICCP,
|
||||||
IDX_LOOP,
|
IDX_ANIM,
|
||||||
IDX_ANMF,
|
IDX_ANMF,
|
||||||
IDX_FRGM,
|
IDX_FRGM,
|
||||||
IDX_ALPHA,
|
IDX_ALPHA,
|
||||||
|
@ -22,7 +22,7 @@ extern "C" {
|
|||||||
const ChunkInfo kChunks[] = {
|
const ChunkInfo kChunks[] = {
|
||||||
{ MKFOURCC('V', 'P', '8', 'X'), WEBP_CHUNK_VP8X, VP8X_CHUNK_SIZE },
|
{ MKFOURCC('V', 'P', '8', 'X'), WEBP_CHUNK_VP8X, VP8X_CHUNK_SIZE },
|
||||||
{ MKFOURCC('I', 'C', 'C', 'P'), WEBP_CHUNK_ICCP, UNDEFINED_CHUNK_SIZE },
|
{ MKFOURCC('I', 'C', 'C', 'P'), WEBP_CHUNK_ICCP, UNDEFINED_CHUNK_SIZE },
|
||||||
{ MKFOURCC('L', 'O', 'O', 'P'), WEBP_CHUNK_LOOP, LOOP_CHUNK_SIZE },
|
{ MKFOURCC('A', 'N', 'I', 'M'), WEBP_CHUNK_ANIM, ANIM_CHUNK_SIZE },
|
||||||
{ MKFOURCC('A', 'N', 'M', 'F'), WEBP_CHUNK_ANMF, ANMF_CHUNK_SIZE },
|
{ MKFOURCC('A', 'N', 'M', 'F'), WEBP_CHUNK_ANMF, ANMF_CHUNK_SIZE },
|
||||||
{ MKFOURCC('F', 'R', 'G', 'M'), WEBP_CHUNK_FRGM, FRGM_CHUNK_SIZE },
|
{ MKFOURCC('F', 'R', 'G', 'M'), WEBP_CHUNK_FRGM, FRGM_CHUNK_SIZE },
|
||||||
{ MKFOURCC('A', 'L', 'P', 'H'), WEBP_CHUNK_ALPHA, UNDEFINED_CHUNK_SIZE },
|
{ MKFOURCC('A', 'L', 'P', 'H'), WEBP_CHUNK_ALPHA, UNDEFINED_CHUNK_SIZE },
|
||||||
@ -135,7 +135,7 @@ static int ChunkSearchListToSet(WebPChunk** chunk_list, uint32_t nth,
|
|||||||
WebPMuxError ChunkAssignData(WebPChunk* chunk, const WebPData* const data,
|
WebPMuxError ChunkAssignData(WebPChunk* chunk, const WebPData* const data,
|
||||||
int copy_data, uint32_t tag) {
|
int copy_data, uint32_t tag) {
|
||||||
// For internally allocated chunks, always copy data & make it owner of data.
|
// For internally allocated chunks, always copy data & make it owner of data.
|
||||||
if (tag == kChunks[IDX_VP8X].tag || tag == kChunks[IDX_LOOP].tag) {
|
if (tag == kChunks[IDX_VP8X].tag || tag == kChunks[IDX_ANIM].tag) {
|
||||||
copy_data = 1;
|
copy_data = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -465,7 +465,7 @@ WebPChunk** MuxGetChunkListFromId(const WebPMux* mux, WebPChunkId id) {
|
|||||||
switch (id) {
|
switch (id) {
|
||||||
case WEBP_CHUNK_VP8X: return (WebPChunk**)&mux->vp8x_;
|
case WEBP_CHUNK_VP8X: return (WebPChunk**)&mux->vp8x_;
|
||||||
case WEBP_CHUNK_ICCP: return (WebPChunk**)&mux->iccp_;
|
case WEBP_CHUNK_ICCP: return (WebPChunk**)&mux->iccp_;
|
||||||
case WEBP_CHUNK_LOOP: return (WebPChunk**)&mux->loop_;
|
case WEBP_CHUNK_ANIM: return (WebPChunk**)&mux->anim_;
|
||||||
case WEBP_CHUNK_EXIF: return (WebPChunk**)&mux->exif_;
|
case WEBP_CHUNK_EXIF: return (WebPChunk**)&mux->exif_;
|
||||||
case WEBP_CHUNK_XMP: return (WebPChunk**)&mux->xmp_;
|
case WEBP_CHUNK_XMP: return (WebPChunk**)&mux->xmp_;
|
||||||
case WEBP_CHUNK_UNKNOWN: return (WebPChunk**)&mux->unknown_;
|
case WEBP_CHUNK_UNKNOWN: return (WebPChunk**)&mux->unknown_;
|
||||||
@ -518,7 +518,7 @@ WebPMuxError MuxValidate(const WebPMux* const mux) {
|
|||||||
int num_iccp;
|
int num_iccp;
|
||||||
int num_exif;
|
int num_exif;
|
||||||
int num_xmp;
|
int num_xmp;
|
||||||
int num_loop_chunks;
|
int num_anim;
|
||||||
int num_frames;
|
int num_frames;
|
||||||
int num_fragments;
|
int num_fragments;
|
||||||
int num_vp8x;
|
int num_vp8x;
|
||||||
@ -548,19 +548,19 @@ WebPMuxError MuxValidate(const WebPMux* const mux) {
|
|||||||
err = ValidateChunk(mux, IDX_XMP, XMP_FLAG, flags, 1, &num_xmp);
|
err = ValidateChunk(mux, IDX_XMP, XMP_FLAG, flags, 1, &num_xmp);
|
||||||
if (err != WEBP_MUX_OK) return err;
|
if (err != WEBP_MUX_OK) return err;
|
||||||
|
|
||||||
// Animation: ANIMATION_FLAG, loop chunk and frame chunk(s) are consistent.
|
// Animation: ANIMATION_FLAG, ANIM chunk and ANMF chunk(s) are consistent.
|
||||||
// At most one loop chunk.
|
// At most one ANIM chunk.
|
||||||
err = ValidateChunk(mux, IDX_LOOP, NO_FLAG, flags, 1, &num_loop_chunks);
|
err = ValidateChunk(mux, IDX_ANIM, NO_FLAG, flags, 1, &num_anim);
|
||||||
if (err != WEBP_MUX_OK) return err;
|
if (err != WEBP_MUX_OK) return err;
|
||||||
err = ValidateChunk(mux, IDX_ANMF, NO_FLAG, flags, -1, &num_frames);
|
err = ValidateChunk(mux, IDX_ANMF, NO_FLAG, flags, -1, &num_frames);
|
||||||
if (err != WEBP_MUX_OK) return err;
|
if (err != WEBP_MUX_OK) return err;
|
||||||
|
|
||||||
{
|
{
|
||||||
const int has_animation = !!(flags & ANIMATION_FLAG);
|
const int has_animation = !!(flags & ANIMATION_FLAG);
|
||||||
if (has_animation && (num_loop_chunks == 0 || num_frames == 0)) {
|
if (has_animation && (num_anim == 0 || num_frames == 0)) {
|
||||||
return WEBP_MUX_INVALID_ARGUMENT;
|
return WEBP_MUX_INVALID_ARGUMENT;
|
||||||
}
|
}
|
||||||
if (!has_animation && (num_loop_chunks == 1 || num_frames > 0)) {
|
if (!has_animation && (num_anim == 1 || num_frames > 0)) {
|
||||||
return WEBP_MUX_INVALID_ARGUMENT;
|
return WEBP_MUX_INVALID_ARGUMENT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ static WebPMuxError MuxGet(const WebPMux* const mux, CHUNK_INDEX idx,
|
|||||||
|
|
||||||
SWITCH_ID_LIST(IDX_VP8X, mux->vp8x_);
|
SWITCH_ID_LIST(IDX_VP8X, mux->vp8x_);
|
||||||
SWITCH_ID_LIST(IDX_ICCP, mux->iccp_);
|
SWITCH_ID_LIST(IDX_ICCP, mux->iccp_);
|
||||||
SWITCH_ID_LIST(IDX_LOOP, mux->loop_);
|
SWITCH_ID_LIST(IDX_ANIM, mux->anim_);
|
||||||
SWITCH_ID_LIST(IDX_EXIF, mux->exif_);
|
SWITCH_ID_LIST(IDX_EXIF, mux->exif_);
|
||||||
SWITCH_ID_LIST(IDX_XMP, mux->xmp_);
|
SWITCH_ID_LIST(IDX_XMP, mux->xmp_);
|
||||||
SWITCH_ID_LIST(IDX_UNKNOWN, mux->unknown_);
|
SWITCH_ID_LIST(IDX_UNKNOWN, mux->unknown_);
|
||||||
@ -376,7 +376,10 @@ static WebPMuxError MuxGetFrameFragmentInternal(const WebPMuxImage* const wpi,
|
|||||||
// Extract info.
|
// Extract info.
|
||||||
frame->x_offset = 2 * GetLE24(frame_frgm_data->bytes + 0);
|
frame->x_offset = 2 * GetLE24(frame_frgm_data->bytes + 0);
|
||||||
frame->y_offset = 2 * GetLE24(frame_frgm_data->bytes + 3);
|
frame->y_offset = 2 * GetLE24(frame_frgm_data->bytes + 3);
|
||||||
frame->duration = is_frame ? 1 + GetLE24(frame_frgm_data->bytes + 12) : 1;
|
frame->duration = is_frame ? GetLE24(frame_frgm_data->bytes + 12) : 1;
|
||||||
|
frame->dispose_method =
|
||||||
|
is_frame ? (WebPMuxAnimDispose)(frame_frgm_data->bytes[15] & 1)
|
||||||
|
: WEBP_MUX_DISPOSE_NONE;
|
||||||
frame->id = ChunkGetIdFromTag(wpi->header_->tag_);
|
frame->id = ChunkGetIdFromTag(wpi->header_->tag_);
|
||||||
return SynthesizeBitstream(wpi, &frame->bitstream);
|
return SynthesizeBitstream(wpi, &frame->bitstream);
|
||||||
}
|
}
|
||||||
@ -403,16 +406,18 @@ WebPMuxError WebPMuxGetFrame(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
WebPMuxError WebPMuxGetLoopCount(const WebPMux* mux, int* loop_count) {
|
WebPMuxError WebPMuxGetAnimationParams(const WebPMux* mux,
|
||||||
WebPData image;
|
WebPMuxAnimParams* params) {
|
||||||
|
WebPData anim;
|
||||||
WebPMuxError err;
|
WebPMuxError err;
|
||||||
|
|
||||||
if (mux == NULL || loop_count == NULL) return WEBP_MUX_INVALID_ARGUMENT;
|
if (mux == NULL || params == NULL) return WEBP_MUX_INVALID_ARGUMENT;
|
||||||
|
|
||||||
err = MuxGet(mux, IDX_LOOP, 1, &image);
|
err = MuxGet(mux, IDX_ANIM, 1, &anim);
|
||||||
if (err != WEBP_MUX_OK) return err;
|
if (err != WEBP_MUX_OK) return err;
|
||||||
if (image.size < kChunks[WEBP_CHUNK_LOOP].size) return WEBP_MUX_BAD_DATA;
|
if (anim.size < kChunks[WEBP_CHUNK_ANIM].size) return WEBP_MUX_BAD_DATA;
|
||||||
*loop_count = GetLE16(image.bytes);
|
params->bgcolor = GetLE32(anim.bytes);
|
||||||
|
params->loop_count = GetLE16(anim.bytes + 4);
|
||||||
|
|
||||||
return WEBP_MUX_OK;
|
return WEBP_MUX_OK;
|
||||||
}
|
}
|
||||||
|
@ -65,8 +65,8 @@ typedef enum {
|
|||||||
#define CHUNK_SIZE_BYTES 4 // Size needed to store chunk's size.
|
#define CHUNK_SIZE_BYTES 4 // Size needed to store chunk's size.
|
||||||
#define CHUNK_HEADER_SIZE 8 // Size of a chunk header.
|
#define CHUNK_HEADER_SIZE 8 // Size of a chunk header.
|
||||||
#define RIFF_HEADER_SIZE 12 // Size of the RIFF header ("RIFFnnnnWEBP").
|
#define RIFF_HEADER_SIZE 12 // Size of the RIFF header ("RIFFnnnnWEBP").
|
||||||
#define ANMF_CHUNK_SIZE 15 // Size of a ANMF chunk.
|
#define ANMF_CHUNK_SIZE 16 // Size of an ANMF chunk.
|
||||||
#define LOOP_CHUNK_SIZE 2 // Size of a LOOP chunk.
|
#define ANIM_CHUNK_SIZE 6 // Size of an ANIM chunk.
|
||||||
#define FRGM_CHUNK_SIZE 6 // Size of a FRGM chunk.
|
#define FRGM_CHUNK_SIZE 6 // Size of a FRGM chunk.
|
||||||
#define VP8X_CHUNK_SIZE 10 // Size of a VP8X chunk.
|
#define VP8X_CHUNK_SIZE 10 // Size of a VP8X chunk.
|
||||||
|
|
||||||
|
@ -59,8 +59,10 @@ typedef struct WebPData WebPData;
|
|||||||
typedef enum WebPMuxError WebPMuxError;
|
typedef enum WebPMuxError WebPMuxError;
|
||||||
typedef enum WebPFeatureFlags WebPFeatureFlags;
|
typedef enum WebPFeatureFlags WebPFeatureFlags;
|
||||||
typedef enum WebPChunkId WebPChunkId;
|
typedef enum WebPChunkId WebPChunkId;
|
||||||
|
typedef enum WebPMuxAnimDispose WebPMuxAnimDispose;
|
||||||
#endif
|
#endif
|
||||||
typedef struct WebPMuxFrameInfo WebPMuxFrameInfo;
|
typedef struct WebPMuxFrameInfo WebPMuxFrameInfo;
|
||||||
|
typedef struct WebPMuxAnimParams WebPMuxAnimParams;
|
||||||
|
|
||||||
typedef struct WebPDemuxer WebPDemuxer;
|
typedef struct WebPDemuxer WebPDemuxer;
|
||||||
#if !(defined(__cplusplus) || defined(c_plusplus))
|
#if !(defined(__cplusplus) || defined(c_plusplus))
|
||||||
@ -94,7 +96,7 @@ enum WebPFeatureFlags {
|
|||||||
enum WebPChunkId {
|
enum WebPChunkId {
|
||||||
WEBP_CHUNK_VP8X, // VP8X
|
WEBP_CHUNK_VP8X, // VP8X
|
||||||
WEBP_CHUNK_ICCP, // ICCP
|
WEBP_CHUNK_ICCP, // ICCP
|
||||||
WEBP_CHUNK_LOOP, // LOOP
|
WEBP_CHUNK_ANIM, // ANIM
|
||||||
WEBP_CHUNK_ANMF, // ANMF
|
WEBP_CHUNK_ANMF, // ANMF
|
||||||
WEBP_CHUNK_FRGM, // FRGM
|
WEBP_CHUNK_FRGM, // FRGM
|
||||||
WEBP_CHUNK_ALPHA, // ALPH
|
WEBP_CHUNK_ALPHA, // ALPH
|
||||||
@ -220,6 +222,13 @@ WEBP_EXTERN(WebPMuxError) WebPMuxDeleteChunk(
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// Images.
|
// Images.
|
||||||
|
|
||||||
|
// Dispose method (animation only). Indicates how the area used by the current
|
||||||
|
// frame is to be treated before rendering the next frame on the canvas.
|
||||||
|
enum WebPMuxAnimDispose {
|
||||||
|
WEBP_MUX_DISPOSE_NONE, // Do not dispose.
|
||||||
|
WEBP_MUX_DISPOSE_BACKGROUND // Dispose to background color.
|
||||||
|
};
|
||||||
|
|
||||||
// Encapsulates data about a single frame/tile.
|
// Encapsulates data about a single frame/tile.
|
||||||
struct WebPMuxFrameInfo {
|
struct WebPMuxFrameInfo {
|
||||||
WebPData bitstream; // image data: can either be a raw VP8/VP8L bitstream
|
WebPData bitstream; // image data: can either be a raw VP8/VP8L bitstream
|
||||||
@ -230,7 +239,8 @@ struct WebPMuxFrameInfo {
|
|||||||
|
|
||||||
WebPChunkId id; // frame type: should be one of WEBP_CHUNK_ANMF,
|
WebPChunkId id; // frame type: should be one of WEBP_CHUNK_ANMF,
|
||||||
// WEBP_CHUNK_FRGM or WEBP_CHUNK_IMAGE
|
// WEBP_CHUNK_FRGM or WEBP_CHUNK_IMAGE
|
||||||
uint32_t pad[3]; // padding for later use
|
WebPMuxAnimDispose dispose_method; // Disposal method for the frame.
|
||||||
|
uint32_t pad[2]; // padding for later use
|
||||||
};
|
};
|
||||||
|
|
||||||
// Sets the (non-animated and non-fragmented) image in the mux object.
|
// Sets the (non-animated and non-fragmented) image in the mux object.
|
||||||
@ -300,28 +310,38 @@ WEBP_EXTERN(WebPMuxError) WebPMuxDeleteFrame(WebPMux* mux, uint32_t nth);
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// Animation.
|
// Animation.
|
||||||
|
|
||||||
// Sets the animation loop count in the mux object. Any existing loop count
|
// Animation parameters.
|
||||||
// value(s) will be removed.
|
struct WebPMuxAnimParams {
|
||||||
|
uint32_t bgcolor; // Background color of the canvas stored (in MSB order) as:
|
||||||
|
// Bits 00 to 07: Alpha.
|
||||||
|
// Bits 08 to 15: Red.
|
||||||
|
// Bits 16 to 23: Green.
|
||||||
|
// Bits 24 to 31: Blue.
|
||||||
|
int loop_count; // Number of times to repeat the animation [0 = infinite].
|
||||||
|
};
|
||||||
|
|
||||||
|
// Sets the animation parameters in the mux object. Any existing ANIM chunks
|
||||||
|
// will be removed.
|
||||||
// Parameters:
|
// Parameters:
|
||||||
// mux - (in/out) object in which loop chunk is to be set/added
|
// mux - (in/out) object in which ANIM chunk is to be set/added
|
||||||
// loop_count - (in) animation loop count value.
|
// params - (in) animation parameters.
|
||||||
// Note that loop_count of zero denotes infinite loop.
|
|
||||||
// Returns:
|
// Returns:
|
||||||
// WEBP_MUX_INVALID_ARGUMENT - if mux is NULL
|
// WEBP_MUX_INVALID_ARGUMENT - if either mux or params is NULL
|
||||||
// WEBP_MUX_MEMORY_ERROR - on memory allocation error.
|
// WEBP_MUX_MEMORY_ERROR - on memory allocation error.
|
||||||
// WEBP_MUX_OK - on success.
|
// WEBP_MUX_OK - on success.
|
||||||
WEBP_EXTERN(WebPMuxError) WebPMuxSetLoopCount(WebPMux* mux, int loop_count);
|
WEBP_EXTERN(WebPMuxError) WebPMuxSetAnimationParams(
|
||||||
|
WebPMux* mux, const WebPMuxAnimParams* params);
|
||||||
|
|
||||||
// Gets the animation loop count from the mux object.
|
// Gets the animation parameters from the mux object.
|
||||||
// Parameters:
|
// Parameters:
|
||||||
// mux - (in) object from which the loop count is to be fetched
|
// mux - (in) object from which the animation parameters to be fetched
|
||||||
// loop_count - (out) the loop_count value present in the LOOP chunk
|
// params - (out) animation parameters extracted from the ANIM chunk
|
||||||
// Returns:
|
// Returns:
|
||||||
// WEBP_MUX_INVALID_ARGUMENT - if either of mux or loop_count is NULL
|
// WEBP_MUX_INVALID_ARGUMENT - if either of mux or params is NULL
|
||||||
// WEBP_MUX_NOT_FOUND - if loop chunk is not present in mux object.
|
// WEBP_MUX_NOT_FOUND - if ANIM chunk is not present in mux object.
|
||||||
// WEBP_MUX_OK - on success.
|
// WEBP_MUX_OK - on success.
|
||||||
WEBP_EXTERN(WebPMuxError) WebPMuxGetLoopCount(const WebPMux* mux,
|
WEBP_EXTERN(WebPMuxError) WebPMuxGetAnimationParams(
|
||||||
int* loop_count);
|
const WebPMux* mux, WebPMuxAnimParams* params);
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// Misc Utilities.
|
// Misc Utilities.
|
||||||
@ -413,7 +433,8 @@ enum WebPFormatFeature {
|
|||||||
WEBP_FF_FORMAT_FLAGS, // Extended format flags present in the 'VP8X' chunk.
|
WEBP_FF_FORMAT_FLAGS, // Extended format flags present in the 'VP8X' chunk.
|
||||||
WEBP_FF_CANVAS_WIDTH,
|
WEBP_FF_CANVAS_WIDTH,
|
||||||
WEBP_FF_CANVAS_HEIGHT,
|
WEBP_FF_CANVAS_HEIGHT,
|
||||||
WEBP_FF_LOOP_COUNT
|
WEBP_FF_LOOP_COUNT,
|
||||||
|
WEBP_FF_BACKGROUND_COLOR
|
||||||
};
|
};
|
||||||
|
|
||||||
// Get the 'feature' value from the 'dmux'.
|
// Get the 'feature' value from the 'dmux'.
|
||||||
@ -433,6 +454,7 @@ struct WebPIterator {
|
|||||||
int x_offset, y_offset; // offset relative to the canvas.
|
int x_offset, y_offset; // offset relative to the canvas.
|
||||||
int width, height; // dimensions of this frame or fragment.
|
int width, height; // dimensions of this frame or fragment.
|
||||||
int duration; // display duration in milliseconds.
|
int duration; // display duration in milliseconds.
|
||||||
|
WebPMuxAnimDispose dispose_method; // dispose method for the frame.
|
||||||
int complete; // true if 'fragment' contains a full frame. partial images
|
int complete; // true if 'fragment' contains a full frame. partial images
|
||||||
// may still be decoded with the WebP incremental decoder.
|
// may still be decoded with the WebP incremental decoder.
|
||||||
WebPData fragment; // The frame or fragment given by 'frame_num' and
|
WebPData fragment; // The frame or fragment given by 'frame_num' and
|
||||||
|
Loading…
Reference in New Issue
Block a user