New comit of SDL2
[supertux.git] / src / SDL2 / external / libwebp-0.3.0 / examples / cwebp.c
1 // Copyright 2011 Google Inc. All Rights Reserved.
2 //
3 // This code is licensed under the same terms as WebM:
4 //  Software License Agreement:  http://www.webmproject.org/license/software/
5 //  Additional IP Rights Grant:  http://www.webmproject.org/license/additional/
6 // -----------------------------------------------------------------------------
7 //
8 //  simple command line calling the WebPEncode function.
9 //  Encodes a raw .YUV into WebP bitstream
10 //
11 // Author: Skal (pascal.massimino@gmail.com)
12
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16
17 #ifdef HAVE_CONFIG_H
18 #include "config.h"
19 #endif
20
21 #include "webp/encode.h"
22
23 #include "./metadata.h"
24 #include "./stopwatch.h"
25
26 #include "./jpegdec.h"
27 #include "./pngdec.h"
28 #include "./tiffdec.h"
29 #include "./wicdec.h"
30
31 #ifndef WEBP_DLL
32 #if defined(__cplusplus) || defined(c_plusplus)
33 extern "C" {
34 #endif
35
36 extern void* VP8GetCPUInfo;   // opaque forward declaration.
37
38 #if defined(__cplusplus) || defined(c_plusplus)
39 }    // extern "C"
40 #endif
41 #endif  // WEBP_DLL
42
43 //------------------------------------------------------------------------------
44
45 static int verbose = 0;
46
47 static int ReadYUV(FILE* in_file, WebPPicture* const pic) {
48   const int use_argb = pic->use_argb;
49   const int uv_width = (pic->width + 1) / 2;
50   const int uv_height = (pic->height + 1) / 2;
51   int y;
52   int ok = 0;
53
54   pic->use_argb = 0;
55   if (!WebPPictureAlloc(pic)) return ok;
56
57   for (y = 0; y < pic->height; ++y) {
58     if (fread(pic->y + y * pic->y_stride, pic->width, 1, in_file) != 1) {
59       goto End;
60     }
61   }
62   for (y = 0; y < uv_height; ++y) {
63     if (fread(pic->u + y * pic->uv_stride, uv_width, 1, in_file) != 1)
64       goto End;
65   }
66   for (y = 0; y < uv_height; ++y) {
67     if (fread(pic->v + y * pic->uv_stride, uv_width, 1, in_file) != 1)
68       goto End;
69   }
70   ok = 1;
71   if (use_argb) ok = WebPPictureYUVAToARGB(pic);
72
73  End:
74   return ok;
75 }
76
77 #ifdef HAVE_WINCODEC_H
78
79 static int ReadPicture(const char* const filename, WebPPicture* const pic,
80                        int keep_alpha, Metadata* const metadata) {
81   int ok;
82   if (pic->width != 0 && pic->height != 0) {
83     // If image size is specified, infer it as YUV format.
84     FILE* in_file = fopen(filename, "rb");
85     if (in_file == NULL) {
86       fprintf(stderr, "Error! Cannot open input file '%s'\n", filename);
87       return 0;
88     }
89     ok = ReadYUV(in_file, pic);
90     fclose(in_file);
91   } else {
92     // If no size specified, try to decode it using WIC.
93     ok = ReadPictureWithWIC(filename, pic, keep_alpha, metadata);
94   }
95   if (!ok) {
96     fprintf(stderr, "Error! Could not process file %s\n", filename);
97   }
98   return ok;
99 }
100
101 #else  // !HAVE_WINCODEC_H
102
103 typedef enum {
104   PNG_ = 0,
105   JPEG_,
106   TIFF_,  // 'TIFF' clashes with libtiff
107   UNSUPPORTED
108 } InputFileFormat;
109
110 static InputFileFormat GetImageType(FILE* in_file) {
111   InputFileFormat format = UNSUPPORTED;
112   uint32_t magic;
113   uint8_t buf[4];
114
115   if ((fread(&buf[0], 4, 1, in_file) != 1) ||
116       (fseek(in_file, 0, SEEK_SET) != 0)) {
117     return format;
118   }
119
120   magic = ((uint32_t)buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
121   if (magic == 0x89504E47U) {
122     format = PNG_;
123   } else if (magic >= 0xFFD8FF00U && magic <= 0xFFD8FFFFU) {
124     format = JPEG_;
125   } else if (magic == 0x49492A00 || magic == 0x4D4D002A) {
126     format = TIFF_;
127   }
128   return format;
129 }
130
131 static int ReadPicture(const char* const filename, WebPPicture* const pic,
132                        int keep_alpha, Metadata* const metadata) {
133   int ok = 0;
134   FILE* in_file = fopen(filename, "rb");
135   if (in_file == NULL) {
136     fprintf(stderr, "Error! Cannot open input file '%s'\n", filename);
137     return ok;
138   }
139
140   if (pic->width == 0 || pic->height == 0) {
141     // If no size specified, try to decode it as PNG/JPEG (as appropriate).
142     const InputFileFormat format = GetImageType(in_file);
143     if (format == PNG_) {
144       ok = ReadPNG(in_file, pic, keep_alpha, metadata);
145     } else if (format == JPEG_) {
146       ok = ReadJPEG(in_file, pic, metadata);
147     } else if (format == TIFF_) {
148       ok = ReadTIFF(filename, pic, keep_alpha, metadata);
149     }
150   } else {
151     // If image size is specified, infer it as YUV format.
152     ok = ReadYUV(in_file, pic);
153   }
154   if (!ok) {
155     fprintf(stderr, "Error! Could not process file %s\n", filename);
156   }
157
158   fclose(in_file);
159   return ok;
160 }
161
162 #endif  // !HAVE_WINCODEC_H
163
164 static void AllocExtraInfo(WebPPicture* const pic) {
165   const int mb_w = (pic->width + 15) / 16;
166   const int mb_h = (pic->height + 15) / 16;
167   pic->extra_info = (uint8_t*)malloc(mb_w * mb_h * sizeof(*pic->extra_info));
168 }
169
170 static void PrintByteCount(const int bytes[4], int total_size,
171                            int* const totals) {
172   int s;
173   int total = 0;
174   for (s = 0; s < 4; ++s) {
175     fprintf(stderr, "| %7d ", bytes[s]);
176     total += bytes[s];
177     if (totals) totals[s] += bytes[s];
178   }
179   fprintf(stderr, "| %7d  (%.1f%%)\n", total, 100.f * total / total_size);
180 }
181
182 static void PrintPercents(const int counts[4], int total) {
183   int s;
184   for (s = 0; s < 4; ++s) {
185     fprintf(stderr, "|      %2d%%", 100 * counts[s] / total);
186   }
187   fprintf(stderr, "| %7d\n", total);
188 }
189
190 static void PrintValues(const int values[4]) {
191   int s;
192   for (s = 0; s < 4; ++s) {
193     fprintf(stderr, "| %7d ", values[s]);
194   }
195   fprintf(stderr, "|\n");
196 }
197
198 static void PrintFullLosslessInfo(const WebPAuxStats* const stats,
199                                   const char* const description) {
200   fprintf(stderr, "Lossless-%s compressed size: %d bytes\n",
201           description, stats->lossless_size);
202   if (stats->lossless_features) {
203     fprintf(stderr, "  * Lossless features used:");
204     if (stats->lossless_features & 1) fprintf(stderr, " PREDICTION");
205     if (stats->lossless_features & 2) fprintf(stderr, " CROSS-COLOR-TRANSFORM");
206     if (stats->lossless_features & 4) fprintf(stderr, " SUBTRACT-GREEN");
207     if (stats->lossless_features & 8) fprintf(stderr, " PALETTE");
208     fprintf(stderr, "\n");
209   }
210   fprintf(stderr, "  * Precision Bits: histogram=%d transform=%d cache=%d\n",
211           stats->histogram_bits, stats->transform_bits, stats->cache_bits);
212   if (stats->palette_size > 0) {
213     fprintf(stderr, "  * Palette size:   %d\n", stats->palette_size);
214   }
215 }
216
217 static void PrintExtraInfoLossless(const WebPPicture* const pic,
218                                    int short_output,
219                                    const char* const file_name) {
220   const WebPAuxStats* const stats = pic->stats;
221   if (short_output) {
222     fprintf(stderr, "%7d %2.2f\n", stats->coded_size, stats->PSNR[3]);
223   } else {
224     fprintf(stderr, "File:      %s\n", file_name);
225     fprintf(stderr, "Dimension: %d x %d\n", pic->width, pic->height);
226     fprintf(stderr, "Output:    %d bytes\n", stats->coded_size);
227     PrintFullLosslessInfo(stats, "ARGB");
228   }
229 }
230
231 static void PrintExtraInfoLossy(const WebPPicture* const pic, int short_output,
232                                 int full_details,
233                                 const char* const file_name) {
234   const WebPAuxStats* const stats = pic->stats;
235   if (short_output) {
236     fprintf(stderr, "%7d %2.2f\n", stats->coded_size, stats->PSNR[3]);
237   } else {
238     const int num_i4 = stats->block_count[0];
239     const int num_i16 = stats->block_count[1];
240     const int num_skip = stats->block_count[2];
241     const int total = num_i4 + num_i16;
242     fprintf(stderr, "File:      %s\n", file_name);
243     fprintf(stderr, "Dimension: %d x %d%s\n",
244             pic->width, pic->height,
245             stats->alpha_data_size ? " (with alpha)" : "");
246     fprintf(stderr, "Output:    "
247             "%d bytes Y-U-V-All-PSNR %2.2f %2.2f %2.2f   %2.2f dB\n",
248             stats->coded_size,
249             stats->PSNR[0], stats->PSNR[1], stats->PSNR[2], stats->PSNR[3]);
250     if (total > 0) {
251       int totals[4] = { 0, 0, 0, 0 };
252       fprintf(stderr, "block count:  intra4: %d\n"
253                       "              intra16: %d  (-> %.2f%%)\n",
254               num_i4, num_i16, 100.f * num_i16 / total);
255       fprintf(stderr, "              skipped block: %d (%.2f%%)\n",
256               num_skip, 100.f * num_skip / total);
257       fprintf(stderr, "bytes used:  header:         %6d  (%.1f%%)\n"
258                       "             mode-partition: %6d  (%.1f%%)\n",
259               stats->header_bytes[0],
260               100.f * stats->header_bytes[0] / stats->coded_size,
261               stats->header_bytes[1],
262               100.f * stats->header_bytes[1] / stats->coded_size);
263       if (stats->alpha_data_size > 0) {
264         fprintf(stderr, "             transparency:   %6d (%.1f dB)\n",
265                 stats->alpha_data_size, stats->PSNR[4]);
266       }
267       if (stats->layer_data_size) {
268         fprintf(stderr, "             enhancement:    %6d\n",
269                 stats->layer_data_size);
270       }
271       fprintf(stderr, " Residuals bytes  "
272                       "|segment 1|segment 2|segment 3"
273                       "|segment 4|  total\n");
274       if (full_details) {
275         fprintf(stderr, "  intra4-coeffs:  ");
276         PrintByteCount(stats->residual_bytes[0], stats->coded_size, totals);
277         fprintf(stderr, " intra16-coeffs:  ");
278         PrintByteCount(stats->residual_bytes[1], stats->coded_size, totals);
279         fprintf(stderr, "  chroma coeffs:  ");
280         PrintByteCount(stats->residual_bytes[2], stats->coded_size, totals);
281       }
282       fprintf(stderr, "    macroblocks:  ");
283       PrintPercents(stats->segment_size, total);
284       fprintf(stderr, "      quantizer:  ");
285       PrintValues(stats->segment_quant);
286       fprintf(stderr, "   filter level:  ");
287       PrintValues(stats->segment_level);
288       if (full_details) {
289         fprintf(stderr, "------------------+---------");
290         fprintf(stderr, "+---------+---------+---------+-----------------\n");
291         fprintf(stderr, " segments total:  ");
292         PrintByteCount(totals, stats->coded_size, NULL);
293       }
294     }
295     if (stats->lossless_size > 0) {
296       PrintFullLosslessInfo(stats, "alpha");
297     }
298   }
299   if (pic->extra_info != NULL) {
300     const int mb_w = (pic->width + 15) / 16;
301     const int mb_h = (pic->height + 15) / 16;
302     const int type = pic->extra_info_type;
303     int x, y;
304     for (y = 0; y < mb_h; ++y) {
305       for (x = 0; x < mb_w; ++x) {
306         const int c = pic->extra_info[x + y * mb_w];
307         if (type == 1) {   // intra4/intra16
308           printf("%c", "+."[c]);
309         } else if (type == 2) {    // segments
310           printf("%c", ".-*X"[c]);
311         } else if (type == 3) {    // quantizers
312           printf("%.2d ", c);
313         } else if (type == 6 || type == 7) {
314           printf("%3d ", c);
315         } else {
316           printf("0x%.2x ", c);
317         }
318       }
319       printf("\n");
320     }
321   }
322 }
323
324 //------------------------------------------------------------------------------
325
326 static int MyWriter(const uint8_t* data, size_t data_size,
327                     const WebPPicture* const pic) {
328   FILE* const out = (FILE*)pic->custom_ptr;
329   return data_size ? (fwrite(data, data_size, 1, out) == 1) : 1;
330 }
331
332 // Dumps a picture as a PGM file using the IMC4 layout.
333 static int DumpPicture(const WebPPicture* const picture, const char* PGM_name) {
334   int y;
335   const int uv_width = (picture->width + 1) / 2;
336   const int uv_height = (picture->height + 1) / 2;
337   const int stride = (picture->width + 1) & ~1;
338   const int alpha_height =
339       WebPPictureHasTransparency(picture) ? picture->height : 0;
340   const int height = picture->height + uv_height + alpha_height;
341   FILE* const f = fopen(PGM_name, "wb");
342   if (f == NULL) return 0;
343   fprintf(f, "P5\n%d %d\n255\n", stride, height);
344   for (y = 0; y < picture->height; ++y) {
345     if (fwrite(picture->y + y * picture->y_stride, picture->width, 1, f) != 1)
346       return 0;
347     if (picture->width & 1) fputc(0, f);  // pad
348   }
349   for (y = 0; y < uv_height; ++y) {
350     if (fwrite(picture->u + y * picture->uv_stride, uv_width, 1, f) != 1)
351       return 0;
352     if (fwrite(picture->v + y * picture->uv_stride, uv_width, 1, f) != 1)
353       return 0;
354   }
355   for (y = 0; y < alpha_height; ++y) {
356     if (fwrite(picture->a + y * picture->a_stride, picture->width, 1, f) != 1)
357       return 0;
358     if (picture->width & 1) fputc(0, f);  // pad
359   }
360   fclose(f);
361   return 1;
362 }
363
364 // -----------------------------------------------------------------------------
365 // Metadata writing.
366
367 enum {
368   METADATA_EXIF = (1 << 0),
369   METADATA_ICC  = (1 << 1),
370   METADATA_XMP  = (1 << 2),
371   METADATA_ALL  = METADATA_EXIF | METADATA_ICC | METADATA_XMP
372 };
373
374 static const int kChunkHeaderSize = 8;
375 static const int kTagSize = 4;
376
377 static void PrintMetadataInfo(const Metadata* const metadata,
378                               int metadata_written) {
379   if (metadata == NULL || metadata_written == 0) return;
380
381   fprintf(stderr, "Metadata:\n");
382   if (metadata_written & METADATA_ICC) {
383     fprintf(stderr, "  * ICC profile:  %6d bytes\n", (int)metadata->iccp.size);
384   }
385   if (metadata_written & METADATA_EXIF) {
386     fprintf(stderr, "  * EXIF data:    %6d bytes\n", (int)metadata->exif.size);
387   }
388   if (metadata_written & METADATA_XMP) {
389     fprintf(stderr, "  * XMP data:     %6d bytes\n", (int)metadata->xmp.size);
390   }
391 }
392
393 // Outputs, in little endian, 'num' bytes from 'val' to 'out'.
394 static int WriteLE(FILE* const out, uint32_t val, int num) {
395   uint8_t buf[4];
396   int i;
397   for (i = 0; i < num; ++i) {
398     buf[i] = (uint8_t)(val & 0xff);
399     val >>= 8;
400   }
401   return (fwrite(buf, num, 1, out) == 1);
402 }
403
404 static int WriteLE24(FILE* const out, uint32_t val) {
405   return WriteLE(out, val, 3);
406 }
407
408 static int WriteLE32(FILE* const out, uint32_t val) {
409   return WriteLE(out, val, 4);
410 }
411
412 static int WriteMetadataChunk(FILE* const out, const char fourcc[4],
413                               const MetadataPayload* const payload) {
414   const uint8_t zero = 0;
415   const size_t need_padding = payload->size & 1;
416   int ok = (fwrite(fourcc, kTagSize, 1, out) == 1);
417   ok = ok && WriteLE32(out, (uint32_t)payload->size);
418   ok = ok && (fwrite(payload->bytes, payload->size, 1, out) == 1);
419   return ok && (fwrite(&zero, need_padding, need_padding, out) == need_padding);
420 }
421
422 // Sets 'flag' in 'vp8x_flags' and updates 'metadata_size' with the size of the
423 // chunk if there is metadata and 'keep' is true.
424 static int UpdateFlagsAndSize(const MetadataPayload* const payload,
425                               int keep, int flag,
426                               uint32_t* vp8x_flags, uint64_t* metadata_size) {
427   if (keep && payload->bytes != NULL && payload->size > 0) {
428     *vp8x_flags |= flag;
429     *metadata_size += kChunkHeaderSize + payload->size + (payload->size & 1);
430     return 1;
431   }
432   return 0;
433 }
434
435 // Writes a WebP file using the image contained in 'memory_writer' and the
436 // metadata from 'metadata'. Metadata is controlled by 'keep_metadata' and the
437 // availability in 'metadata'. Returns true on success.
438 // For details see doc/webp-container-spec.txt#extended-file-format.
439 static int WriteWebPWithMetadata(FILE* const out,
440                                  const WebPPicture* const picture,
441                                  const WebPMemoryWriter* const memory_writer,
442                                  const Metadata* const metadata,
443                                  int keep_metadata,
444                                  int* const metadata_written) {
445   const char kVP8XHeader[] = "VP8X\x0a\x00\x00\x00";
446   const int kAlphaFlag = 0x10;
447   const int kEXIFFlag  = 0x08;
448   const int kICCPFlag  = 0x20;
449   const int kXMPFlag   = 0x04;
450   const size_t kRiffHeaderSize = 12;
451   const size_t kMaxChunkPayload = ~0 - kChunkHeaderSize - 1;
452   const size_t kMinSize = kRiffHeaderSize + kChunkHeaderSize;
453   uint32_t flags = 0;
454   uint64_t metadata_size = 0;
455   const int write_exif = UpdateFlagsAndSize(&metadata->exif,
456                                             !!(keep_metadata & METADATA_EXIF),
457                                             kEXIFFlag, &flags, &metadata_size);
458   const int write_iccp = UpdateFlagsAndSize(&metadata->iccp,
459                                             !!(keep_metadata & METADATA_ICC),
460                                             kICCPFlag, &flags, &metadata_size);
461   const int write_xmp  = UpdateFlagsAndSize(&metadata->xmp,
462                                             !!(keep_metadata & METADATA_XMP),
463                                             kXMPFlag, &flags, &metadata_size);
464   uint8_t* webp = memory_writer->mem;
465   size_t webp_size = memory_writer->size;
466
467   *metadata_written = 0;
468
469   if (webp_size < kMinSize) return 0;
470   if (webp_size - kChunkHeaderSize + metadata_size > kMaxChunkPayload) {
471     fprintf(stderr, "Error! Addition of metadata would exceed "
472                     "container size limit.\n");
473     return 0;
474   }
475
476   if (metadata_size > 0) {
477     const int kVP8XChunkSize = 18;
478     const int has_vp8x = !memcmp(webp + kRiffHeaderSize, "VP8X", kTagSize);
479     const uint32_t riff_size = (uint32_t)(webp_size - kChunkHeaderSize +
480                                           (has_vp8x ? 0 : kVP8XChunkSize) +
481                                           metadata_size);
482     // RIFF
483     int ok = (fwrite(webp, kTagSize, 1, out) == 1);
484     // RIFF size (file header size is not recorded)
485     ok = ok && WriteLE32(out, riff_size);
486     webp += kChunkHeaderSize;
487     webp_size -= kChunkHeaderSize;
488     // WEBP
489     ok = ok && (fwrite(webp, kTagSize, 1, out) == 1);
490     webp += kTagSize;
491     webp_size -= kTagSize;
492     if (has_vp8x) {  // update the existing VP8X flags
493       webp[kChunkHeaderSize] |= (uint8_t)(flags & 0xff);
494       ok = ok && (fwrite(webp, kVP8XChunkSize, 1, out) == 1);
495       webp_size -= kVP8XChunkSize;
496     } else {
497       const int is_lossless = !memcmp(webp, "VP8L", kTagSize);
498       // The alpha flag is forced with lossless images.
499       if (is_lossless) flags |= kAlphaFlag;
500       ok = ok && (fwrite(kVP8XHeader, kChunkHeaderSize, 1, out) == 1);
501       ok = ok && WriteLE32(out, flags);
502       ok = ok && WriteLE24(out, picture->width - 1);
503       ok = ok && WriteLE24(out, picture->height - 1);
504     }
505     if (write_iccp) {
506       ok = ok && WriteMetadataChunk(out, "ICCP", &metadata->iccp);
507       *metadata_written |= METADATA_ICC;
508     }
509     // Image
510     ok = ok && (fwrite(webp, webp_size, 1, out) == 1);
511     if (write_exif) {
512       ok = ok && WriteMetadataChunk(out, "EXIF", &metadata->exif);
513       *metadata_written |= METADATA_EXIF;
514     }
515     if (write_xmp) {
516       ok = ok && WriteMetadataChunk(out, "XMP ", &metadata->xmp);
517       *metadata_written |= METADATA_XMP;
518     }
519     return ok;
520   } else {
521     // No metadata, just write the original image file.
522     return (fwrite(webp, webp_size, 1, out) == 1);
523   }
524 }
525
526 //------------------------------------------------------------------------------
527
528 static int ProgressReport(int percent, const WebPPicture* const picture) {
529   printf("[%s]: %3d %%      \r",
530          (char*)picture->user_data, percent);
531   fflush(stdout);
532   return 1;  // all ok
533 }
534
535 //------------------------------------------------------------------------------
536
537 static void HelpShort(void) {
538   printf("Usage:\n\n");
539   printf("   cwebp [options] -q quality input.png -o output.webp\n\n");
540   printf("where quality is between 0 (poor) to 100 (very good).\n");
541   printf("Typical value is around 80.\n\n");
542   printf("Try -longhelp for an exhaustive list of advanced options.\n");
543 }
544
545 static void HelpLong(void) {
546   printf("Usage:\n");
547   printf(" cwebp [-preset <...>] [options] in_file [-o out_file]\n\n");
548   printf("If input size (-s) for an image is not specified, "
549          "it is assumed to be a PNG, JPEG or TIFF file.\n");
550 #ifdef HAVE_WINCODEC_H
551   printf("Windows builds can take as input any of the files handled by WIC\n");
552 #endif
553   printf("options:\n");
554   printf("  -h / -help  ............ short help\n");
555   printf("  -H / -longhelp  ........ long help\n");
556   printf("  -q <float> ............. quality factor (0:small..100:big)\n");
557   printf("  -alpha_q <int> ......... Transparency-compression quality "
558          "(0..100).\n");
559   printf("  -preset <string> ....... Preset setting, one of:\n");
560   printf("                            default, photo, picture,\n");
561   printf("                            drawing, icon, text\n");
562   printf("     -preset must come first, as it overwrites other parameters.");
563   printf("\n");
564   printf("  -m <int> ............... compression method (0=fast, 6=slowest)\n");
565   printf("  -segments <int> ........ number of segments to use (1..4)\n");
566   printf("  -size <int> ............ Target size (in bytes)\n");
567   printf("  -psnr <float> .......... Target PSNR (in dB. typically: 42)\n");
568   printf("\n");
569   printf("  -s <int> <int> ......... Input size (width x height) for YUV\n");
570   printf("  -sns <int> ............. Spatial Noise Shaping (0:off, 100:max)\n");
571   printf("  -f <int> ............... filter strength (0=off..100)\n");
572   printf("  -sharpness <int> ....... "
573          "filter sharpness (0:most .. 7:least sharp)\n");
574   printf("  -strong ................ use strong filter instead "
575                                      "of simple (default).\n");
576   printf("  -nostrong .............. use simple filter instead of strong.\n");
577   printf("  -partition_limit <int> . limit quality to fit the 512k limit on\n");
578   printf("                           "
579          "the first partition (0=no degradation ... 100=full)\n");
580   printf("  -pass <int> ............ analysis pass number (1..10)\n");
581   printf("  -crop <x> <y> <w> <h> .. crop picture with the given rectangle\n");
582   printf("  -resize <w> <h> ........ resize picture (after any cropping)\n");
583   printf("  -mt .................... use multi-threading if available\n");
584   printf("  -low_memory ............ reduce memory usage (slower encoding)\n");
585 #ifdef WEBP_EXPERIMENTAL_FEATURES
586   printf("  -444 / -422 / -gray ..... Change colorspace\n");
587 #endif
588   printf("  -map <int> ............. print map of extra info.\n");
589   printf("  -print_psnr ............ prints averaged PSNR distortion.\n");
590   printf("  -print_ssim ............ prints averaged SSIM distortion.\n");
591   printf("  -print_lsim ............ prints local-similarity distortion.\n");
592   printf("  -d <file.pgm> .......... dump the compressed output (PGM file).\n");
593   printf("  -alpha_method <int> .... Transparency-compression method (0..1)\n");
594   printf("  -alpha_filter <string> . predictive filtering for alpha plane.\n");
595   printf("                           One of: none, fast (default) or best.\n");
596   printf("  -alpha_cleanup ......... Clean RGB values in transparent area.\n");
597   printf("  -blend_alpha <hex> ..... Blend colors against background color\n"
598          "                           expressed as RGB values written in\n"
599          "                           hexadecimal, e.g. 0xc0e0d0 for red=0xc0\n"
600          "                           green=0xe0 and blue=0xd0.\n");
601   printf("  -noalpha ............... discard any transparency information.\n");
602   printf("  -lossless .............. Encode image losslessly.\n");
603   printf("  -hint <string> ......... Specify image characteristics hint.\n");
604   printf("                           One of: photo, picture or graph\n");
605
606   printf("\n");
607   printf("  -metadata <string> ..... comma separated list of metadata to\n");
608   printf("                           ");
609   printf("copy from the input to the output if present.\n");
610   printf("                           "
611          "Valid values: all, none (default), exif, icc, xmp\n");
612
613   printf("\n");
614   printf("  -short ................. condense printed message\n");
615   printf("  -quiet ................. don't print anything.\n");
616   printf("  -version ............... print version number and exit.\n");
617 #ifndef WEBP_DLL
618   printf("  -noasm ................. disable all assembly optimizations.\n");
619 #endif
620   printf("  -v ..................... verbose, e.g. print encoding/decoding "
621          "times\n");
622   printf("  -progress .............. report encoding progress\n");
623   printf("\n");
624   printf("Experimental Options:\n");
625   printf("  -jpeg_like ............. Roughly match expected JPEG size.\n");
626   printf("  -af .................... auto-adjust filter strength.\n");
627   printf("  -pre <int> ............. pre-processing filter\n");
628   printf("\n");
629 }
630
631 //------------------------------------------------------------------------------
632 // Error messages
633
634 static const char* const kErrorMessages[] = {
635   "OK",
636   "OUT_OF_MEMORY: Out of memory allocating objects",
637   "BITSTREAM_OUT_OF_MEMORY: Out of memory re-allocating byte buffer",
638   "NULL_PARAMETER: NULL parameter passed to function",
639   "INVALID_CONFIGURATION: configuration is invalid",
640   "BAD_DIMENSION: Bad picture dimension. Maximum width and height "
641   "allowed is 16383 pixels.",
642   "PARTITION0_OVERFLOW: Partition #0 is too big to fit 512k.\n"
643   "To reduce the size of this partition, try using less segments "
644   "with the -segments option, and eventually reduce the number of "
645   "header bits using -partition_limit. More details are available "
646   "in the manual (`man cwebp`)",
647   "PARTITION_OVERFLOW: Partition is too big to fit 16M",
648   "BAD_WRITE: Picture writer returned an I/O error",
649   "FILE_TOO_BIG: File would be too big to fit in 4G",
650   "USER_ABORT: encoding abort requested by user"
651 };
652
653 //------------------------------------------------------------------------------
654
655 int main(int argc, const char *argv[]) {
656   int return_value = -1;
657   const char *in_file = NULL, *out_file = NULL, *dump_file = NULL;
658   FILE *out = NULL;
659   int c;
660   int short_output = 0;
661   int quiet = 0;
662   int keep_alpha = 1;
663   int blend_alpha = 0;
664   uint32_t background_color = 0xffffffu;
665   int crop = 0, crop_x = 0, crop_y = 0, crop_w = 0, crop_h = 0;
666   int resize_w = 0, resize_h = 0;
667   int show_progress = 0;
668   int keep_metadata = 0;
669   int metadata_written = 0;
670   WebPPicture picture;
671   int print_distortion = -1;        // -1=off, 0=PSNR, 1=SSIM, 2=LSIM
672   WebPPicture original_picture;    // when PSNR or SSIM is requested
673   WebPConfig config;
674   WebPAuxStats stats;
675   WebPMemoryWriter memory_writer;
676   Metadata metadata;
677   Stopwatch stop_watch;
678
679   MetadataInit(&metadata);
680   WebPMemoryWriterInit(&memory_writer);
681   if (!WebPPictureInit(&picture) ||
682       !WebPPictureInit(&original_picture) ||
683       !WebPConfigInit(&config)) {
684     fprintf(stderr, "Error! Version mismatch!\n");
685     return -1;
686   }
687
688   if (argc == 1) {
689     HelpShort();
690     return 0;
691   }
692
693   for (c = 1; c < argc; ++c) {
694     if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) {
695       HelpShort();
696       return 0;
697     } else if (!strcmp(argv[c], "-H") || !strcmp(argv[c], "-longhelp")) {
698       HelpLong();
699       return 0;
700     } else if (!strcmp(argv[c], "-o") && c < argc - 1) {
701       out_file = argv[++c];
702     } else if (!strcmp(argv[c], "-d") && c < argc - 1) {
703       dump_file = argv[++c];
704       config.show_compressed = 1;
705     } else if (!strcmp(argv[c], "-print_psnr")) {
706       config.show_compressed = 1;
707       print_distortion = 0;
708     } else if (!strcmp(argv[c], "-print_ssim")) {
709       config.show_compressed = 1;
710       print_distortion = 1;
711     } else if (!strcmp(argv[c], "-print_lsim")) {
712       config.show_compressed = 1;
713       print_distortion = 2;
714     } else if (!strcmp(argv[c], "-short")) {
715       short_output++;
716     } else if (!strcmp(argv[c], "-s") && c < argc - 2) {
717       picture.width = strtol(argv[++c], NULL, 0);
718       picture.height = strtol(argv[++c], NULL, 0);
719     } else if (!strcmp(argv[c], "-m") && c < argc - 1) {
720       config.method = strtol(argv[++c], NULL, 0);
721     } else if (!strcmp(argv[c], "-q") && c < argc - 1) {
722       config.quality = (float)strtod(argv[++c], NULL);
723     } else if (!strcmp(argv[c], "-alpha_q") && c < argc - 1) {
724       config.alpha_quality = strtol(argv[++c], NULL, 0);
725     } else if (!strcmp(argv[c], "-alpha_method") && c < argc - 1) {
726       config.alpha_compression = strtol(argv[++c], NULL, 0);
727     } else if (!strcmp(argv[c], "-alpha_cleanup")) {
728       keep_alpha = keep_alpha ? 2 : 0;
729     } else if (!strcmp(argv[c], "-blend_alpha") && c < argc - 1) {
730       blend_alpha = 1;
731       background_color = strtol(argv[++c], NULL, 16);  // <- parses '0x' prefix
732       background_color = background_color & 0x00ffffffu;
733     } else if (!strcmp(argv[c], "-alpha_filter") && c < argc - 1) {
734       ++c;
735       if (!strcmp(argv[c], "none")) {
736         config.alpha_filtering = 0;
737       } else if (!strcmp(argv[c], "fast")) {
738         config.alpha_filtering = 1;
739       } else if (!strcmp(argv[c], "best")) {
740         config.alpha_filtering = 2;
741       } else {
742         fprintf(stderr, "Error! Unrecognized alpha filter: %s\n", argv[c]);
743         goto Error;
744       }
745     } else if (!strcmp(argv[c], "-noalpha")) {
746       keep_alpha = 0;
747     } else if (!strcmp(argv[c], "-lossless")) {
748       config.lossless = 1;
749     } else if (!strcmp(argv[c], "-hint") && c < argc - 1) {
750       ++c;
751       if (!strcmp(argv[c], "photo")) {
752         config.image_hint = WEBP_HINT_PHOTO;
753       } else if (!strcmp(argv[c], "picture")) {
754         config.image_hint = WEBP_HINT_PICTURE;
755       } else if (!strcmp(argv[c], "graph")) {
756         config.image_hint = WEBP_HINT_GRAPH;
757       } else {
758         fprintf(stderr, "Error! Unrecognized image hint: %s\n", argv[c]);
759         goto Error;
760       }
761     } else if (!strcmp(argv[c], "-size") && c < argc - 1) {
762       config.target_size = strtol(argv[++c], NULL, 0);
763     } else if (!strcmp(argv[c], "-psnr") && c < argc - 1) {
764       config.target_PSNR = (float)strtod(argv[++c], NULL);
765     } else if (!strcmp(argv[c], "-sns") && c < argc - 1) {
766       config.sns_strength = strtol(argv[++c], NULL, 0);
767     } else if (!strcmp(argv[c], "-f") && c < argc - 1) {
768       config.filter_strength = strtol(argv[++c], NULL, 0);
769     } else if (!strcmp(argv[c], "-af")) {
770       config.autofilter = 1;
771     } else if (!strcmp(argv[c], "-jpeg_like")) {
772       config.emulate_jpeg_size = 1;
773     } else if (!strcmp(argv[c], "-mt")) {
774       ++config.thread_level;  // increase thread level
775     } else if (!strcmp(argv[c], "-low_memory")) {
776       config.low_memory = 1;
777     } else if (!strcmp(argv[c], "-strong")) {
778       config.filter_type = 1;
779     } else if (!strcmp(argv[c], "-nostrong")) {
780       config.filter_type = 0;
781     } else if (!strcmp(argv[c], "-sharpness") && c < argc - 1) {
782       config.filter_sharpness = strtol(argv[++c], NULL, 0);
783     } else if (!strcmp(argv[c], "-pass") && c < argc - 1) {
784       config.pass = strtol(argv[++c], NULL, 0);
785     } else if (!strcmp(argv[c], "-pre") && c < argc - 1) {
786       config.preprocessing = strtol(argv[++c], NULL, 0);
787     } else if (!strcmp(argv[c], "-segments") && c < argc - 1) {
788       config.segments = strtol(argv[++c], NULL, 0);
789     } else if (!strcmp(argv[c], "-partition_limit") && c < argc - 1) {
790       config.partition_limit = strtol(argv[++c], NULL, 0);
791     } else if (!strcmp(argv[c], "-map") && c < argc - 1) {
792       picture.extra_info_type = strtol(argv[++c], NULL, 0);
793 #ifdef WEBP_EXPERIMENTAL_FEATURES
794     } else if (!strcmp(argv[c], "-444")) {
795       picture.colorspace = WEBP_YUV444;
796     } else if (!strcmp(argv[c], "-422")) {
797       picture.colorspace = WEBP_YUV422;
798     } else if (!strcmp(argv[c], "-gray")) {
799       picture.colorspace = WEBP_YUV400;
800 #endif
801     } else if (!strcmp(argv[c], "-crop") && c < argc - 4) {
802       crop = 1;
803       crop_x = strtol(argv[++c], NULL, 0);
804       crop_y = strtol(argv[++c], NULL, 0);
805       crop_w = strtol(argv[++c], NULL, 0);
806       crop_h = strtol(argv[++c], NULL, 0);
807     } else if (!strcmp(argv[c], "-resize") && c < argc - 2) {
808       resize_w = strtol(argv[++c], NULL, 0);
809       resize_h = strtol(argv[++c], NULL, 0);
810 #ifndef WEBP_DLL
811     } else if (!strcmp(argv[c], "-noasm")) {
812       VP8GetCPUInfo = NULL;
813 #endif
814     } else if (!strcmp(argv[c], "-version")) {
815       const int version = WebPGetEncoderVersion();
816       printf("%d.%d.%d\n",
817         (version >> 16) & 0xff, (version >> 8) & 0xff, version & 0xff);
818       return 0;
819     } else if (!strcmp(argv[c], "-progress")) {
820       show_progress = 1;
821     } else if (!strcmp(argv[c], "-quiet")) {
822       quiet = 1;
823     } else if (!strcmp(argv[c], "-preset") && c < argc - 1) {
824       WebPPreset preset;
825       ++c;
826       if (!strcmp(argv[c], "default")) {
827         preset = WEBP_PRESET_DEFAULT;
828       } else if (!strcmp(argv[c], "photo")) {
829         preset = WEBP_PRESET_PHOTO;
830       } else if (!strcmp(argv[c], "picture")) {
831         preset = WEBP_PRESET_PICTURE;
832       } else if (!strcmp(argv[c], "drawing")) {
833         preset = WEBP_PRESET_DRAWING;
834       } else if (!strcmp(argv[c], "icon")) {
835         preset = WEBP_PRESET_ICON;
836       } else if (!strcmp(argv[c], "text")) {
837         preset = WEBP_PRESET_TEXT;
838       } else {
839         fprintf(stderr, "Error! Unrecognized preset: %s\n", argv[c]);
840         goto Error;
841       }
842       if (!WebPConfigPreset(&config, preset, config.quality)) {
843         fprintf(stderr, "Error! Could initialize configuration with preset.\n");
844         goto Error;
845       }
846     } else if (!strcmp(argv[c], "-metadata") && c < argc - 1) {
847       static const struct {
848         const char* option;
849         int flag;
850       } kTokens[] = {
851         { "all",  METADATA_ALL },
852         { "none", 0 },
853         { "exif", METADATA_EXIF },
854         { "icc",  METADATA_ICC },
855         { "xmp",  METADATA_XMP },
856       };
857       const size_t kNumTokens = sizeof(kTokens) / sizeof(kTokens[0]);
858       const char* start = argv[++c];
859       const char* const end = start + strlen(start);
860
861       while (start < end) {
862         size_t i;
863         const char* token = strchr(start, ',');
864         if (token == NULL) token = end;
865
866         for (i = 0; i < kNumTokens; ++i) {
867           if ((size_t)(token - start) == strlen(kTokens[i].option) &&
868               !strncmp(start, kTokens[i].option, strlen(kTokens[i].option))) {
869             if (kTokens[i].flag != 0) {
870               keep_metadata |= kTokens[i].flag;
871             } else {
872               keep_metadata = 0;
873             }
874             break;
875           }
876         }
877         if (i == kNumTokens) {
878           fprintf(stderr, "Error! Unknown metadata type '%.*s'\n",
879                   (int)(token - start), start);
880           HelpLong();
881           return -1;
882         }
883         start = token + 1;
884       }
885 #ifdef HAVE_WINCODEC_H
886       if (keep_metadata != 0 && keep_metadata != METADATA_ICC) {
887         // TODO(jzern): remove when -metadata is supported on all platforms.
888         fprintf(stderr, "Warning: only ICC profile extraction is currently"
889                         " supported on this platform!\n");
890       }
891 #endif
892     } else if (!strcmp(argv[c], "-v")) {
893       verbose = 1;
894     } else if (argv[c][0] == '-') {
895       fprintf(stderr, "Error! Unknown option '%s'\n", argv[c]);
896       HelpLong();
897       return -1;
898     } else {
899       in_file = argv[c];
900     }
901   }
902   if (in_file == NULL) {
903     fprintf(stderr, "No input file specified!\n");
904     HelpShort();
905     goto Error;
906   }
907
908   // Check for unsupported command line options for lossless mode and log
909   // warning for such options.
910   if (!quiet && config.lossless == 1) {
911     if (config.target_size > 0 || config.target_PSNR > 0) {
912       fprintf(stderr, "Encoding for specified size or PSNR is not supported"
913                       " for lossless encoding. Ignoring such option(s)!\n");
914     }
915     if (config.partition_limit > 0) {
916       fprintf(stderr, "Partition limit option is not required for lossless"
917                       " encoding. Ignoring this option!\n");
918     }
919   }
920
921   if (!WebPValidateConfig(&config)) {
922     fprintf(stderr, "Error! Invalid configuration.\n");
923     goto Error;
924   }
925
926   // Read the input
927   if (verbose) {
928     StopwatchReadAndReset(&stop_watch);
929   }
930   if (!ReadPicture(in_file, &picture, keep_alpha,
931                    (keep_metadata == 0) ? NULL : &metadata)) {
932     fprintf(stderr, "Error! Cannot read input picture file '%s'\n", in_file);
933     goto Error;
934   }
935   picture.progress_hook = (show_progress && !quiet) ? ProgressReport : NULL;
936
937   if (blend_alpha) {
938     WebPBlendAlpha(&picture, background_color);
939   }
940
941   if (keep_alpha == 2) {
942     WebPCleanupTransparentArea(&picture);
943   }
944
945   if (verbose) {
946     const double read_time = StopwatchReadAndReset(&stop_watch);
947     fprintf(stderr, "Time to read input: %.3fs\n", read_time);
948   }
949
950   // Open the output
951   if (out_file) {
952     out = fopen(out_file, "wb");
953     if (out == NULL) {
954       fprintf(stderr, "Error! Cannot open output file '%s'\n", out_file);
955       goto Error;
956     } else {
957       if (!short_output && !quiet) {
958         fprintf(stderr, "Saving file '%s'\n", out_file);
959       }
960     }
961     if (keep_metadata == 0) {
962       picture.writer = MyWriter;
963       picture.custom_ptr = (void*)out;
964     } else {
965       picture.writer = WebPMemoryWrite;
966       picture.custom_ptr = (void*)&memory_writer;
967     }
968   } else {
969     out = NULL;
970     if (!quiet && !short_output) {
971       fprintf(stderr, "No output file specified (no -o flag). Encoding will\n");
972       fprintf(stderr, "be performed, but its results discarded.\n\n");
973     }
974   }
975   if (!quiet) {
976     picture.stats = &stats;
977     picture.user_data = (void*)in_file;
978   }
979
980   // Compress
981   if (verbose) {
982     StopwatchReadAndReset(&stop_watch);
983   }
984   if (crop != 0) {
985     // We use self-cropping using a view.
986     if (!WebPPictureView(&picture, crop_x, crop_y, crop_w, crop_h, &picture)) {
987       fprintf(stderr, "Error! Cannot crop picture\n");
988       goto Error;
989     }
990   }
991   if ((resize_w | resize_h) > 0) {
992     if (!WebPPictureRescale(&picture, resize_w, resize_h)) {
993       fprintf(stderr, "Error! Cannot resize picture\n");
994       goto Error;
995     }
996   }
997   if (picture.extra_info_type > 0) {
998     AllocExtraInfo(&picture);
999   }
1000   if (print_distortion >= 0) {  // Save original picture for later comparison
1001     WebPPictureCopy(&picture, &original_picture);
1002   }
1003   if (!WebPEncode(&config, &picture)) {
1004     fprintf(stderr, "Error! Cannot encode picture as WebP\n");
1005     fprintf(stderr, "Error code: %d (%s)\n",
1006             picture.error_code, kErrorMessages[picture.error_code]);
1007     goto Error;
1008   }
1009   if (verbose) {
1010     const double encode_time = StopwatchReadAndReset(&stop_watch);
1011     fprintf(stderr, "Time to encode picture: %.3fs\n", encode_time);
1012   }
1013
1014   // Write info
1015   if (dump_file) {
1016     if (picture.use_argb) {
1017       fprintf(stderr, "Warning: can't dump file (-d option) in lossless mode.");
1018     } else if (!DumpPicture(&picture, dump_file)) {
1019       fprintf(stderr, "Warning, couldn't dump picture %s\n", dump_file);
1020     }
1021   }
1022
1023   if (keep_metadata != 0 && out != NULL) {
1024     if (!WriteWebPWithMetadata(out, &picture, &memory_writer,
1025                                &metadata, keep_metadata, &metadata_written)) {
1026       fprintf(stderr, "Error writing WebP file with metadata!\n");
1027       goto Error;
1028     }
1029   }
1030
1031   if (!quiet) {
1032     if (config.lossless) {
1033       PrintExtraInfoLossless(&picture, short_output, in_file);
1034     } else {
1035       PrintExtraInfoLossy(&picture, short_output, config.low_memory, in_file);
1036     }
1037     if (!short_output) {
1038       PrintMetadataInfo(&metadata, metadata_written);
1039     }
1040   }
1041   if (!quiet && !short_output && print_distortion >= 0) {  // print distortion
1042     static const char* distortion_names[] = { "PSNR", "SSIM", "LSIM" };
1043     float values[5];
1044     WebPPictureDistortion(&picture, &original_picture,
1045                           print_distortion, values);
1046     fprintf(stderr, "%s: Y:%.2f U:%.2f V:%.2f A:%.2f  Total:%.2f\n",
1047             distortion_names[print_distortion],
1048             values[0], values[1], values[2], values[3], values[4]);
1049   }
1050   return_value = 0;
1051
1052  Error:
1053   free(memory_writer.mem);
1054   free(picture.extra_info);
1055   MetadataFree(&metadata);
1056   WebPPictureFree(&picture);
1057   WebPPictureFree(&original_picture);
1058   if (out != NULL) {
1059     fclose(out);
1060   }
1061
1062   return return_value;
1063 }
1064
1065 //------------------------------------------------------------------------------