1 // Copyright 2011 Google Inc. All Rights Reserved.
2 //
3 // Use of this source code is governed by a BSD-style license
4 // that can be found in the COPYING file in the root of the source
5 // tree. An additional intellectual property rights grant can be found
6 // in the file PATENTS. All contributing project authors may
7 // be found in the AUTHORS file in the root of the source tree.
8 // -----------------------------------------------------------------------------
9 //
10 // Simple command-line to create a WebP container file and to extract or strip
11 // relevant data from the container file.
12 //
13 // Authors: Vikas ([email protected]),
14 // Urvang ([email protected])
15
16 /* Usage examples:
17
18 Create container WebP file:
19 webpmux -frame anim_1.webp +100+10+10 \
20 -frame anim_2.webp +100+25+25+1 \
21 -frame anim_3.webp +100+50+50+1 \
22 -frame anim_4.webp +100 \
23 -loop 10 -bgcolor 128,255,255,255 \
24 -o out_animation_container.webp
25
26 webpmux -set icc image_profile.icc in.webp -o out_icc_container.webp
27 webpmux -set exif image_metadata.exif in.webp -o out_exif_container.webp
28 webpmux -set xmp image_metadata.xmp in.webp -o out_xmp_container.webp
29 webpmux -set loop 1 in.webp -o out_looped.webp
30
31 Extract relevant data from WebP container file:
32 webpmux -get frame n in.webp -o out_frame.webp
33 webpmux -get icc in.webp -o image_profile.icc
34 webpmux -get exif in.webp -o image_metadata.exif
35 webpmux -get xmp in.webp -o image_metadata.xmp
36
37 Strip data from WebP Container file:
38 webpmux -strip icc in.webp -o out.webp
39 webpmux -strip exif in.webp -o out.webp
40 webpmux -strip xmp in.webp -o out.webp
41
42 Change duration of frame intervals:
43 webpmux -duration 150 in.webp -o out.webp
44 webpmux -duration 33,2 in.webp -o out.webp
45 webpmux -duration 200,10,0 -duration 150,6,50 in.webp -o out.webp
46
47 Misc:
48 webpmux -info in.webp
49 webpmux [ -h | -help ]
50 webpmux -version
51 webpmux argument_file_name
52 */
53
54 #ifdef HAVE_CONFIG_H
55 #include "webp/config.h"
56 #endif
57
58 #include <assert.h>
59 #include <stdio.h>
60 #include <stdlib.h>
61 #include <string.h>
62 #include "webp/decode.h"
63 #include "webp/mux.h"
64 #include "../examples/example_util.h"
65 #include "../imageio/imageio_util.h"
66 #include "./unicode.h"
67
68 //------------------------------------------------------------------------------
69 // Config object to parse command-line arguments.
70
71 typedef enum {
72 NIL_ACTION = 0,
73 ACTION_GET,
74 ACTION_SET,
75 ACTION_STRIP,
76 ACTION_INFO,
77 ACTION_HELP,
78 ACTION_DURATION
79 } ActionType;
80
81 typedef enum {
82 NIL_SUBTYPE = 0,
83 SUBTYPE_ANMF,
84 SUBTYPE_LOOP,
85 SUBTYPE_BGCOLOR
86 } FeatureSubType;
87
88 typedef struct {
89 FeatureSubType subtype_;
90 const char* filename_;
91 const char* params_;
92 } FeatureArg;
93
94 typedef enum {
95 NIL_FEATURE = 0,
96 FEATURE_EXIF,
97 FEATURE_XMP,
98 FEATURE_ICCP,
99 FEATURE_ANMF,
100 FEATURE_DURATION,
101 FEATURE_LOOP,
102 FEATURE_BGCOLOR,
103 LAST_FEATURE
104 } FeatureType;
105
106 static const char* const kFourccList[LAST_FEATURE] = {
107 NULL, "EXIF", "XMP ", "ICCP", "ANMF"
108 };
109
110 static const char* const kDescriptions[LAST_FEATURE] = {
111 NULL, "EXIF metadata", "XMP metadata", "ICC profile",
112 "Animation frame"
113 };
114
115 typedef struct {
116 CommandLineArguments cmd_args_;
117
118 ActionType action_type_;
119 const char* input_;
120 const char* output_;
121 FeatureType type_;
122 FeatureArg* args_;
123 int arg_count_;
124 } Config;
125
126 //------------------------------------------------------------------------------
127 // Helper functions.
128
CountOccurrences(const CommandLineArguments * const args,const char * const arg)129 static int CountOccurrences(const CommandLineArguments* const args,
130 const char* const arg) {
131 int i;
132 int num_occurences = 0;
133
134 for (i = 0; i < args->argc_; ++i) {
135 if (!strcmp(args->argv_[i], arg)) {
136 ++num_occurences;
137 }
138 }
139 return num_occurences;
140 }
141
142 static const char* const kErrorMessages[-WEBP_MUX_NOT_ENOUGH_DATA + 1] = {
143 "WEBP_MUX_NOT_FOUND", "WEBP_MUX_INVALID_ARGUMENT", "WEBP_MUX_BAD_DATA",
144 "WEBP_MUX_MEMORY_ERROR", "WEBP_MUX_NOT_ENOUGH_DATA"
145 };
146
ErrorString(WebPMuxError err)147 static const char* ErrorString(WebPMuxError err) {
148 assert(err <= WEBP_MUX_NOT_FOUND && err >= WEBP_MUX_NOT_ENOUGH_DATA);
149 return kErrorMessages[-err];
150 }
151
152 #define RETURN_IF_ERROR(ERR_MSG) \
153 do { \
154 if (err != WEBP_MUX_OK) { \
155 fprintf(stderr, ERR_MSG); \
156 return err; \
157 } \
158 } while (0)
159
160 #define RETURN_IF_ERROR3(ERR_MSG, FORMAT_STR1, FORMAT_STR2) \
161 do { \
162 if (err != WEBP_MUX_OK) { \
163 fprintf(stderr, ERR_MSG, FORMAT_STR1, FORMAT_STR2); \
164 return err; \
165 } \
166 } while (0)
167
168 #define ERROR_GOTO1(ERR_MSG, LABEL) \
169 do { \
170 fprintf(stderr, ERR_MSG); \
171 ok = 0; \
172 goto LABEL; \
173 } while (0)
174
175 #define ERROR_GOTO2(ERR_MSG, FORMAT_STR, LABEL) \
176 do { \
177 fprintf(stderr, ERR_MSG, FORMAT_STR); \
178 ok = 0; \
179 goto LABEL; \
180 } while (0)
181
182 #define ERROR_GOTO3(ERR_MSG, FORMAT_STR1, FORMAT_STR2, LABEL) \
183 do { \
184 fprintf(stderr, ERR_MSG, FORMAT_STR1, FORMAT_STR2); \
185 ok = 0; \
186 goto LABEL; \
187 } while (0)
188
DisplayInfo(const WebPMux * mux)189 static WebPMuxError DisplayInfo(const WebPMux* mux) {
190 int width, height;
191 uint32_t flag;
192
193 WebPMuxError err = WebPMuxGetCanvasSize(mux, &width, &height);
194 assert(err == WEBP_MUX_OK); // As WebPMuxCreate() was successful earlier.
195 printf("Canvas size: %d x %d\n", width, height);
196
197 err = WebPMuxGetFeatures(mux, &flag);
198 RETURN_IF_ERROR("Failed to retrieve features\n");
199
200 if (flag == 0) {
201 printf("No features present.\n");
202 return err;
203 }
204
205 // Print the features present.
206 printf("Features present:");
207 if (flag & ANIMATION_FLAG) printf(" animation");
208 if (flag & ICCP_FLAG) printf(" ICC profile");
209 if (flag & EXIF_FLAG) printf(" EXIF metadata");
210 if (flag & XMP_FLAG) printf(" XMP metadata");
211 if (flag & ALPHA_FLAG) printf(" transparency");
212 printf("\n");
213
214 if (flag & ANIMATION_FLAG) {
215 const WebPChunkId id = WEBP_CHUNK_ANMF;
216 const char* const type_str = "frame";
217 int nFrames;
218
219 WebPMuxAnimParams params;
220 err = WebPMuxGetAnimationParams(mux, ¶ms);
221 assert(err == WEBP_MUX_OK);
222 printf("Background color : 0x%.8X Loop Count : %d\n",
223 params.bgcolor, params.loop_count);
224
225 err = WebPMuxNumChunks(mux, id, &nFrames);
226 assert(err == WEBP_MUX_OK);
227
228 printf("Number of %ss: %d\n", type_str, nFrames);
229 if (nFrames > 0) {
230 int i;
231 printf("No.: width height alpha x_offset y_offset ");
232 printf("duration dispose blend ");
233 printf("image_size compression\n");
234 for (i = 1; i <= nFrames; i++) {
235 WebPMuxFrameInfo frame;
236 err = WebPMuxGetFrame(mux, i, &frame);
237 if (err == WEBP_MUX_OK) {
238 WebPBitstreamFeatures features;
239 const VP8StatusCode status = WebPGetFeatures(
240 frame.bitstream.bytes, frame.bitstream.size, &features);
241 assert(status == VP8_STATUS_OK); // Checked by WebPMuxCreate().
242 (void)status;
243 printf("%3d: %5d %5d %5s %8d %8d ", i, features.width,
244 features.height, features.has_alpha ? "yes" : "no",
245 frame.x_offset, frame.y_offset);
246 {
247 const char* const dispose =
248 (frame.dispose_method == WEBP_MUX_DISPOSE_NONE) ? "none"
249 : "background";
250 const char* const blend =
251 (frame.blend_method == WEBP_MUX_BLEND) ? "yes" : "no";
252 printf("%8d %10s %5s ", frame.duration, dispose, blend);
253 }
254 printf("%10d %11s\n", (int)frame.bitstream.size,
255 (features.format == 1) ? "lossy" :
256 (features.format == 2) ? "lossless" :
257 "undefined");
258 }
259 WebPDataClear(&frame.bitstream);
260 RETURN_IF_ERROR3("Failed to retrieve %s#%d\n", type_str, i);
261 }
262 }
263 }
264
265 if (flag & ICCP_FLAG) {
266 WebPData icc_profile;
267 err = WebPMuxGetChunk(mux, "ICCP", &icc_profile);
268 assert(err == WEBP_MUX_OK);
269 printf("Size of the ICC profile data: %d\n", (int)icc_profile.size);
270 }
271
272 if (flag & EXIF_FLAG) {
273 WebPData exif;
274 err = WebPMuxGetChunk(mux, "EXIF", &exif);
275 assert(err == WEBP_MUX_OK);
276 printf("Size of the EXIF metadata: %d\n", (int)exif.size);
277 }
278
279 if (flag & XMP_FLAG) {
280 WebPData xmp;
281 err = WebPMuxGetChunk(mux, "XMP ", &xmp);
282 assert(err == WEBP_MUX_OK);
283 printf("Size of the XMP metadata: %d\n", (int)xmp.size);
284 }
285
286 if ((flag & ALPHA_FLAG) && !(flag & ANIMATION_FLAG)) {
287 WebPMuxFrameInfo image;
288 err = WebPMuxGetFrame(mux, 1, &image);
289 if (err == WEBP_MUX_OK) {
290 printf("Size of the image (with alpha): %d\n", (int)image.bitstream.size);
291 }
292 WebPDataClear(&image.bitstream);
293 RETURN_IF_ERROR("Failed to retrieve the image\n");
294 }
295
296 return WEBP_MUX_OK;
297 }
298
PrintHelp(void)299 static void PrintHelp(void) {
300 printf("Usage: webpmux -get GET_OPTIONS INPUT -o OUTPUT\n");
301 printf(" webpmux -set SET_OPTIONS INPUT -o OUTPUT\n");
302 printf(" webpmux -duration DURATION_OPTIONS [-duration ...]\n");
303 printf(" INPUT -o OUTPUT\n");
304 printf(" webpmux -strip STRIP_OPTIONS INPUT -o OUTPUT\n");
305 printf(" webpmux -frame FRAME_OPTIONS [-frame...] [-loop LOOP_COUNT]"
306 "\n");
307 printf(" [-bgcolor BACKGROUND_COLOR] -o OUTPUT\n");
308 printf(" webpmux -info INPUT\n");
309 printf(" webpmux [-h|-help]\n");
310 printf(" webpmux -version\n");
311 printf(" webpmux argument_file_name\n");
312
313 printf("\n");
314 printf("GET_OPTIONS:\n");
315 printf(" Extract relevant data:\n");
316 printf(" icc get ICC profile\n");
317 printf(" exif get EXIF metadata\n");
318 printf(" xmp get XMP metadata\n");
319 printf(" frame n get nth frame\n");
320
321 printf("\n");
322 printf("SET_OPTIONS:\n");
323 printf(" Set color profile/metadata/parameters:\n");
324 printf(" loop LOOP_COUNT set the loop count\n");
325 printf(" bgcolor BACKGROUND_COLOR set the animation background color\n");
326 printf(" icc file.icc set ICC profile\n");
327 printf(" exif file.exif set EXIF metadata\n");
328 printf(" xmp file.xmp set XMP metadata\n");
329 printf(" where: 'file.icc' contains the ICC profile to be set,\n");
330 printf(" 'file.exif' contains the EXIF metadata to be set\n");
331 printf(" 'file.xmp' contains the XMP metadata to be set\n");
332
333 printf("\n");
334 printf("DURATION_OPTIONS:\n");
335 printf(" Set duration of selected frames:\n");
336 printf(" duration set duration for all frames\n");
337 printf(" duration,frame set duration of a particular frame\n");
338 printf(" duration,start,end set duration of frames in the\n");
339 printf(" interval [start,end])\n");
340 printf(" where: 'duration' is the duration in milliseconds\n");
341 printf(" 'start' is the start frame index\n");
342 printf(" 'end' is the inclusive end frame index\n");
343 printf(" The special 'end' value '0' means: last frame.\n");
344
345 printf("\n");
346 printf("STRIP_OPTIONS:\n");
347 printf(" Strip color profile/metadata:\n");
348 printf(" icc strip ICC profile\n");
349 printf(" exif strip EXIF metadata\n");
350 printf(" xmp strip XMP metadata\n");
351
352 printf("\n");
353 printf("FRAME_OPTIONS(i):\n");
354 printf(" Create animation:\n");
355 printf(" file_i +di[+xi+yi[+mi[bi]]]\n");
356 printf(" where: 'file_i' is the i'th animation frame (WebP format),\n");
357 printf(" 'di' is the pause duration before next frame,\n");
358 printf(" 'xi','yi' specify the image offset for this frame,\n");
359 printf(" 'mi' is the dispose method for this frame (0 or 1),\n");
360 printf(" 'bi' is the blending method for this frame (+b or -b)"
361 "\n");
362
363 printf("\n");
364 printf("LOOP_COUNT:\n");
365 printf(" Number of times to repeat the animation.\n");
366 printf(" Valid range is 0 to 65535 [Default: 0 (infinite)].\n");
367
368 printf("\n");
369 printf("BACKGROUND_COLOR:\n");
370 printf(" Background color of the canvas.\n");
371 printf(" A,R,G,B\n");
372 printf(" where: 'A', 'R', 'G' and 'B' are integers in the range 0 to 255 "
373 "specifying\n");
374 printf(" the Alpha, Red, Green and Blue component values "
375 "respectively\n");
376 printf(" [Default: 255,255,255,255]\n");
377
378 printf("\nINPUT & OUTPUT are in WebP format.\n");
379
380 printf("\nNote: The nature of EXIF, XMP and ICC data is not checked");
381 printf(" and is assumed to be\nvalid.\n");
382 printf("\nNote: if a single file name is passed as the argument, the "
383 "arguments will be\n");
384 printf("tokenized from this file. The file name must not start with "
385 "the character '-'.\n");
386 }
387
WarnAboutOddOffset(const WebPMuxFrameInfo * const info)388 static void WarnAboutOddOffset(const WebPMuxFrameInfo* const info) {
389 if ((info->x_offset | info->y_offset) & 1) {
390 fprintf(stderr, "Warning: odd offsets will be snapped to even values"
391 " (%d, %d) -> (%d, %d)\n", info->x_offset, info->y_offset,
392 info->x_offset & ~1, info->y_offset & ~1);
393 }
394 }
395
CreateMux(const char * const filename,WebPMux ** mux)396 static int CreateMux(const char* const filename, WebPMux** mux) {
397 WebPData bitstream;
398 assert(mux != NULL);
399 if (!ExUtilReadFileToWebPData(filename, &bitstream)) return 0;
400 *mux = WebPMuxCreate(&bitstream, 1);
401 WebPDataClear(&bitstream);
402 if (*mux != NULL) return 1;
403 WFPRINTF(stderr, "Failed to create mux object from file %s.\n",
404 (const W_CHAR*)filename);
405 return 0;
406 }
407
WriteData(const char * filename,const WebPData * const webpdata)408 static int WriteData(const char* filename, const WebPData* const webpdata) {
409 int ok = 0;
410 FILE* fout = WSTRCMP(filename, "-") ? WFOPEN(filename, "wb")
411 : ImgIoUtilSetBinaryMode(stdout);
412 if (fout == NULL) {
413 WFPRINTF(stderr, "Error opening output WebP file %s!\n",
414 (const W_CHAR*)filename);
415 return 0;
416 }
417 if (fwrite(webpdata->bytes, webpdata->size, 1, fout) != 1) {
418 WFPRINTF(stderr, "Error writing file %s!\n", (const W_CHAR*)filename);
419 } else {
420 WFPRINTF(stderr, "Saved file %s (%d bytes)\n",
421 (const W_CHAR*)filename, (int)webpdata->size);
422 ok = 1;
423 }
424 if (fout != stdout) fclose(fout);
425 return ok;
426 }
427
WriteWebP(WebPMux * const mux,const char * filename)428 static int WriteWebP(WebPMux* const mux, const char* filename) {
429 int ok;
430 WebPData webp_data;
431 const WebPMuxError err = WebPMuxAssemble(mux, &webp_data);
432 if (err != WEBP_MUX_OK) {
433 fprintf(stderr, "Error (%s) assembling the WebP file.\n", ErrorString(err));
434 return 0;
435 }
436 ok = WriteData(filename, &webp_data);
437 WebPDataClear(&webp_data);
438 return ok;
439 }
440
DuplicateMuxHeader(const WebPMux * const mux)441 static WebPMux* DuplicateMuxHeader(const WebPMux* const mux) {
442 WebPMux* new_mux = WebPMuxNew();
443 WebPMuxAnimParams p;
444 WebPMuxError err;
445 int i;
446 int ok = 1;
447
448 if (new_mux == NULL) return NULL;
449
450 err = WebPMuxGetAnimationParams(mux, &p);
451 if (err == WEBP_MUX_OK) {
452 err = WebPMuxSetAnimationParams(new_mux, &p);
453 if (err != WEBP_MUX_OK) {
454 ERROR_GOTO2("Error (%s) handling animation params.\n",
455 ErrorString(err), End);
456 }
457 } else {
458 /* it might not be an animation. Just keep moving. */
459 }
460
461 for (i = 1; i <= 3; ++i) {
462 WebPData metadata;
463 err = WebPMuxGetChunk(mux, kFourccList[i], &metadata);
464 if (err == WEBP_MUX_OK && metadata.size > 0) {
465 err = WebPMuxSetChunk(new_mux, kFourccList[i], &metadata, 1);
466 if (err != WEBP_MUX_OK) {
467 ERROR_GOTO1("Error transferring metadata in DuplicateMuxHeader().",
468 End);
469 }
470 }
471 }
472
473 End:
474 if (!ok) {
475 WebPMuxDelete(new_mux);
476 new_mux = NULL;
477 }
478 return new_mux;
479 }
480
ParseFrameArgs(const char * args,WebPMuxFrameInfo * const info)481 static int ParseFrameArgs(const char* args, WebPMuxFrameInfo* const info) {
482 int dispose_method, unused;
483 char plus_minus, blend_method;
484 const int num_args = sscanf(args, "+%d+%d+%d+%d%c%c+%d", &info->duration,
485 &info->x_offset, &info->y_offset, &dispose_method,
486 &plus_minus, &blend_method, &unused);
487 switch (num_args) {
488 case 1:
489 info->x_offset = info->y_offset = 0; // fall through
490 case 3:
491 dispose_method = 0; // fall through
492 case 4:
493 plus_minus = '+';
494 blend_method = 'b'; // fall through
495 case 6:
496 break;
497 case 2:
498 case 5:
499 default:
500 return 0;
501 }
502
503 WarnAboutOddOffset(info);
504
505 // Note: The validity of the following conversion is checked by
506 // WebPMuxPushFrame().
507 info->dispose_method = (WebPMuxAnimDispose)dispose_method;
508
509 if (blend_method != 'b') return 0;
510 if (plus_minus != '-' && plus_minus != '+') return 0;
511 info->blend_method =
512 (plus_minus == '+') ? WEBP_MUX_BLEND : WEBP_MUX_NO_BLEND;
513 return 1;
514 }
515
ParseBgcolorArgs(const char * args,uint32_t * const bgcolor)516 static int ParseBgcolorArgs(const char* args, uint32_t* const bgcolor) {
517 uint32_t a, r, g, b;
518 if (sscanf(args, "%u,%u,%u,%u", &a, &r, &g, &b) != 4) return 0;
519 if (a >= 256 || r >= 256 || g >= 256 || b >= 256) return 0;
520 *bgcolor = (a << 24) | (r << 16) | (g << 8) | (b << 0);
521 return 1;
522 }
523
524 //------------------------------------------------------------------------------
525 // Clean-up.
526
DeleteConfig(Config * const config)527 static void DeleteConfig(Config* const config) {
528 if (config != NULL) {
529 free(config->args_);
530 ExUtilDeleteCommandLineArguments(&config->cmd_args_);
531 memset(config, 0, sizeof(*config));
532 }
533 }
534
535 //------------------------------------------------------------------------------
536 // Parsing.
537
538 // Basic syntactic checks on the command-line arguments.
539 // Returns 1 on valid, 0 otherwise.
540 // Also fills up num_feature_args to be number of feature arguments given.
541 // (e.g. if there are 4 '-frame's and 1 '-loop', then num_feature_args = 5).
ValidateCommandLine(const CommandLineArguments * const cmd_args,int * num_feature_args)542 static int ValidateCommandLine(const CommandLineArguments* const cmd_args,
543 int* num_feature_args) {
544 int num_frame_args;
545 int num_loop_args;
546 int num_bgcolor_args;
547 int num_durations_args;
548 int ok = 1;
549
550 assert(num_feature_args != NULL);
551 *num_feature_args = 0;
552
553 // Simple checks.
554 if (CountOccurrences(cmd_args, "-get") > 1) {
555 ERROR_GOTO1("ERROR: Multiple '-get' arguments specified.\n", ErrValidate);
556 }
557 if (CountOccurrences(cmd_args, "-set") > 1) {
558 ERROR_GOTO1("ERROR: Multiple '-set' arguments specified.\n", ErrValidate);
559 }
560 if (CountOccurrences(cmd_args, "-strip") > 1) {
561 ERROR_GOTO1("ERROR: Multiple '-strip' arguments specified.\n", ErrValidate);
562 }
563 if (CountOccurrences(cmd_args, "-info") > 1) {
564 ERROR_GOTO1("ERROR: Multiple '-info' arguments specified.\n", ErrValidate);
565 }
566 if (CountOccurrences(cmd_args, "-o") > 1) {
567 ERROR_GOTO1("ERROR: Multiple output files specified.\n", ErrValidate);
568 }
569
570 // Compound checks.
571 num_frame_args = CountOccurrences(cmd_args, "-frame");
572 num_loop_args = CountOccurrences(cmd_args, "-loop");
573 num_bgcolor_args = CountOccurrences(cmd_args, "-bgcolor");
574 num_durations_args = CountOccurrences(cmd_args, "-duration");
575
576 if (num_loop_args > 1) {
577 ERROR_GOTO1("ERROR: Multiple loop counts specified.\n", ErrValidate);
578 }
579 if (num_bgcolor_args > 1) {
580 ERROR_GOTO1("ERROR: Multiple background colors specified.\n", ErrValidate);
581 }
582
583 if ((num_frame_args == 0) && (num_loop_args + num_bgcolor_args > 0)) {
584 ERROR_GOTO1("ERROR: Loop count and background color are relevant only in "
585 "case of animation.\n", ErrValidate);
586 }
587 if (num_durations_args > 0 && num_frame_args != 0) {
588 ERROR_GOTO1("ERROR: Can not combine -duration and -frame commands.\n",
589 ErrValidate);
590 }
591
592 assert(ok == 1);
593 if (num_durations_args > 0) {
594 *num_feature_args = num_durations_args;
595 } else if (num_frame_args == 0) {
596 // Single argument ('set' action for ICCP/EXIF/XMP, OR a 'get' action).
597 *num_feature_args = 1;
598 } else {
599 // Multiple arguments ('set' action for animation)
600 *num_feature_args = num_frame_args + num_loop_args + num_bgcolor_args;
601 }
602
603 ErrValidate:
604 return ok;
605 }
606
607 #define ACTION_IS_NIL (config->action_type_ == NIL_ACTION)
608
609 #define FEATURETYPE_IS_NIL (config->type_ == NIL_FEATURE)
610
611 #define CHECK_NUM_ARGS_AT_LEAST(NUM, LABEL) \
612 do { \
613 if (argc < i + (NUM)) { \
614 fprintf(stderr, "ERROR: Too few arguments for '%s'.\n", argv[i]); \
615 goto LABEL; \
616 } \
617 } while (0)
618
619 #define CHECK_NUM_ARGS_AT_MOST(NUM, LABEL) \
620 do { \
621 if (argc > i + (NUM)) { \
622 fprintf(stderr, "ERROR: Too many arguments for '%s'.\n", argv[i]); \
623 goto LABEL; \
624 } \
625 } while (0)
626
627 #define CHECK_NUM_ARGS_EXACTLY(NUM, LABEL) \
628 do { \
629 CHECK_NUM_ARGS_AT_LEAST(NUM, LABEL); \
630 CHECK_NUM_ARGS_AT_MOST(NUM, LABEL); \
631 } while (0)
632
633 // Parses command-line arguments to fill up config object. Also performs some
634 // semantic checks. unicode_argv contains wchar_t arguments or is null.
ParseCommandLine(Config * config,const W_CHAR ** const unicode_argv)635 static int ParseCommandLine(Config* config, const W_CHAR** const unicode_argv) {
636 int i = 0;
637 int feature_arg_index = 0;
638 int ok = 1;
639 int argc = config->cmd_args_.argc_;
640 const char* const* argv = config->cmd_args_.argv_;
641 // Unicode file paths will be used if available.
642 const char* const* wargv =
643 (unicode_argv != NULL) ? (const char**)(unicode_argv + 1) : argv;
644
645 while (i < argc) {
646 FeatureArg* const arg = &config->args_[feature_arg_index];
647 if (argv[i][0] == '-') { // One of the action types or output.
648 if (!strcmp(argv[i], "-set")) {
649 if (ACTION_IS_NIL) {
650 config->action_type_ = ACTION_SET;
651 } else {
652 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
653 }
654 ++i;
655 } else if (!strcmp(argv[i], "-duration")) {
656 CHECK_NUM_ARGS_AT_LEAST(2, ErrParse);
657 if (ACTION_IS_NIL || config->action_type_ == ACTION_DURATION) {
658 config->action_type_ = ACTION_DURATION;
659 } else {
660 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
661 }
662 if (FEATURETYPE_IS_NIL || config->type_ == FEATURE_DURATION) {
663 config->type_ = FEATURE_DURATION;
664 } else {
665 ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
666 }
667 arg->params_ = argv[i + 1];
668 ++feature_arg_index;
669 i += 2;
670 } else if (!strcmp(argv[i], "-get")) {
671 if (ACTION_IS_NIL) {
672 config->action_type_ = ACTION_GET;
673 } else {
674 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
675 }
676 ++i;
677 } else if (!strcmp(argv[i], "-strip")) {
678 if (ACTION_IS_NIL) {
679 config->action_type_ = ACTION_STRIP;
680 config->arg_count_ = 0;
681 } else {
682 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
683 }
684 ++i;
685 } else if (!strcmp(argv[i], "-frame")) {
686 CHECK_NUM_ARGS_AT_LEAST(3, ErrParse);
687 if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) {
688 config->action_type_ = ACTION_SET;
689 } else {
690 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
691 }
692 if (FEATURETYPE_IS_NIL || config->type_ == FEATURE_ANMF) {
693 config->type_ = FEATURE_ANMF;
694 } else {
695 ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
696 }
697 arg->subtype_ = SUBTYPE_ANMF;
698 arg->filename_ = wargv[i + 1];
699 arg->params_ = argv[i + 2];
700 ++feature_arg_index;
701 i += 3;
702 } else if (!strcmp(argv[i], "-loop") || !strcmp(argv[i], "-bgcolor")) {
703 CHECK_NUM_ARGS_AT_LEAST(2, ErrParse);
704 if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) {
705 config->action_type_ = ACTION_SET;
706 } else {
707 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
708 }
709 if (FEATURETYPE_IS_NIL || config->type_ == FEATURE_ANMF) {
710 config->type_ = FEATURE_ANMF;
711 } else {
712 ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
713 }
714 arg->subtype_ =
715 !strcmp(argv[i], "-loop") ? SUBTYPE_LOOP : SUBTYPE_BGCOLOR;
716 arg->params_ = argv[i + 1];
717 ++feature_arg_index;
718 i += 2;
719 } else if (!strcmp(argv[i], "-o")) {
720 CHECK_NUM_ARGS_AT_LEAST(2, ErrParse);
721 config->output_ = wargv[i + 1];
722 i += 2;
723 } else if (!strcmp(argv[i], "-info")) {
724 CHECK_NUM_ARGS_EXACTLY(2, ErrParse);
725 if (config->action_type_ != NIL_ACTION) {
726 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
727 } else {
728 config->action_type_ = ACTION_INFO;
729 config->arg_count_ = 0;
730 config->input_ = wargv[i + 1];
731 }
732 i += 2;
733 } else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "-help")) {
734 PrintHelp();
735 DeleteConfig(config);
736 LOCAL_FREE((W_CHAR** const)unicode_argv);
737 exit(0);
738 } else if (!strcmp(argv[i], "-version")) {
739 const int version = WebPGetMuxVersion();
740 printf("%d.%d.%d\n",
741 (version >> 16) & 0xff, (version >> 8) & 0xff, version & 0xff);
742 DeleteConfig(config);
743 LOCAL_FREE((W_CHAR** const)unicode_argv);
744 exit(0);
745 } else if (!strcmp(argv[i], "--")) {
746 if (i < argc - 1) {
747 ++i;
748 if (config->input_ == NULL) {
749 config->input_ = wargv[i];
750 } else {
751 ERROR_GOTO2("ERROR at '%s': Multiple input files specified.\n",
752 argv[i], ErrParse);
753 }
754 }
755 break;
756 } else {
757 ERROR_GOTO2("ERROR: Unknown option: '%s'.\n", argv[i], ErrParse);
758 }
759 } else { // One of the feature types or input.
760 if (ACTION_IS_NIL) {
761 ERROR_GOTO1("ERROR: Action must be specified before other arguments.\n",
762 ErrParse);
763 }
764 if (!strcmp(argv[i], "icc") || !strcmp(argv[i], "exif") ||
765 !strcmp(argv[i], "xmp")) {
766 if (FEATURETYPE_IS_NIL) {
767 config->type_ = (!strcmp(argv[i], "icc")) ? FEATURE_ICCP :
768 (!strcmp(argv[i], "exif")) ? FEATURE_EXIF : FEATURE_XMP;
769 } else {
770 ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
771 }
772 if (config->action_type_ == ACTION_SET) {
773 CHECK_NUM_ARGS_AT_LEAST(2, ErrParse);
774 arg->filename_ = wargv[i + 1];
775 ++feature_arg_index;
776 i += 2;
777 } else {
778 ++i;
779 }
780 } else if (!strcmp(argv[i], "frame") &&
781 (config->action_type_ == ACTION_GET)) {
782 CHECK_NUM_ARGS_AT_LEAST(2, ErrParse);
783 config->type_ = FEATURE_ANMF;
784 arg->params_ = argv[i + 1];
785 ++feature_arg_index;
786 i += 2;
787 } else if (!strcmp(argv[i], "loop") &&
788 (config->action_type_ == ACTION_SET)) {
789 CHECK_NUM_ARGS_AT_LEAST(2, ErrParse);
790 config->type_ = FEATURE_LOOP;
791 arg->params_ = argv[i + 1];
792 ++feature_arg_index;
793 i += 2;
794 } else if (!strcmp(argv[i], "bgcolor") &&
795 (config->action_type_ == ACTION_SET)) {
796 CHECK_NUM_ARGS_AT_LEAST(2, ErrParse);
797 config->type_ = FEATURE_BGCOLOR;
798 arg->params_ = argv[i + 1];
799 ++feature_arg_index;
800 i += 2;
801 } else { // Assume input file.
802 if (config->input_ == NULL) {
803 config->input_ = wargv[i];
804 } else {
805 ERROR_GOTO2("ERROR at '%s': Multiple input files specified.\n",
806 argv[i], ErrParse);
807 }
808 ++i;
809 }
810 }
811 }
812 ErrParse:
813 return ok;
814 }
815
816 // Additional checks after config is filled.
ValidateConfig(Config * const config)817 static int ValidateConfig(Config* const config) {
818 int ok = 1;
819
820 // Action.
821 if (ACTION_IS_NIL) {
822 ERROR_GOTO1("ERROR: No action specified.\n", ErrValidate2);
823 }
824
825 // Feature type.
826 if (FEATURETYPE_IS_NIL && config->action_type_ != ACTION_INFO) {
827 ERROR_GOTO1("ERROR: No feature specified.\n", ErrValidate2);
828 }
829
830 // Input file.
831 if (config->input_ == NULL) {
832 if (config->action_type_ != ACTION_SET) {
833 ERROR_GOTO1("ERROR: No input file specified.\n", ErrValidate2);
834 } else if (config->type_ != FEATURE_ANMF) {
835 ERROR_GOTO1("ERROR: No input file specified.\n", ErrValidate2);
836 }
837 }
838
839 // Output file.
840 if (config->output_ == NULL && config->action_type_ != ACTION_INFO) {
841 ERROR_GOTO1("ERROR: No output file specified.\n", ErrValidate2);
842 }
843
844 ErrValidate2:
845 return ok;
846 }
847
848 // Create config object from command-line arguments.
InitializeConfig(int argc,const char * argv[],Config * const config,const W_CHAR ** const unicode_argv)849 static int InitializeConfig(int argc, const char* argv[], Config* const config,
850 const W_CHAR** const unicode_argv) {
851 int num_feature_args = 0;
852 int ok;
853
854 memset(config, 0, sizeof(*config));
855
856 ok = ExUtilInitCommandLineArguments(argc, argv, &config->cmd_args_);
857 if (!ok) return 0;
858
859 // Validate command-line arguments.
860 if (!ValidateCommandLine(&config->cmd_args_, &num_feature_args)) {
861 ERROR_GOTO1("Exiting due to command-line parsing error.\n", Err1);
862 }
863
864 config->arg_count_ = num_feature_args;
865 config->args_ = (FeatureArg*)calloc(num_feature_args, sizeof(*config->args_));
866 if (config->args_ == NULL) {
867 ERROR_GOTO1("ERROR: Memory allocation error.\n", Err1);
868 }
869
870 // Parse command-line.
871 if (!ParseCommandLine(config, unicode_argv) || !ValidateConfig(config)) {
872 ERROR_GOTO1("Exiting due to command-line parsing error.\n", Err1);
873 }
874
875 Err1:
876 return ok;
877 }
878
879 #undef ACTION_IS_NIL
880 #undef FEATURETYPE_IS_NIL
881 #undef CHECK_NUM_ARGS_AT_LEAST
882 #undef CHECK_NUM_ARGS_AT_MOST
883 #undef CHECK_NUM_ARGS_EXACTLY
884
885 //------------------------------------------------------------------------------
886 // Processing.
887
GetFrame(const WebPMux * mux,const Config * config)888 static int GetFrame(const WebPMux* mux, const Config* config) {
889 WebPMuxError err = WEBP_MUX_OK;
890 WebPMux* mux_single = NULL;
891 int num = 0;
892 int ok = 1;
893 int parse_error = 0;
894 const WebPChunkId id = WEBP_CHUNK_ANMF;
895 WebPMuxFrameInfo info;
896 WebPDataInit(&info.bitstream);
897
898 num = ExUtilGetInt(config->args_[0].params_, 10, &parse_error);
899 if (num < 0) {
900 ERROR_GOTO1("ERROR: Frame/Fragment index must be non-negative.\n", ErrGet);
901 }
902 if (parse_error) goto ErrGet;
903
904 err = WebPMuxGetFrame(mux, num, &info);
905 if (err == WEBP_MUX_OK && info.id != id) err = WEBP_MUX_NOT_FOUND;
906 if (err != WEBP_MUX_OK) {
907 ERROR_GOTO3("ERROR (%s): Could not get frame %d.\n",
908 ErrorString(err), num, ErrGet);
909 }
910
911 mux_single = WebPMuxNew();
912 if (mux_single == NULL) {
913 err = WEBP_MUX_MEMORY_ERROR;
914 ERROR_GOTO2("ERROR (%s): Could not allocate a mux object.\n",
915 ErrorString(err), ErrGet);
916 }
917 err = WebPMuxSetImage(mux_single, &info.bitstream, 1);
918 if (err != WEBP_MUX_OK) {
919 ERROR_GOTO2("ERROR (%s): Could not create single image mux object.\n",
920 ErrorString(err), ErrGet);
921 }
922
923 ok = WriteWebP(mux_single, config->output_);
924
925 ErrGet:
926 WebPDataClear(&info.bitstream);
927 WebPMuxDelete(mux_single);
928 return ok && !parse_error;
929 }
930
931 // Read and process config.
Process(const Config * config)932 static int Process(const Config* config) {
933 WebPMux* mux = NULL;
934 WebPData chunk;
935 WebPMuxError err = WEBP_MUX_OK;
936 int ok = 1;
937
938 switch (config->action_type_) {
939 case ACTION_GET: {
940 ok = CreateMux(config->input_, &mux);
941 if (!ok) goto Err2;
942 switch (config->type_) {
943 case FEATURE_ANMF:
944 ok = GetFrame(mux, config);
945 break;
946
947 case FEATURE_ICCP:
948 case FEATURE_EXIF:
949 case FEATURE_XMP:
950 err = WebPMuxGetChunk(mux, kFourccList[config->type_], &chunk);
951 if (err != WEBP_MUX_OK) {
952 ERROR_GOTO3("ERROR (%s): Could not get the %s.\n",
953 ErrorString(err), kDescriptions[config->type_], Err2);
954 }
955 ok = WriteData(config->output_, &chunk);
956 break;
957
958 default:
959 ERROR_GOTO1("ERROR: Invalid feature for action 'get'.\n", Err2);
960 break;
961 }
962 break;
963 }
964 case ACTION_SET: {
965 switch (config->type_) {
966 case FEATURE_ANMF: {
967 int i;
968 WebPMuxAnimParams params = { 0xFFFFFFFF, 0 };
969 mux = WebPMuxNew();
970 if (mux == NULL) {
971 ERROR_GOTO2("ERROR (%s): Could not allocate a mux object.\n",
972 ErrorString(WEBP_MUX_MEMORY_ERROR), Err2);
973 }
974 for (i = 0; i < config->arg_count_; ++i) {
975 switch (config->args_[i].subtype_) {
976 case SUBTYPE_BGCOLOR: {
977 uint32_t bgcolor;
978 ok = ParseBgcolorArgs(config->args_[i].params_, &bgcolor);
979 if (!ok) {
980 ERROR_GOTO1("ERROR: Could not parse the background color \n",
981 Err2);
982 }
983 params.bgcolor = bgcolor;
984 break;
985 }
986 case SUBTYPE_LOOP: {
987 int parse_error = 0;
988 const int loop_count =
989 ExUtilGetInt(config->args_[i].params_, 10, &parse_error);
990 if (loop_count < 0 || loop_count > 65535) {
991 // Note: This is only a 'necessary' condition for loop_count
992 // to be valid. The 'sufficient' conditioned in checked in
993 // WebPMuxSetAnimationParams() method called later.
994 ERROR_GOTO1("ERROR: Loop count must be in the range 0 to "
995 "65535.\n", Err2);
996 }
997 ok = !parse_error;
998 if (!ok) goto Err2;
999 params.loop_count = loop_count;
1000 break;
1001 }
1002 case SUBTYPE_ANMF: {
1003 WebPMuxFrameInfo frame;
1004 frame.id = WEBP_CHUNK_ANMF;
1005 ok = ExUtilReadFileToWebPData(config->args_[i].filename_,
1006 &frame.bitstream);
1007 if (!ok) goto Err2;
1008 ok = ParseFrameArgs(config->args_[i].params_, &frame);
1009 if (!ok) {
1010 WebPDataClear(&frame.bitstream);
1011 ERROR_GOTO1("ERROR: Could not parse frame properties.\n",
1012 Err2);
1013 }
1014 err = WebPMuxPushFrame(mux, &frame, 1);
1015 WebPDataClear(&frame.bitstream);
1016 if (err != WEBP_MUX_OK) {
1017 ERROR_GOTO3("ERROR (%s): Could not add a frame at index %d."
1018 "\n", ErrorString(err), i, Err2);
1019 }
1020 break;
1021 }
1022 default: {
1023 ERROR_GOTO1("ERROR: Invalid subtype for 'frame'", Err2);
1024 break;
1025 }
1026 }
1027 }
1028 err = WebPMuxSetAnimationParams(mux, ¶ms);
1029 if (err != WEBP_MUX_OK) {
1030 ERROR_GOTO2("ERROR (%s): Could not set animation parameters.\n",
1031 ErrorString(err), Err2);
1032 }
1033 break;
1034 }
1035
1036 case FEATURE_ICCP:
1037 case FEATURE_EXIF:
1038 case FEATURE_XMP: {
1039 ok = CreateMux(config->input_, &mux);
1040 if (!ok) goto Err2;
1041 ok = ExUtilReadFileToWebPData(config->args_[0].filename_, &chunk);
1042 if (!ok) goto Err2;
1043 err = WebPMuxSetChunk(mux, kFourccList[config->type_], &chunk, 1);
1044 WebPDataClear(&chunk);
1045 if (err != WEBP_MUX_OK) {
1046 ERROR_GOTO3("ERROR (%s): Could not set the %s.\n",
1047 ErrorString(err), kDescriptions[config->type_], Err2);
1048 }
1049 break;
1050 }
1051 case FEATURE_LOOP: {
1052 WebPMuxAnimParams params = { 0xFFFFFFFF, 0 };
1053 int parse_error = 0;
1054 const int loop_count =
1055 ExUtilGetInt(config->args_[0].params_, 10, &parse_error);
1056 if (loop_count < 0 || loop_count > 65535 || parse_error) {
1057 ERROR_GOTO1("ERROR: Loop count must be in the range 0 to 65535.\n",
1058 Err2);
1059 }
1060 ok = CreateMux(config->input_, &mux);
1061 if (!ok) goto Err2;
1062 ok = (WebPMuxGetAnimationParams(mux, ¶ms) == WEBP_MUX_OK);
1063 if (!ok) {
1064 ERROR_GOTO1("ERROR: input file does not seem to be an animation.\n",
1065 Err2);
1066 }
1067 params.loop_count = loop_count;
1068 err = WebPMuxSetAnimationParams(mux, ¶ms);
1069 ok = (err == WEBP_MUX_OK);
1070 if (!ok) {
1071 ERROR_GOTO2("ERROR (%s): Could not set animation parameters.\n",
1072 ErrorString(err), Err2);
1073 }
1074 break;
1075 }
1076 case FEATURE_BGCOLOR: {
1077 WebPMuxAnimParams params = { 0xFFFFFFFF, 0 };
1078 uint32_t bgcolor;
1079 ok = ParseBgcolorArgs(config->args_[0].params_, &bgcolor);
1080 if (!ok) {
1081 ERROR_GOTO1("ERROR: Could not parse the background color.\n",
1082 Err2);
1083 }
1084 ok = CreateMux(config->input_, &mux);
1085 if (!ok) goto Err2;
1086 ok = (WebPMuxGetAnimationParams(mux, ¶ms) == WEBP_MUX_OK);
1087 if (!ok) {
1088 ERROR_GOTO1("ERROR: input file does not seem to be an animation.\n",
1089 Err2);
1090 }
1091 params.bgcolor = bgcolor;
1092 err = WebPMuxSetAnimationParams(mux, ¶ms);
1093 ok = (err == WEBP_MUX_OK);
1094 if (!ok) {
1095 ERROR_GOTO2("ERROR (%s): Could not set animation parameters.\n",
1096 ErrorString(err), Err2);
1097 }
1098 break;
1099 }
1100 default: {
1101 ERROR_GOTO1("ERROR: Invalid feature for action 'set'.\n", Err2);
1102 break;
1103 }
1104 }
1105 ok = WriteWebP(mux, config->output_);
1106 break;
1107 }
1108 case ACTION_DURATION: {
1109 int num_frames;
1110 ok = CreateMux(config->input_, &mux);
1111 if (!ok) goto Err2;
1112 err = WebPMuxNumChunks(mux, WEBP_CHUNK_ANMF, &num_frames);
1113 ok = (err == WEBP_MUX_OK);
1114 if (!ok) {
1115 ERROR_GOTO1("ERROR: can not parse the number of frames.\n", Err2);
1116 }
1117 if (num_frames == 0) {
1118 fprintf(stderr, "Doesn't look like the source is animated. "
1119 "Skipping duration setting.\n");
1120 ok = WriteWebP(mux, config->output_);
1121 if (!ok) goto Err2;
1122 } else {
1123 int i;
1124 int* durations = NULL;
1125 WebPMux* new_mux = DuplicateMuxHeader(mux);
1126 if (new_mux == NULL) goto Err2;
1127 durations = (int*)WebPMalloc((size_t)num_frames * sizeof(*durations));
1128 if (durations == NULL) goto Err2;
1129 for (i = 0; i < num_frames; ++i) durations[i] = -1;
1130
1131 // Parse intervals to process.
1132 for (i = 0; i < config->arg_count_; ++i) {
1133 int k;
1134 int args[3];
1135 int duration, start, end;
1136 const int nb_args = ExUtilGetInts(config->args_[i].params_,
1137 10, 3, args);
1138 ok = (nb_args >= 1);
1139 if (!ok) goto Err3;
1140 duration = args[0];
1141 if (duration < 0) {
1142 ERROR_GOTO1("ERROR: duration must be strictly positive.\n", Err3);
1143 }
1144
1145 if (nb_args == 1) { // only duration is present -> use full interval
1146 start = 1;
1147 end = num_frames;
1148 } else {
1149 start = args[1];
1150 if (start <= 0) {
1151 start = 1;
1152 } else if (start > num_frames) {
1153 start = num_frames;
1154 }
1155 end = (nb_args >= 3) ? args[2] : start;
1156 if (end == 0 || end > num_frames) end = num_frames;
1157 }
1158
1159 for (k = start; k <= end; ++k) {
1160 assert(k >= 1 && k <= num_frames);
1161 durations[k - 1] = duration;
1162 }
1163 }
1164
1165 // Apply non-negative durations to their destination frames.
1166 for (i = 1; i <= num_frames; ++i) {
1167 WebPMuxFrameInfo frame;
1168 err = WebPMuxGetFrame(mux, i, &frame);
1169 if (err != WEBP_MUX_OK || frame.id != WEBP_CHUNK_ANMF) {
1170 ERROR_GOTO2("ERROR: can not retrieve frame #%d.\n", i, Err3);
1171 }
1172 if (durations[i - 1] >= 0) frame.duration = durations[i - 1];
1173 err = WebPMuxPushFrame(new_mux, &frame, 1);
1174 if (err != WEBP_MUX_OK) {
1175 ERROR_GOTO2("ERROR: error push frame data #%d\n", i, Err3);
1176 }
1177 WebPDataClear(&frame.bitstream);
1178 }
1179 WebPMuxDelete(mux);
1180 ok = WriteWebP(new_mux, config->output_);
1181 mux = new_mux; // transfer for the WebPMuxDelete() call
1182 new_mux = NULL;
1183
1184 Err3:
1185 WebPFree(durations);
1186 WebPMuxDelete(new_mux);
1187 if (!ok) goto Err2;
1188 }
1189 break;
1190 }
1191 case ACTION_STRIP: {
1192 ok = CreateMux(config->input_, &mux);
1193 if (!ok) goto Err2;
1194 if (config->type_ == FEATURE_ICCP || config->type_ == FEATURE_EXIF ||
1195 config->type_ == FEATURE_XMP) {
1196 err = WebPMuxDeleteChunk(mux, kFourccList[config->type_]);
1197 if (err != WEBP_MUX_OK) {
1198 ERROR_GOTO3("ERROR (%s): Could not strip the %s.\n",
1199 ErrorString(err), kDescriptions[config->type_], Err2);
1200 }
1201 } else {
1202 ERROR_GOTO1("ERROR: Invalid feature for action 'strip'.\n", Err2);
1203 break;
1204 }
1205 ok = WriteWebP(mux, config->output_);
1206 break;
1207 }
1208 case ACTION_INFO: {
1209 ok = CreateMux(config->input_, &mux);
1210 if (!ok) goto Err2;
1211 ok = (DisplayInfo(mux) == WEBP_MUX_OK);
1212 break;
1213 }
1214 default: {
1215 assert(0); // Invalid action.
1216 break;
1217 }
1218 }
1219
1220 Err2:
1221 WebPMuxDelete(mux);
1222 return ok;
1223 }
1224
1225 //------------------------------------------------------------------------------
1226 // Main.
1227
main(int argc,const char * argv[])1228 int main(int argc, const char* argv[]) {
1229 Config config;
1230 int ok;
1231
1232 INIT_WARGV(argc, argv);
1233
1234 ok = InitializeConfig(argc - 1, argv + 1, &config, GET_WARGV_OR_NULL());
1235 if (ok) {
1236 ok = Process(&config);
1237 } else {
1238 PrintHelp();
1239 }
1240 DeleteConfig(&config);
1241 FREE_WARGV_AND_RETURN(!ok);
1242 }
1243
1244 //------------------------------------------------------------------------------
1245