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