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