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