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