xref: /aosp_15_r20/external/webp/examples/img2webp.c (revision b2055c353e87c8814eb2b6b1b11112a1562253bd)
1 // Copyright 2016 Google Inc. All Rights Reserved.
2 //
3 // Use of this source code is governed by a BSD-style license
4 // that can be found in the COPYING file in the root of the source
5 // tree. An additional intellectual property rights grant can be found
6 // in the file PATENTS. All contributing project authors may
7 // be found in the AUTHORS file in the root of the source tree.
8 // -----------------------------------------------------------------------------
9 //
10 //  generate an animated WebP out of a sequence of images
11 //  (PNG, JPEG, ...)
12 //
13 //  Example usage:
14 //     img2webp -o out.webp -q 40 -mixed -duration 40 input??.png
15 //
16 // Author: [email protected] (Pascal Massimino)
17 
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 
22 #ifdef HAVE_CONFIG_H
23 #include "webp/config.h"
24 #endif
25 
26 #include "../examples/example_util.h"
27 #include "../imageio/image_dec.h"
28 #include "../imageio/imageio_util.h"
29 #include "./stopwatch.h"
30 #include "./unicode.h"
31 #include "sharpyuv/sharpyuv.h"
32 #include "webp/encode.h"
33 #include "webp/mux.h"
34 
35 //------------------------------------------------------------------------------
36 
Help(void)37 static void Help(void) {
38   printf("Usage:\n\n");
39   printf("  img2webp [file_options] [[frame_options] frame_file]...");
40   printf(" [-o webp_file]\n\n");
41 
42   printf("File-level options (only used at the start of compression):\n");
43   printf(" -min_size ............ minimize size\n");
44   printf(" -kmax <int> .......... maximum number of frame between key-frames\n"
45          "                        (0=only keyframes)\n");
46   printf(" -kmin <int> .......... minimum number of frame between key-frames\n"
47          "                        (0=disable key-frames altogether)\n");
48   printf(" -mixed ............... use mixed lossy/lossless automatic mode\n");
49   printf(" -near_lossless <int> . use near-lossless image preprocessing\n"
50          "                        (0..100=off), default=100\n");
51   printf(" -sharp_yuv ........... use sharper (and slower) RGB->YUV "
52                                   "conversion\n                        "
53                                   "(lossy only)\n");
54   printf(" -loop <int> .......... loop count (default: 0, = infinite loop)\n");
55   printf(" -v ................... verbose mode\n");
56   printf(" -h ................... this help\n");
57   printf(" -version ............. print version number and exit\n");
58   printf("\n");
59 
60   printf("Per-frame options (only used for subsequent images input):\n");
61   printf(" -d <int> ............. frame duration in ms (default: 100)\n");
62   printf(" -lossless  ........... use lossless mode (default)\n");
63   printf(" -lossy ... ........... use lossy mode\n");
64   printf(" -q <float> ........... quality\n");
65   printf(" -m <int> ............. method to use\n");
66 
67   printf("\n");
68   printf("example: img2webp -loop 2 in0.png -lossy in1.jpg\n"
69          "                  -d 80 in2.tiff -o out.webp\n");
70   printf("\nNote: if a single file name is passed as the argument, the "
71          "arguments will be\n");
72   printf("tokenized from this file. The file name must not start with "
73          "the character '-'.\n");
74   printf("\nSupported input formats:\n  %s\n",
75          WebPGetEnabledInputFileFormats());
76 }
77 
78 //------------------------------------------------------------------------------
79 
ReadImage(const char filename[],WebPPicture * const pic)80 static int ReadImage(const char filename[], WebPPicture* const pic) {
81   const uint8_t* data = NULL;
82   size_t data_size = 0;
83   WebPImageReader reader;
84   int ok;
85 #ifdef HAVE_WINCODEC_H
86   // Try to decode the file using WIC falling back to the other readers for
87   // e.g., WebP.
88   ok = ReadPictureWithWIC(filename, pic, 1, NULL);
89   if (ok) return 1;
90 #endif
91   if (!ImgIoUtilReadFile(filename, &data, &data_size)) return 0;
92   reader = WebPGuessImageReader(data, data_size);
93   ok = reader(data, data_size, pic, 1, NULL);
94   WebPFree((void*)data);
95   return ok;
96 }
97 
SetLoopCount(int loop_count,WebPData * const webp_data)98 static int SetLoopCount(int loop_count, WebPData* const webp_data) {
99   int ok = 1;
100   WebPMuxError err;
101   uint32_t features;
102   WebPMuxAnimParams new_params;
103   WebPMux* const mux = WebPMuxCreate(webp_data, 1);
104   if (mux == NULL) return 0;
105 
106   err = WebPMuxGetFeatures(mux, &features);
107   ok = (err == WEBP_MUX_OK);
108   if (!ok || !(features & ANIMATION_FLAG)) goto End;
109 
110   err = WebPMuxGetAnimationParams(mux, &new_params);
111   ok = (err == WEBP_MUX_OK);
112   if (ok) {
113     new_params.loop_count = loop_count;
114     err = WebPMuxSetAnimationParams(mux, &new_params);
115     ok = (err == WEBP_MUX_OK);
116   }
117   if (ok) {
118     WebPDataClear(webp_data);
119     err = WebPMuxAssemble(mux, webp_data);
120     ok = (err == WEBP_MUX_OK);
121   }
122 
123  End:
124   WebPMuxDelete(mux);
125   if (!ok) {
126     fprintf(stderr, "Error during loop-count setting\n");
127   }
128   return ok;
129 }
130 
131 //------------------------------------------------------------------------------
132 
main(int argc,const char * argv[])133 int main(int argc, const char* argv[]) {
134   const char* output = NULL;
135   WebPAnimEncoder* enc = NULL;
136   int verbose = 0;
137   int pic_num = 0;
138   int duration = 100;
139   int timestamp_ms = 0;
140   int loop_count = 0;
141   int width = 0, height = 0;
142   WebPAnimEncoderOptions anim_config;
143   WebPConfig config;
144   WebPPicture pic;
145   WebPData webp_data;
146   int c;
147   int have_input = 0;
148   CommandLineArguments cmd_args;
149   int ok;
150 
151   INIT_WARGV(argc, argv);
152 
153   ok = ExUtilInitCommandLineArguments(argc - 1, argv + 1, &cmd_args);
154   if (!ok) FREE_WARGV_AND_RETURN(1);
155 
156   argc = cmd_args.argc_;
157   argv = cmd_args.argv_;
158 
159   WebPDataInit(&webp_data);
160   if (!WebPAnimEncoderOptionsInit(&anim_config) ||
161       !WebPConfigInit(&config) ||
162       !WebPPictureInit(&pic)) {
163     fprintf(stderr, "Library version mismatch!\n");
164     ok = 0;
165     goto End;
166   }
167 
168   // 1st pass of option parsing
169   for (c = 0; ok && c < argc; ++c) {
170     if (argv[c][0] == '-') {
171       int parse_error = 0;
172       if (!strcmp(argv[c], "-o") && c + 1 < argc) {
173         argv[c] = NULL;
174         output = (const char*)GET_WARGV_SHIFTED(argv, ++c);
175       } else if (!strcmp(argv[c], "-kmin") && c + 1 < argc) {
176         argv[c] = NULL;
177         anim_config.kmin = ExUtilGetInt(argv[++c], 0, &parse_error);
178       } else if (!strcmp(argv[c], "-kmax") && c + 1 < argc) {
179         argv[c] = NULL;
180         anim_config.kmax = ExUtilGetInt(argv[++c], 0, &parse_error);
181       } else if (!strcmp(argv[c], "-loop") && c + 1 < argc) {
182         argv[c] = NULL;
183         loop_count = ExUtilGetInt(argv[++c], 0, &parse_error);
184         if (loop_count < 0) {
185           fprintf(stderr, "Invalid non-positive loop-count (%d)\n", loop_count);
186           parse_error = 1;
187         }
188       } else if (!strcmp(argv[c], "-min_size")) {
189         anim_config.minimize_size = 1;
190       } else if (!strcmp(argv[c], "-mixed")) {
191         anim_config.allow_mixed = 1;
192         config.lossless = 0;
193       } else if (!strcmp(argv[c], "-near_lossless") && c + 1 < argc) {
194         argv[c] = NULL;
195         config.near_lossless = ExUtilGetInt(argv[++c], 0, &parse_error);
196       } else if (!strcmp(argv[c], "-sharp_yuv")) {
197         config.use_sharp_yuv = 1;
198       } else if (!strcmp(argv[c], "-v")) {
199         verbose = 1;
200       } else if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) {
201         Help();
202         FREE_WARGV_AND_RETURN(0);
203       } else if (!strcmp(argv[c], "-version")) {
204         const int enc_version = WebPGetEncoderVersion();
205         const int mux_version = WebPGetMuxVersion();
206         const int sharpyuv_version = SharpYuvGetVersion();
207         printf("WebP Encoder version: %d.%d.%d\nWebP Mux version: %d.%d.%d\n",
208                (enc_version >> 16) & 0xff, (enc_version >> 8) & 0xff,
209                enc_version & 0xff, (mux_version >> 16) & 0xff,
210                (mux_version >> 8) & 0xff, mux_version & 0xff);
211         printf("libsharpyuv: %d.%d.%d\n", (sharpyuv_version >> 24) & 0xff,
212                (sharpyuv_version >> 16) & 0xffff, sharpyuv_version & 0xff);
213         goto End;
214       } else {
215         continue;
216       }
217       ok = !parse_error;
218       if (!ok) goto End;
219       argv[c] = NULL;   // mark option as 'parsed' during 1st pass
220     } else {
221       have_input |= 1;
222     }
223   }
224   if (!have_input) {
225     fprintf(stderr, "No input file(s) for generating animation!\n");
226     goto End;
227   }
228 
229   // image-reading pass
230   pic_num = 0;
231   config.lossless = 1;
232   for (c = 0; ok && c < argc; ++c) {
233     if (argv[c] == NULL) continue;
234     if (argv[c][0] == '-') {    // parse local options
235       int parse_error = 0;
236       if (!strcmp(argv[c], "-lossy")) {
237         if (!anim_config.allow_mixed) config.lossless = 0;
238       } else if (!strcmp(argv[c], "-lossless")) {
239         if (!anim_config.allow_mixed) config.lossless = 1;
240       } else if (!strcmp(argv[c], "-q") && c + 1 < argc) {
241         config.quality = ExUtilGetFloat(argv[++c], &parse_error);
242       } else if (!strcmp(argv[c], "-m") && c + 1 < argc) {
243         config.method = ExUtilGetInt(argv[++c], 0, &parse_error);
244       } else if (!strcmp(argv[c], "-d") && c + 1 < argc) {
245         duration = ExUtilGetInt(argv[++c], 0, &parse_error);
246         if (duration <= 0) {
247           fprintf(stderr, "Invalid negative duration (%d)\n", duration);
248           parse_error = 1;
249         }
250       } else {
251         parse_error = 1;   // shouldn't be here.
252         fprintf(stderr, "Unknown option [%s]\n", argv[c]);
253       }
254       ok = !parse_error;
255       if (!ok) goto End;
256       continue;
257     }
258 
259     if (ok) {
260       ok = WebPValidateConfig(&config);
261       if (!ok) {
262         fprintf(stderr, "Invalid configuration.\n");
263         goto End;
264       }
265     }
266 
267     // read next input image
268     pic.use_argb = 1;
269     ok = ReadImage((const char*)GET_WARGV_SHIFTED(argv, c), &pic);
270     if (!ok) goto End;
271 
272     if (enc == NULL) {
273       width  = pic.width;
274       height = pic.height;
275       enc = WebPAnimEncoderNew(width, height, &anim_config);
276       ok = (enc != NULL);
277       if (!ok) {
278         fprintf(stderr, "Could not create WebPAnimEncoder object.\n");
279       }
280     }
281 
282     if (ok) {
283       ok = (width == pic.width && height == pic.height);
284       if (!ok) {
285         fprintf(stderr, "Frame #%d dimension mismatched! "
286                         "Got %d x %d. Was expecting %d x %d.\n",
287                 pic_num, pic.width, pic.height, width, height);
288       }
289     }
290 
291     if (ok) {
292       ok = WebPAnimEncoderAdd(enc, &pic, timestamp_ms, &config);
293       if (!ok) {
294         fprintf(stderr, "Error while adding frame #%d\n", pic_num);
295       }
296     }
297     WebPPictureFree(&pic);
298     if (!ok) goto End;
299 
300     if (verbose) {
301       WFPRINTF(stderr, "Added frame #%3d at time %4d (file: %s)\n",
302                pic_num, timestamp_ms, GET_WARGV_SHIFTED(argv, c));
303     }
304     timestamp_ms += duration;
305     ++pic_num;
306   }
307 
308   // add a last fake frame to signal the last duration
309   ok = ok && WebPAnimEncoderAdd(enc, NULL, timestamp_ms, NULL);
310   ok = ok && WebPAnimEncoderAssemble(enc, &webp_data);
311   if (!ok) {
312     fprintf(stderr, "Error during final animation assembly.\n");
313   }
314 
315  End:
316   // free resources
317   WebPAnimEncoderDelete(enc);
318 
319   if (ok && loop_count > 0) {  // Re-mux to add loop count.
320     ok = SetLoopCount(loop_count, &webp_data);
321   }
322 
323   if (ok) {
324     if (output != NULL) {
325       ok = ImgIoUtilWriteFile(output, webp_data.bytes, webp_data.size);
326       if (ok) WFPRINTF(stderr, "output file: %s     ", (const W_CHAR*)output);
327     } else {
328       fprintf(stderr, "[no output file specified]   ");
329     }
330   }
331 
332   if (ok) {
333     fprintf(stderr, "[%d frames, %u bytes].\n",
334             pic_num, (unsigned int)webp_data.size);
335   }
336   WebPDataClear(&webp_data);
337   ExUtilDeleteCommandLineArguments(&cmd_args);
338   FREE_WARGV_AND_RETURN(ok ? 0 : 1);
339 }
340