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