go-libwebp

Experimental translation from libwebp to Go source.
Log | Files | Refs | README | LICENSE

cwebp.c (50869B)


      1 // Copyright 2011 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 //  simple command line calling the WebPEncode function.
     11 //  Encodes a raw .YUV into WebP bitstream
     12 //
     13 // Author: Skal (pascal.massimino@gmail.com)
     14 
     15 #include <assert.h>
     16 #include <stdio.h>
     17 #include <stdlib.h>
     18 #include <string.h>
     19 
     20 #ifdef HAVE_CONFIG_H
     21 #include "webp/config.h"
     22 #endif
     23 
     24 #include "../examples/example_util.h"
     25 #include "../imageio/image_dec.h"
     26 #include "../imageio/imageio_util.h"
     27 #include "../imageio/webpdec.h"
     28 #include "./stopwatch.h"
     29 #include "./unicode.h"
     30 #include "imageio/metadata.h"
     31 #include "sharpyuv/sharpyuv.h"
     32 #include "webp/encode.h"
     33 #include "webp/types.h"
     34 
     35 #ifndef WEBP_DLL
     36 #ifdef __cplusplus
     37 extern "C" {
     38 #endif
     39 
     40 extern void* VP8GetCPUInfo;   // opaque forward declaration.
     41 
     42 #ifdef __cplusplus
     43 }    // extern "C"
     44 #endif
     45 #endif  // WEBP_DLL
     46 
     47 //------------------------------------------------------------------------------
     48 
     49 static int verbose = 0;
     50 
     51 static int ReadYUV(const uint8_t* const data, size_t data_size,
     52                    WebPPicture* const pic) {
     53   const int use_argb = pic->use_argb;
     54   const int uv_width = (pic->width + 1) / 2;
     55   const int uv_height = (pic->height + 1) / 2;
     56   const int y_plane_size = pic->width * pic->height;
     57   const int uv_plane_size = uv_width * uv_height;
     58   const size_t expected_data_size = y_plane_size + 2 * uv_plane_size;
     59 
     60   if (data_size != expected_data_size) {
     61     fprintf(stderr,
     62             "input data doesn't have the expected size (%d instead of %d)\n",
     63             (int)data_size, (int)expected_data_size);
     64     return 0;
     65   }
     66 
     67   pic->use_argb = 0;
     68   if (!WebPPictureAlloc(pic)) return 0;
     69   ImgIoUtilCopyPlane(data, pic->width, pic->y, pic->y_stride,
     70                      pic->width, pic->height);
     71   ImgIoUtilCopyPlane(data + y_plane_size, uv_width,
     72                      pic->u, pic->uv_stride, uv_width, uv_height);
     73   ImgIoUtilCopyPlane(data + y_plane_size + uv_plane_size, uv_width,
     74                      pic->v, pic->uv_stride, uv_width, uv_height);
     75   return use_argb ? WebPPictureYUVAToARGB(pic) : 1;
     76 }
     77 
     78 #ifdef HAVE_WINCODEC_H
     79 
     80 static int ReadPicture(const char* const filename, WebPPicture* const pic,
     81                        int keep_alpha, Metadata* const metadata) {
     82   int ok = 0;
     83   const uint8_t* data = NULL;
     84   size_t data_size = 0;
     85   if (pic->width != 0 && pic->height != 0) {
     86     ok = ImgIoUtilReadFile(filename, &data, &data_size);
     87     ok = ok && ReadYUV(data, data_size, pic);
     88   } else {
     89     // If no size specified, try to decode it using WIC.
     90     ok = ReadPictureWithWIC(filename, pic, keep_alpha, metadata);
     91     if (!ok) {
     92       ok = ImgIoUtilReadFile(filename, &data, &data_size);
     93       ok = ok && ReadWebP(data, data_size, pic, keep_alpha, metadata);
     94     }
     95   }
     96   if (!ok) {
     97     WFPRINTF(stderr, "Error! Could not process file %s\n",
     98              (const W_CHAR*)filename);
     99   }
    100   WebPFree((void*)data);
    101   return ok;
    102 }
    103 
    104 #else  // !HAVE_WINCODEC_H
    105 
    106 static int ReadPicture(const char* const filename, WebPPicture* const pic,
    107                        int keep_alpha, Metadata* const metadata) {
    108   const uint8_t* data = NULL;
    109   size_t data_size = 0;
    110   int ok = 0;
    111 
    112   ok = ImgIoUtilReadFile(filename, &data, &data_size);
    113   if (!ok) goto End;
    114 
    115   if (pic->width == 0 || pic->height == 0) {
    116     WebPImageReader reader = WebPGuessImageReader(data, data_size);
    117     ok = reader(data, data_size, pic, keep_alpha, metadata);
    118   } else {
    119     // If image size is specified, infer it as YUV format.
    120     ok = ReadYUV(data, data_size, pic);
    121   }
    122  End:
    123   if (!ok) {
    124     WFPRINTF(stderr, "Error! Could not process file %s\n",
    125              (const W_CHAR*)filename);
    126   }
    127   WebPFree((void*)data);
    128   return ok;
    129 }
    130 
    131 #endif  // !HAVE_WINCODEC_H
    132 
    133 static void AllocExtraInfo(WebPPicture* const pic) {
    134   const int mb_w = (pic->width + 15) / 16;
    135   const int mb_h = (pic->height + 15) / 16;
    136   pic->extra_info =
    137       (uint8_t*)WebPMalloc(mb_w * mb_h * sizeof(*pic->extra_info));
    138 }
    139 
    140 static void PrintByteCount(const int bytes[4], int total_size,
    141                            int* const totals) {
    142   int s;
    143   int total = 0;
    144   for (s = 0; s < 4; ++s) {
    145     fprintf(stderr, "| %7d ", bytes[s]);
    146     total += bytes[s];
    147     if (totals) totals[s] += bytes[s];
    148   }
    149   fprintf(stderr, "| %7d  (%.1f%%)\n", total, 100.f * total / total_size);
    150 }
    151 
    152 static void PrintPercents(const int counts[4]) {
    153   int s;
    154   const int total = counts[0] + counts[1] + counts[2] + counts[3];
    155   for (s = 0; s < 4; ++s) {
    156     fprintf(stderr, "|     %3d%%", (int)(100. * counts[s] / total + .5));
    157   }
    158   fprintf(stderr, "| %7d\n", total);
    159 }
    160 
    161 static void PrintValues(const int values[4]) {
    162   int s;
    163   for (s = 0; s < 4; ++s) {
    164     fprintf(stderr, "| %7d ", values[s]);
    165   }
    166   fprintf(stderr, "|\n");
    167 }
    168 
    169 static void PrintFullLosslessInfo(const WebPAuxStats* const stats,
    170                                   const char* const description) {
    171   fprintf(stderr, "Lossless-%s compressed size: %d bytes\n",
    172           description, stats->lossless_size);
    173   fprintf(stderr, "  * Header size: %d bytes, image data size: %d\n",
    174           stats->lossless_hdr_size, stats->lossless_data_size);
    175   if (stats->lossless_features) {
    176     fprintf(stderr, "  * Lossless features used:");
    177     if (stats->lossless_features & 1) fprintf(stderr, " PREDICTION");
    178     if (stats->lossless_features & 2) fprintf(stderr, " CROSS-COLOR-TRANSFORM");
    179     if (stats->lossless_features & 4) fprintf(stderr, " SUBTRACT-GREEN");
    180     if (stats->lossless_features & 8) fprintf(stderr, " PALETTE");
    181     fprintf(stderr, "\n");
    182   }
    183   fprintf(stderr, "  * Precision Bits: histogram=%d", stats->histogram_bits);
    184   if (stats->lossless_features & 1) {
    185     fprintf(stderr, " prediction=%d", stats->transform_bits);
    186   }
    187   if (stats->lossless_features & 2) {
    188     fprintf(stderr, " cross-color=%d", stats->cross_color_transform_bits);
    189   }
    190   fprintf(stderr, " cache=%d\n", stats->cache_bits);
    191   if (stats->palette_size > 0) {
    192     fprintf(stderr, "  * Palette size:   %d\n", stats->palette_size);
    193   }
    194 }
    195 
    196 static void PrintExtraInfoLossless(const WebPPicture* const pic,
    197                                    int short_output,
    198                                    const char* const file_name) {
    199   const WebPAuxStats* const stats = pic->stats;
    200   if (short_output) {
    201     fprintf(stderr, "%7d %2.2f\n", stats->coded_size, stats->PSNR[3]);
    202   } else {
    203     WFPRINTF(stderr, "File:      %s\n", (const W_CHAR*)file_name);
    204     fprintf(stderr, "Dimension: %d x %d\n", pic->width, pic->height);
    205     fprintf(stderr, "Output:    %d bytes (%.2f bpp)\n", stats->coded_size,
    206             8.f * stats->coded_size / pic->width / pic->height);
    207     PrintFullLosslessInfo(stats, "ARGB");
    208   }
    209 }
    210 
    211 static void PrintExtraInfoLossy(const WebPPicture* const pic, int short_output,
    212                                 int full_details,
    213                                 const char* const file_name) {
    214   const WebPAuxStats* const stats = pic->stats;
    215   if (short_output) {
    216     fprintf(stderr, "%7d %2.2f\n", stats->coded_size, stats->PSNR[3]);
    217   } else {
    218     const int num_i4 = stats->block_count[0];
    219     const int num_i16 = stats->block_count[1];
    220     const int num_skip = stats->block_count[2];
    221     const int total = num_i4 + num_i16;
    222     WFPRINTF(stderr, "File:      %s\n", (const W_CHAR*)file_name);
    223     fprintf(stderr, "Dimension: %d x %d%s\n",
    224             pic->width, pic->height,
    225             stats->alpha_data_size ? " (with alpha)" : "");
    226     fprintf(stderr, "Output:    "
    227             "%d bytes Y-U-V-All-PSNR %2.2f %2.2f %2.2f   %2.2f dB\n"
    228             "           (%.2f bpp)\n",
    229             stats->coded_size,
    230             stats->PSNR[0], stats->PSNR[1], stats->PSNR[2], stats->PSNR[3],
    231             8.f * stats->coded_size / pic->width / pic->height);
    232     if (total > 0) {
    233       int totals[4] = { 0, 0, 0, 0 };
    234       fprintf(stderr, "block count:  intra4:     %6d  (%.2f%%)\n"
    235                       "              intra16:    %6d  (%.2f%%)\n"
    236                       "              skipped:    %6d  (%.2f%%)\n",
    237               num_i4, 100.f * num_i4 / total,
    238               num_i16, 100.f * num_i16 / total,
    239               num_skip, 100.f * num_skip / total);
    240       fprintf(stderr, "bytes used:  header:         %6d  (%.1f%%)\n"
    241                       "             mode-partition: %6d  (%.1f%%)\n",
    242               stats->header_bytes[0],
    243               100.f * stats->header_bytes[0] / stats->coded_size,
    244               stats->header_bytes[1],
    245               100.f * stats->header_bytes[1] / stats->coded_size);
    246       if (stats->alpha_data_size > 0) {
    247         fprintf(stderr, "             transparency:   %6d (%.1f dB)\n",
    248                 stats->alpha_data_size, stats->PSNR[4]);
    249       }
    250       fprintf(stderr, " Residuals bytes  "
    251                       "|segment 1|segment 2|segment 3"
    252                       "|segment 4|  total\n");
    253       if (full_details) {
    254         fprintf(stderr, "  intra4-coeffs:  ");
    255         PrintByteCount(stats->residual_bytes[0], stats->coded_size, totals);
    256         fprintf(stderr, " intra16-coeffs:  ");
    257         PrintByteCount(stats->residual_bytes[1], stats->coded_size, totals);
    258         fprintf(stderr, "  chroma coeffs:  ");
    259         PrintByteCount(stats->residual_bytes[2], stats->coded_size, totals);
    260       }
    261       fprintf(stderr, "    macroblocks:  ");
    262       PrintPercents(stats->segment_size);
    263       fprintf(stderr, "      quantizer:  ");
    264       PrintValues(stats->segment_quant);
    265       fprintf(stderr, "   filter level:  ");
    266       PrintValues(stats->segment_level);
    267       if (full_details) {
    268         fprintf(stderr, "------------------+---------");
    269         fprintf(stderr, "+---------+---------+---------+-----------------\n");
    270         fprintf(stderr, " segments total:  ");
    271         PrintByteCount(totals, stats->coded_size, NULL);
    272       }
    273     }
    274     if (stats->lossless_size > 0) {
    275       PrintFullLosslessInfo(stats, "alpha");
    276     }
    277   }
    278 }
    279 
    280 static void PrintMapInfo(const WebPPicture* const pic) {
    281   if (pic->extra_info != NULL) {
    282     const int mb_w = (pic->width + 15) / 16;
    283     const int mb_h = (pic->height + 15) / 16;
    284     const int type = pic->extra_info_type;
    285     int x, y;
    286     for (y = 0; y < mb_h; ++y) {
    287       for (x = 0; x < mb_w; ++x) {
    288         const int c = pic->extra_info[x + y * mb_w];
    289         if (type == 1) {   // intra4/intra16
    290           fprintf(stderr, "%c", "+."[c]);
    291         } else if (type == 2) {    // segments
    292           fprintf(stderr, "%c", ".-*X"[c]);
    293         } else if (type == 3) {    // quantizers
    294           fprintf(stderr, "%.2d ", c);
    295         } else if (type == 6 || type == 7) {
    296           fprintf(stderr, "%3d ", c);
    297         } else {
    298           fprintf(stderr, "0x%.2x ", c);
    299         }
    300       }
    301       fprintf(stderr, "\n");
    302     }
    303   }
    304 }
    305 
    306 //------------------------------------------------------------------------------
    307 
    308 static int MyWriter(const uint8_t* data, size_t data_size,
    309                     const WebPPicture* const pic) {
    310   FILE* const out = (FILE*)pic->custom_ptr;
    311   return data_size ? (fwrite(data, data_size, 1, out) == 1) : 1;
    312 }
    313 
    314 // Dumps a picture as a PGM file using the IMC4 layout.
    315 static int DumpPicture(const WebPPicture* const picture, const char* PGM_name) {
    316   int y;
    317   int ok = 0;
    318   const int uv_width = (picture->width + 1) / 2;
    319   const int uv_height = (picture->height + 1) / 2;
    320   const int stride = (picture->width + 1) & ~1;
    321   const uint8_t* src_y = picture->y;
    322   const uint8_t* src_u = picture->u;
    323   const uint8_t* src_v = picture->v;
    324   const uint8_t* src_a = picture->a;
    325   const int alpha_height =
    326       WebPPictureHasTransparency(picture) ? picture->height : 0;
    327   const int height = picture->height + uv_height + alpha_height;
    328   FILE* const f = WFOPEN(PGM_name, "wb");
    329   if (f == NULL) return 0;
    330   fprintf(f, "P5\n%d %d\n255\n", stride, height);
    331   for (y = 0; y < picture->height; ++y) {
    332     if (fwrite(src_y, picture->width, 1, f) != 1) goto Error;
    333     if (picture->width & 1) fputc(0, f);  // pad
    334     src_y += picture->y_stride;
    335   }
    336   for (y = 0; y < uv_height; ++y) {
    337     if (fwrite(src_u, uv_width, 1, f) != 1) goto Error;
    338     if (fwrite(src_v, uv_width, 1, f) != 1) goto Error;
    339     src_u += picture->uv_stride;
    340     src_v += picture->uv_stride;
    341   }
    342   for (y = 0; y < alpha_height; ++y) {
    343     if (fwrite(src_a, picture->width, 1, f) != 1) goto Error;
    344     if (picture->width & 1) fputc(0, f);  // pad
    345     src_a += picture->a_stride;
    346   }
    347   ok = 1;
    348 
    349  Error:
    350   fclose(f);
    351   return ok;
    352 }
    353 
    354 // -----------------------------------------------------------------------------
    355 // Metadata writing.
    356 
    357 enum {
    358   METADATA_EXIF = (1 << 0),
    359   METADATA_ICC  = (1 << 1),
    360   METADATA_XMP  = (1 << 2),
    361   METADATA_ALL  = METADATA_EXIF | METADATA_ICC | METADATA_XMP
    362 };
    363 
    364 static const int kChunkHeaderSize = 8;
    365 static const int kTagSize = 4;
    366 
    367 static void PrintMetadataInfo(const Metadata* const metadata,
    368                               int metadata_written) {
    369   if (metadata == NULL || metadata_written == 0) return;
    370 
    371   fprintf(stderr, "Metadata:\n");
    372   if (metadata_written & METADATA_ICC) {
    373     fprintf(stderr, "  * ICC profile:  %6d bytes\n", (int)metadata->iccp.size);
    374   }
    375   if (metadata_written & METADATA_EXIF) {
    376     fprintf(stderr, "  * EXIF data:    %6d bytes\n", (int)metadata->exif.size);
    377   }
    378   if (metadata_written & METADATA_XMP) {
    379     fprintf(stderr, "  * XMP data:     %6d bytes\n", (int)metadata->xmp.size);
    380   }
    381 }
    382 
    383 // Outputs, in little endian, 'num' bytes from 'val' to 'out'.
    384 static int WriteLE(FILE* const out, uint32_t val, int num) {
    385   uint8_t buf[4];
    386   int i;
    387   for (i = 0; i < num; ++i) {
    388     buf[i] = (uint8_t)(val & 0xff);
    389     val >>= 8;
    390   }
    391   return (fwrite(buf, num, 1, out) == 1);
    392 }
    393 
    394 static int WriteLE24(FILE* const out, uint32_t val) {
    395   return WriteLE(out, val, 3);
    396 }
    397 
    398 static int WriteLE32(FILE* const out, uint32_t val) {
    399   return WriteLE(out, val, 4);
    400 }
    401 
    402 static int WriteMetadataChunk(FILE* const out, const char fourcc[4],
    403                               const MetadataPayload* const payload) {
    404   const uint8_t zero = 0;
    405   const size_t need_padding = payload->size & 1;
    406   int ok = (fwrite(fourcc, kTagSize, 1, out) == 1);
    407   ok = ok && WriteLE32(out, (uint32_t)payload->size);
    408   ok = ok && (fwrite(payload->bytes, payload->size, 1, out) == 1);
    409   return ok && (fwrite(&zero, need_padding, need_padding, out) == need_padding);
    410 }
    411 
    412 // Sets 'flag' in 'vp8x_flags' and updates 'metadata_size' with the size of the
    413 // chunk if there is metadata and 'keep' is true.
    414 static int UpdateFlagsAndSize(const MetadataPayload* const payload,
    415                               int keep, int flag,
    416                               uint32_t* vp8x_flags, uint64_t* metadata_size) {
    417   if (keep && payload->bytes != NULL && payload->size > 0) {
    418     *vp8x_flags |= flag;
    419     *metadata_size += kChunkHeaderSize + payload->size + (payload->size & 1);
    420     return 1;
    421   }
    422   return 0;
    423 }
    424 
    425 // Writes a WebP file using the image contained in 'memory_writer' and the
    426 // metadata from 'metadata'. Metadata is controlled by 'keep_metadata' and the
    427 // availability in 'metadata'. Returns true on success.
    428 // For details see doc/webp-container-spec.txt#extended-file-format.
    429 static int WriteWebPWithMetadata(FILE* const out,
    430                                  const WebPPicture* const picture,
    431                                  const WebPMemoryWriter* const memory_writer,
    432                                  const Metadata* const metadata,
    433                                  int keep_metadata,
    434                                  int* const metadata_written) {
    435   const char kVP8XHeader[] = "VP8X\x0a\x00\x00\x00";
    436   const int kAlphaFlag = 0x10;
    437   const int kEXIFFlag  = 0x08;
    438   const int kICCPFlag  = 0x20;
    439   const int kXMPFlag   = 0x04;
    440   const size_t kRiffHeaderSize = 12;
    441   const size_t kMaxChunkPayload = ~0 - kChunkHeaderSize - 1;
    442   const size_t kMinSize = kRiffHeaderSize + kChunkHeaderSize;
    443   uint32_t flags = 0;
    444   uint64_t metadata_size = 0;
    445   const int write_exif = UpdateFlagsAndSize(&metadata->exif,
    446                                             !!(keep_metadata & METADATA_EXIF),
    447                                             kEXIFFlag, &flags, &metadata_size);
    448   const int write_iccp = UpdateFlagsAndSize(&metadata->iccp,
    449                                             !!(keep_metadata & METADATA_ICC),
    450                                             kICCPFlag, &flags, &metadata_size);
    451   const int write_xmp  = UpdateFlagsAndSize(&metadata->xmp,
    452                                             !!(keep_metadata & METADATA_XMP),
    453                                             kXMPFlag, &flags, &metadata_size);
    454   uint8_t* webp = memory_writer->mem;
    455   size_t webp_size = memory_writer->size;
    456 
    457   *metadata_written = 0;
    458 
    459   if (webp_size < kMinSize) return 0;
    460   if (webp_size - kChunkHeaderSize + metadata_size > kMaxChunkPayload) {
    461     fprintf(stderr, "Error! Addition of metadata would exceed "
    462                     "container size limit.\n");
    463     return 0;
    464   }
    465 
    466   if (metadata_size > 0) {
    467     const int kVP8XChunkSize = 18;
    468     const int has_vp8x = !memcmp(webp + kRiffHeaderSize, "VP8X", kTagSize);
    469     const uint32_t riff_size = (uint32_t)(webp_size - kChunkHeaderSize +
    470                                           (has_vp8x ? 0 : kVP8XChunkSize) +
    471                                           metadata_size);
    472     // RIFF
    473     int ok = (fwrite(webp, kTagSize, 1, out) == 1);
    474     // RIFF size (file header size is not recorded)
    475     ok = ok && WriteLE32(out, riff_size);
    476     webp += kChunkHeaderSize;
    477     webp_size -= kChunkHeaderSize;
    478     // WEBP
    479     ok = ok && (fwrite(webp, kTagSize, 1, out) == 1);
    480     webp += kTagSize;
    481     webp_size -= kTagSize;
    482     if (has_vp8x) {  // update the existing VP8X flags
    483       webp[kChunkHeaderSize] |= (uint8_t)(flags & 0xff);
    484       ok = ok && (fwrite(webp, kVP8XChunkSize, 1, out) == 1);
    485       webp += kVP8XChunkSize;
    486       webp_size -= kVP8XChunkSize;
    487     } else {
    488       const int is_lossless = !memcmp(webp, "VP8L", kTagSize);
    489       if (is_lossless) {
    490         // Presence of alpha is stored in the 37th bit (29th after the
    491         // signature) of VP8L data.
    492         if (webp[kChunkHeaderSize + 4] & (1 << 4)) flags |= kAlphaFlag;
    493       }
    494       ok = ok && (fwrite(kVP8XHeader, kChunkHeaderSize, 1, out) == 1);
    495       ok = ok && WriteLE32(out, flags);
    496       ok = ok && WriteLE24(out, picture->width - 1);
    497       ok = ok && WriteLE24(out, picture->height - 1);
    498     }
    499     if (write_iccp) {
    500       ok = ok && WriteMetadataChunk(out, "ICCP", &metadata->iccp);
    501       *metadata_written |= METADATA_ICC;
    502     }
    503     // Image
    504     ok = ok && (fwrite(webp, webp_size, 1, out) == 1);
    505     if (write_exif) {
    506       ok = ok && WriteMetadataChunk(out, "EXIF", &metadata->exif);
    507       *metadata_written |= METADATA_EXIF;
    508     }
    509     if (write_xmp) {
    510       ok = ok && WriteMetadataChunk(out, "XMP ", &metadata->xmp);
    511       *metadata_written |= METADATA_XMP;
    512     }
    513     return ok;
    514   }
    515 
    516   // No metadata, just write the original image file.
    517   return (fwrite(webp, webp_size, 1, out) == 1);
    518 }
    519 
    520 //------------------------------------------------------------------------------
    521 // Resize
    522 
    523 enum {
    524   RESIZE_MODE_DOWN_ONLY,
    525   RESIZE_MODE_UP_ONLY,
    526   RESIZE_MODE_ALWAYS,
    527   RESIZE_MODE_DEFAULT = RESIZE_MODE_ALWAYS
    528 };
    529 
    530 static void ApplyResizeMode(const int resize_mode,
    531                             const WebPPicture* const pic,
    532                             int* const resize_w, int* const resize_h) {
    533   const int src_w = pic->width;
    534   const int src_h = pic->height;
    535   const int dst_w = *resize_w;
    536   const int dst_h = *resize_h;
    537 
    538   if (resize_mode == RESIZE_MODE_DOWN_ONLY) {
    539     if ((dst_w == 0 && src_h <= dst_h) ||
    540         (dst_h == 0 && src_w <= dst_w) ||
    541         (src_w <= dst_w && src_h <= dst_h)) {
    542       *resize_w = *resize_h = 0;
    543     }
    544   } else if (resize_mode == RESIZE_MODE_UP_ONLY) {
    545     if (src_w >= dst_w && src_h >= dst_h) {
    546       *resize_w = *resize_h = 0;
    547     }
    548   }
    549 }
    550 
    551 //------------------------------------------------------------------------------
    552 
    553 static int ProgressReport(int percent, const WebPPicture* const picture) {
    554   fprintf(stderr, "[%s]: %3d %%      \r",
    555           (char*)picture->user_data, percent);
    556   return 1;  // all ok
    557 }
    558 
    559 //------------------------------------------------------------------------------
    560 
    561 static void HelpShort(void) {
    562   printf("Usage:\n\n");
    563   printf("   cwebp [options] -q quality input.png -o output.webp\n\n");
    564   printf("where quality is between 0 (poor) to 100 (very good).\n");
    565   printf("Typical value is around 80.\n\n");
    566   printf("Try -longhelp for an exhaustive list of advanced options.\n");
    567 }
    568 
    569 static void HelpLong(void) {
    570   printf("Usage:\n");
    571   printf(" cwebp [-preset <...>] [options] in_file [-o out_file]\n\n");
    572   printf("If input size (-s) for an image is not specified, it is\n"
    573          "assumed to be a PNG, JPEG, TIFF or WebP file.\n");
    574   printf("Note: Animated PNG and WebP files are not supported.\n");
    575 #ifdef HAVE_WINCODEC_H
    576   printf("Windows builds can take as input any of the files handled by WIC.\n");
    577 #endif
    578   printf("\nOptions:\n");
    579   printf("  -h / -help ............. short help\n");
    580   printf("  -H / -longhelp ......... long help\n");
    581   printf("  -q <float> ............. quality factor (0:small..100:big), "
    582          "default=75\n");
    583   printf("  -alpha_q <int> ......... transparency-compression quality (0..100),"
    584          "\n                           default=100\n");
    585   printf("  -preset <string> ....... preset setting, one of:\n");
    586   printf("                            default, photo, picture,\n");
    587   printf("                            drawing, icon, text\n");
    588   printf("     -preset must come first, as it overwrites other parameters\n");
    589   printf("  -z <int> ............... activates lossless preset with given\n"
    590          "                           level in [0:fast, ..., 9:slowest]\n");
    591   printf("\n");
    592   printf("  -m <int> ............... compression method (0=fast, 6=slowest), "
    593          "default=4\n");
    594   printf("  -segments <int> ........ number of segments to use (1..4), "
    595          "default=4\n");
    596   printf("  -size <int> ............ target size (in bytes)\n");
    597   printf("  -psnr <float> .......... target PSNR (in dB. typically: 42)\n");
    598   printf("\n");
    599   printf("  -s <int> <int> ......... input size (width x height) for YUV\n");
    600   printf("  -sns <int> ............. spatial noise shaping (0:off, 100:max), "
    601          "default=50\n");
    602   printf("  -f <int> ............... filter strength (0=off..100), "
    603          "default=60\n");
    604   printf("  -sharpness <int> ....... "
    605          "filter sharpness (0:most .. 7:least sharp), default=0\n");
    606   printf("  -strong ................ use strong filter instead "
    607                                      "of simple (default)\n");
    608   printf("  -nostrong .............. use simple filter instead of strong\n");
    609   printf("  -sharp_yuv ............. use sharper (and slower) RGB->YUV "
    610                                      "conversion\n");
    611   printf("  -partition_limit <int> . limit quality to fit the 512k limit on\n");
    612   printf("                           "
    613          "the first partition (0=no degradation ... 100=full)\n");
    614   printf("  -pass <int> ............ analysis pass number (1..10)\n");
    615   printf("  -qrange <min> <max> .... specifies the permissible quality range\n"
    616          "                           (default: 0 100)\n");
    617   printf("  -crop <x> <y> <w> <h> .. crop picture with the given rectangle\n");
    618   printf("  -resize <w> <h> ........ resize picture (*after* any cropping)\n");
    619   printf("  -resize_mode <string> .. one of: up_only, down_only,"
    620          " always (default)\n");
    621   printf("  -mt .................... use multi-threading if available\n");
    622   printf("  -low_memory ............ reduce memory usage (slower encoding)\n");
    623   printf("  -map <int> ............. print map of extra info\n");
    624   printf("  -print_psnr ............ prints averaged PSNR distortion\n");
    625   printf("  -print_ssim ............ prints averaged SSIM distortion\n");
    626   printf("  -print_lsim ............ prints local-similarity distortion\n");
    627   printf("  -d <file.pgm> .......... dump the compressed output (PGM file)\n");
    628   printf("  -alpha_method <int> .... transparency-compression method (0..1), "
    629          "default=1\n");
    630   printf("  -alpha_filter <string> . predictive filtering for alpha plane,\n");
    631   printf("                           one of: none, fast (default) or best\n");
    632   printf("  -exact ................. preserve RGB values in transparent area, "
    633          "default=off\n");
    634   printf("  -blend_alpha <hex> ..... blend colors against background color\n"
    635          "                           expressed as RGB values written in\n"
    636          "                           hexadecimal, e.g. 0xc0e0d0 for red=0xc0\n"
    637          "                           green=0xe0 and blue=0xd0\n");
    638   printf("  -noalpha ............... discard any transparency information\n");
    639   printf("  -lossless .............. encode image losslessly, default=off\n");
    640   printf("  -near_lossless <int> ... use near-lossless image preprocessing\n"
    641          "                           (0..100=off), default=100\n");
    642   printf("  -hint <string> ......... specify image characteristics hint,\n");
    643   printf("                           one of: photo, picture or graph\n");
    644 
    645   printf("\n");
    646   printf("  -metadata <string> ..... comma separated list of metadata to\n");
    647   printf("                           ");
    648   printf("copy from the input to the output if present.\n");
    649   printf("                           "
    650          "Valid values: all, none (default), exif, icc, xmp\n");
    651 
    652   printf("\n");
    653   printf("  -short ................. condense printed message\n");
    654   printf("  -quiet ................. don't print anything\n");
    655   printf("  -version ............... print version number and exit\n");
    656 #ifndef WEBP_DLL
    657   printf("  -noasm ................. disable all assembly optimizations\n");
    658 #endif
    659   printf("  -v ..................... verbose, e.g. print encoding/decoding "
    660          "times\n");
    661   printf("  -progress .............. report encoding progress\n");
    662   printf("\n");
    663   printf("Experimental Options:\n");
    664   printf("  -jpeg_like ............. roughly match expected JPEG size\n");
    665   printf("  -af .................... auto-adjust filter strength\n");
    666   printf("  -pre <int> ............. pre-processing filter\n");
    667   printf("\n");
    668   printf("Supported input formats:\n  %s\n", WebPGetEnabledInputFileFormats());
    669 }
    670 
    671 //------------------------------------------------------------------------------
    672 // Error messages
    673 
    674 static const char* const kErrorMessages[VP8_ENC_ERROR_LAST] = {
    675   "OK",
    676   "OUT_OF_MEMORY: Out of memory allocating objects",
    677   "BITSTREAM_OUT_OF_MEMORY: Out of memory re-allocating byte buffer",
    678   "NULL_PARAMETER: NULL parameter passed to function",
    679   "INVALID_CONFIGURATION: configuration is invalid",
    680   "BAD_DIMENSION: Bad picture dimension. Maximum width and height "
    681   "allowed is 16383 pixels.",
    682   "PARTITION0_OVERFLOW: Partition #0 is too big to fit 512k.\n"
    683   "To reduce the size of this partition, try using less segments "
    684   "with the -segments option, and eventually reduce the number of "
    685   "header bits using -partition_limit. More details are available "
    686   "in the manual (`man cwebp`)",
    687   "PARTITION_OVERFLOW: Partition is too big to fit 16M",
    688   "BAD_WRITE: Picture writer returned an I/O error",
    689   "FILE_TOO_BIG: File would be too big to fit in 4G",
    690   "USER_ABORT: encoding abort requested by user"
    691 };
    692 
    693 //------------------------------------------------------------------------------
    694 
    695 // Returns EXIT_SUCCESS on success, EXIT_FAILURE on failure.
    696 int main(int argc, const char* argv[]) {
    697   int return_value = EXIT_FAILURE;
    698   const char* in_file = NULL, *out_file = NULL, *dump_file = NULL;
    699   FILE* out = NULL;
    700   int c;
    701   int short_output = 0;
    702   int quiet = 0;
    703   int keep_alpha = 1;
    704   int blend_alpha = 0;
    705   uint32_t background_color = 0xffffffu;
    706   int crop = 0, crop_x = 0, crop_y = 0, crop_w = 0, crop_h = 0;
    707   int resize_w = 0, resize_h = 0;
    708   int resize_mode = RESIZE_MODE_DEFAULT;
    709   int lossless_preset = 6;
    710   int use_lossless_preset = -1;  // -1=unset, 0=don't use, 1=use it
    711   int show_progress = 0;
    712   int keep_metadata = 0;
    713   int metadata_written = 0;
    714   WebPPicture picture;
    715   int print_distortion = -1;        // -1=off, 0=PSNR, 1=SSIM, 2=LSIM
    716   WebPPicture original_picture;    // when PSNR or SSIM is requested
    717   WebPConfig config;
    718   WebPAuxStats stats;
    719   WebPMemoryWriter memory_writer;
    720   int use_memory_writer;
    721   Metadata metadata;
    722   Stopwatch stop_watch;
    723 
    724   INIT_WARGV(argc, argv);
    725 
    726   MetadataInit(&metadata);
    727   WebPMemoryWriterInit(&memory_writer);
    728   if (!WebPPictureInit(&picture) ||
    729       !WebPPictureInit(&original_picture) ||
    730       !WebPConfigInit(&config)) {
    731     fprintf(stderr, "Error! Version mismatch!\n");
    732     FREE_WARGV_AND_RETURN(EXIT_FAILURE);
    733   }
    734 
    735   if (argc == 1) {
    736     HelpShort();
    737     FREE_WARGV_AND_RETURN(EXIT_FAILURE);
    738   }
    739 
    740   for (c = 1; c < argc; ++c) {
    741     int parse_error = 0;
    742     if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) {
    743       HelpShort();
    744       FREE_WARGV_AND_RETURN(EXIT_SUCCESS);
    745     } else if (!strcmp(argv[c], "-H") || !strcmp(argv[c], "-longhelp")) {
    746       HelpLong();
    747       FREE_WARGV_AND_RETURN(EXIT_SUCCESS);
    748     } else if (!strcmp(argv[c], "-o") && c + 1 < argc) {
    749       out_file = (const char*)GET_WARGV(argv, ++c);
    750     } else if (!strcmp(argv[c], "-d") && c + 1 < argc) {
    751       dump_file = (const char*)GET_WARGV(argv, ++c);
    752       config.show_compressed = 1;
    753     } else if (!strcmp(argv[c], "-print_psnr")) {
    754       config.show_compressed = 1;
    755       print_distortion = 0;
    756     } else if (!strcmp(argv[c], "-print_ssim")) {
    757       config.show_compressed = 1;
    758       print_distortion = 1;
    759     } else if (!strcmp(argv[c], "-print_lsim")) {
    760       config.show_compressed = 1;
    761       print_distortion = 2;
    762     } else if (!strcmp(argv[c], "-short")) {
    763       ++short_output;
    764     } else if (!strcmp(argv[c], "-s") && c + 2 < argc) {
    765       picture.width = ExUtilGetInt(argv[++c], 0, &parse_error);
    766       picture.height = ExUtilGetInt(argv[++c], 0, &parse_error);
    767       if (picture.width > WEBP_MAX_DIMENSION || picture.width < 0 ||
    768           picture.height > WEBP_MAX_DIMENSION ||  picture.height < 0) {
    769         fprintf(stderr,
    770                 "Specified dimension (%d x %d) is out of range.\n",
    771                 picture.width, picture.height);
    772         goto Error;
    773       }
    774     } else if (!strcmp(argv[c], "-m") && c + 1 < argc) {
    775       config.method = ExUtilGetInt(argv[++c], 0, &parse_error);
    776       use_lossless_preset = 0;   // disable -z option
    777     } else if (!strcmp(argv[c], "-q") && c + 1 < argc) {
    778       config.quality = ExUtilGetFloat(argv[++c], &parse_error);
    779       use_lossless_preset = 0;   // disable -z option
    780     } else if (!strcmp(argv[c], "-z") && c + 1 < argc) {
    781       lossless_preset = ExUtilGetInt(argv[++c], 0, &parse_error);
    782       if (use_lossless_preset != 0) use_lossless_preset = 1;
    783     } else if (!strcmp(argv[c], "-alpha_q") && c + 1 < argc) {
    784       config.alpha_quality = ExUtilGetInt(argv[++c], 0, &parse_error);
    785     } else if (!strcmp(argv[c], "-alpha_method") && c + 1 < argc) {
    786       config.alpha_compression = ExUtilGetInt(argv[++c], 0, &parse_error);
    787     } else if (!strcmp(argv[c], "-alpha_cleanup")) {
    788       // This flag is obsolete, does opposite of -exact.
    789       config.exact = 0;
    790     } else if (!strcmp(argv[c], "-exact")) {
    791       config.exact = 1;
    792     } else if (!strcmp(argv[c], "-blend_alpha") && c + 1 < argc) {
    793       blend_alpha = 1;
    794       // background color is given in hex with an optional '0x' prefix
    795       background_color = ExUtilGetInt(argv[++c], 16, &parse_error);
    796       background_color = background_color & 0x00ffffffu;
    797     } else if (!strcmp(argv[c], "-alpha_filter") && c + 1 < argc) {
    798       ++c;
    799       if (!strcmp(argv[c], "none")) {
    800         config.alpha_filtering = 0;
    801       } else if (!strcmp(argv[c], "fast")) {
    802         config.alpha_filtering = 1;
    803       } else if (!strcmp(argv[c], "best")) {
    804         config.alpha_filtering = 2;
    805       } else {
    806         fprintf(stderr, "Error! Unrecognized alpha filter: %s\n", argv[c]);
    807         goto Error;
    808       }
    809     } else if (!strcmp(argv[c], "-noalpha")) {
    810       keep_alpha = 0;
    811     } else if (!strcmp(argv[c], "-lossless")) {
    812       config.lossless = 1;
    813     } else if (!strcmp(argv[c], "-near_lossless") && c + 1 < argc) {
    814       config.near_lossless = ExUtilGetInt(argv[++c], 0, &parse_error);
    815       config.lossless = 1;  // use near-lossless only with lossless
    816     } else if (!strcmp(argv[c], "-hint") && c + 1 < argc) {
    817       ++c;
    818       if (!strcmp(argv[c], "photo")) {
    819         config.image_hint = WEBP_HINT_PHOTO;
    820       } else if (!strcmp(argv[c], "picture")) {
    821         config.image_hint = WEBP_HINT_PICTURE;
    822       } else if (!strcmp(argv[c], "graph")) {
    823         config.image_hint = WEBP_HINT_GRAPH;
    824       } else {
    825         fprintf(stderr, "Error! Unrecognized image hint: %s\n", argv[c]);
    826         goto Error;
    827       }
    828     } else if (!strcmp(argv[c], "-size") && c + 1 < argc) {
    829       config.target_size = ExUtilGetInt(argv[++c], 0, &parse_error);
    830     } else if (!strcmp(argv[c], "-psnr") && c + 1 < argc) {
    831       config.target_PSNR = ExUtilGetFloat(argv[++c], &parse_error);
    832     } else if (!strcmp(argv[c], "-sns") && c + 1 < argc) {
    833       config.sns_strength = ExUtilGetInt(argv[++c], 0, &parse_error);
    834     } else if (!strcmp(argv[c], "-f") && c + 1 < argc) {
    835       config.filter_strength = ExUtilGetInt(argv[++c], 0, &parse_error);
    836     } else if (!strcmp(argv[c], "-af")) {
    837       config.autofilter = 1;
    838     } else if (!strcmp(argv[c], "-jpeg_like")) {
    839       config.emulate_jpeg_size = 1;
    840     } else if (!strcmp(argv[c], "-mt")) {
    841       ++config.thread_level;  // increase thread level
    842     } else if (!strcmp(argv[c], "-low_memory")) {
    843       config.low_memory = 1;
    844     } else if (!strcmp(argv[c], "-strong")) {
    845       config.filter_type = 1;
    846     } else if (!strcmp(argv[c], "-nostrong")) {
    847       config.filter_type = 0;
    848     } else if (!strcmp(argv[c], "-sharpness") && c + 1 < argc) {
    849       config.filter_sharpness = ExUtilGetInt(argv[++c], 0, &parse_error);
    850     } else if (!strcmp(argv[c], "-sharp_yuv")) {
    851       config.use_sharp_yuv = 1;
    852     } else if (!strcmp(argv[c], "-pass") && c + 1 < argc) {
    853       config.pass = ExUtilGetInt(argv[++c], 0, &parse_error);
    854     } else if (!strcmp(argv[c], "-qrange") && c + 2 < argc) {
    855       config.qmin = ExUtilGetInt(argv[++c], 0, &parse_error);
    856       config.qmax = ExUtilGetInt(argv[++c], 0, &parse_error);
    857       if (config.qmin < 0) config.qmin = 0;
    858       if (config.qmax > 100) config.qmax = 100;
    859     } else if (!strcmp(argv[c], "-pre") && c + 1 < argc) {
    860       config.preprocessing = ExUtilGetInt(argv[++c], 0, &parse_error);
    861     } else if (!strcmp(argv[c], "-segments") && c + 1 < argc) {
    862       config.segments = ExUtilGetInt(argv[++c], 0, &parse_error);
    863     } else if (!strcmp(argv[c], "-partition_limit") && c + 1 < argc) {
    864       config.partition_limit = ExUtilGetInt(argv[++c], 0, &parse_error);
    865     } else if (!strcmp(argv[c], "-map") && c + 1 < argc) {
    866       picture.extra_info_type = ExUtilGetInt(argv[++c], 0, &parse_error);
    867     } else if (!strcmp(argv[c], "-crop") && c + 4 < argc) {
    868       crop = 1;
    869       crop_x = ExUtilGetInt(argv[++c], 0, &parse_error);
    870       crop_y = ExUtilGetInt(argv[++c], 0, &parse_error);
    871       crop_w = ExUtilGetInt(argv[++c], 0, &parse_error);
    872       crop_h = ExUtilGetInt(argv[++c], 0, &parse_error);
    873     } else if (!strcmp(argv[c], "-resize") && c + 2 < argc) {
    874       resize_w = ExUtilGetInt(argv[++c], 0, &parse_error);
    875       resize_h = ExUtilGetInt(argv[++c], 0, &parse_error);
    876     } else if (!strcmp(argv[c], "-resize_mode") && c + 1 < argc) {
    877       ++c;
    878       if (!strcmp(argv[c], "down_only")) {
    879         resize_mode = RESIZE_MODE_DOWN_ONLY;
    880       } else if (!strcmp(argv[c], "up_only")) {
    881         resize_mode = RESIZE_MODE_UP_ONLY;
    882       } else if (!strcmp(argv[c], "always")) {
    883         resize_mode = RESIZE_MODE_ALWAYS;
    884       } else {
    885         fprintf(stderr, "Error! Unrecognized resize mode: %s\n", argv[c]);
    886         goto Error;
    887       }
    888 #ifndef WEBP_DLL
    889     } else if (!strcmp(argv[c], "-noasm")) {
    890       VP8GetCPUInfo = NULL;
    891 #endif
    892     } else if (!strcmp(argv[c], "-version")) {
    893       const int version = WebPGetEncoderVersion();
    894       const int sharpyuv_version = SharpYuvGetVersion();
    895       printf("%d.%d.%d\n",
    896              (version >> 16) & 0xff, (version >> 8) & 0xff, version & 0xff);
    897       printf("libsharpyuv: %d.%d.%d\n",
    898              (sharpyuv_version >> 24) & 0xff, (sharpyuv_version >> 16) & 0xffff,
    899              sharpyuv_version & 0xff);
    900       FREE_WARGV_AND_RETURN(EXIT_SUCCESS);
    901     } else if (!strcmp(argv[c], "-progress")) {
    902       show_progress = 1;
    903     } else if (!strcmp(argv[c], "-quiet")) {
    904       quiet = 1;
    905     } else if (!strcmp(argv[c], "-preset") && c + 1 < argc) {
    906       WebPPreset preset;
    907       ++c;
    908       if (!strcmp(argv[c], "default")) {
    909         preset = WEBP_PRESET_DEFAULT;
    910       } else if (!strcmp(argv[c], "photo")) {
    911         preset = WEBP_PRESET_PHOTO;
    912       } else if (!strcmp(argv[c], "picture")) {
    913         preset = WEBP_PRESET_PICTURE;
    914       } else if (!strcmp(argv[c], "drawing")) {
    915         preset = WEBP_PRESET_DRAWING;
    916       } else if (!strcmp(argv[c], "icon")) {
    917         preset = WEBP_PRESET_ICON;
    918       } else if (!strcmp(argv[c], "text")) {
    919         preset = WEBP_PRESET_TEXT;
    920       } else {
    921         fprintf(stderr, "Error! Unrecognized preset: %s\n", argv[c]);
    922         goto Error;
    923       }
    924       if (!WebPConfigPreset(&config, preset, config.quality)) {
    925         fprintf(stderr, "Error! Could initialize configuration with preset.\n");
    926         goto Error;
    927       }
    928     } else if (!strcmp(argv[c], "-metadata") && c + 1 < argc) {
    929       static const struct {
    930         const char* option;
    931         int flag;
    932       } kTokens[] = {
    933         { "all",  METADATA_ALL },
    934         { "none", 0 },
    935         { "exif", METADATA_EXIF },
    936         { "icc",  METADATA_ICC },
    937         { "xmp",  METADATA_XMP },
    938       };
    939       const size_t kNumTokens = sizeof(kTokens) / sizeof(kTokens[0]);
    940       const char* start = argv[++c];
    941       const char* const end = start + strlen(start);
    942 
    943       while (start < end) {
    944         size_t i;
    945         const char* token = strchr(start, ',');
    946         if (token == NULL) token = end;
    947 
    948         for (i = 0; i < kNumTokens; ++i) {
    949           if ((size_t)(token - start) == strlen(kTokens[i].option) &&
    950               !strncmp(start, kTokens[i].option, strlen(kTokens[i].option))) {
    951             if (kTokens[i].flag != 0) {
    952               keep_metadata |= kTokens[i].flag;
    953             } else {
    954               keep_metadata = 0;
    955             }
    956             break;
    957           }
    958         }
    959         if (i == kNumTokens) {
    960           fprintf(stderr, "Error! Unknown metadata type '%.*s'\n",
    961                   (int)(token - start), start);
    962           FREE_WARGV_AND_RETURN(EXIT_FAILURE);
    963         }
    964         start = token + 1;
    965       }
    966 #ifdef HAVE_WINCODEC_H
    967       if (keep_metadata != 0 && keep_metadata != METADATA_ICC) {
    968         // TODO(jzern): remove when -metadata is supported on all platforms.
    969         fprintf(stderr, "Warning: only ICC profile extraction is currently"
    970                         " supported on this platform!\n");
    971       }
    972 #endif
    973     } else if (!strcmp(argv[c], "-v")) {
    974       verbose = 1;
    975     } else if (!strcmp(argv[c], "--")) {
    976       if (c + 1 < argc) in_file = (const char*)GET_WARGV(argv, ++c);
    977       break;
    978     } else if (argv[c][0] == '-') {
    979       fprintf(stderr, "Error! Unknown option '%s'\n", argv[c]);
    980       HelpLong();
    981       FREE_WARGV_AND_RETURN(EXIT_FAILURE);
    982     } else {
    983       in_file = (const char*)GET_WARGV(argv, c);
    984     }
    985 
    986     if (parse_error) {
    987       HelpLong();
    988       FREE_WARGV_AND_RETURN(EXIT_FAILURE);
    989     }
    990   }
    991   if (in_file == NULL) {
    992     fprintf(stderr, "No input file specified!\n");
    993     HelpShort();
    994     goto Error;
    995   }
    996 
    997   if (use_lossless_preset == 1) {
    998     if (!WebPConfigLosslessPreset(&config, lossless_preset)) {
    999       fprintf(stderr, "Invalid lossless preset (-z %d)\n", lossless_preset);
   1000       goto Error;
   1001     }
   1002   }
   1003 
   1004   // Check for unsupported command line options for lossless mode and log
   1005   // warning for such options.
   1006   if (!quiet && config.lossless == 1) {
   1007     if (config.target_size > 0 || config.target_PSNR > 0) {
   1008       fprintf(stderr, "Encoding for specified size or PSNR is not supported"
   1009                       " for lossless encoding. Ignoring such option(s)!\n");
   1010     }
   1011     if (config.partition_limit > 0) {
   1012       fprintf(stderr, "Partition limit option is not required for lossless"
   1013                       " encoding. Ignoring this option!\n");
   1014     }
   1015   }
   1016   // If a target size or PSNR was given, but somehow the -pass option was
   1017   // omitted, force a reasonable value.
   1018   if (config.target_size > 0 || config.target_PSNR > 0) {
   1019     if (config.pass == 1) config.pass = 6;
   1020   }
   1021 
   1022   if (!WebPValidateConfig(&config)) {
   1023     fprintf(stderr, "Error! Invalid configuration.\n");
   1024     goto Error;
   1025   }
   1026 
   1027   // Read the input. We need to decide if we prefer ARGB or YUVA
   1028   // samples, depending on the expected compression mode (this saves
   1029   // some conversion steps).
   1030   picture.use_argb = (config.lossless || config.use_sharp_yuv ||
   1031                       config.preprocessing > 0 ||
   1032                       crop || (resize_w | resize_h) > 0);
   1033   if (verbose) {
   1034     StopwatchReset(&stop_watch);
   1035   }
   1036   if (!ReadPicture(in_file, &picture, keep_alpha,
   1037                    (keep_metadata == 0) ? NULL : &metadata)) {
   1038     WFPRINTF(stderr, "Error! Cannot read input picture file '%s'\n",
   1039              (const W_CHAR*)in_file);
   1040     goto Error;
   1041   }
   1042   picture.progress_hook = (show_progress && !quiet) ? ProgressReport : NULL;
   1043 
   1044   if (blend_alpha) {
   1045     WebPBlendAlpha(&picture, background_color);
   1046   }
   1047 
   1048   if (verbose) {
   1049     const double read_time = StopwatchReadAndReset(&stop_watch);
   1050     fprintf(stderr, "Time to read input: %.3fs\n", read_time);
   1051   }
   1052   // The bitstream should be kept in memory when metadata must be appended
   1053   // before writing it to a file/stream, and/or when the near-losslessly encoded
   1054   // bitstream must be decoded for distortion computation (lossy will modify the
   1055   // 'picture' but not the lossless pipeline).
   1056   // Otherwise directly write the bitstream to a file.
   1057   use_memory_writer = (out_file != NULL && keep_metadata) ||
   1058                       (!quiet && print_distortion >= 0 && config.lossless &&
   1059                        config.near_lossless < 100);
   1060 
   1061   // Open the output
   1062   if (out_file != NULL) {
   1063     const int use_stdout = !WSTRCMP(out_file, "-");
   1064     out = use_stdout ? ImgIoUtilSetBinaryMode(stdout) : WFOPEN(out_file, "wb");
   1065     if (out == NULL) {
   1066       WFPRINTF(stderr, "Error! Cannot open output file '%s'\n",
   1067                (const W_CHAR*)out_file);
   1068       goto Error;
   1069     } else {
   1070       if (!short_output && !quiet) {
   1071         WFPRINTF(stderr, "Saving file '%s'\n", (const W_CHAR*)out_file);
   1072       }
   1073     }
   1074     if (use_memory_writer) {
   1075       picture.writer = WebPMemoryWrite;
   1076       picture.custom_ptr = (void*)&memory_writer;
   1077     } else {
   1078       picture.writer = MyWriter;
   1079       picture.custom_ptr = (void*)out;
   1080     }
   1081   } else {
   1082     out = NULL;
   1083     if (use_memory_writer) {
   1084       picture.writer = WebPMemoryWrite;
   1085       picture.custom_ptr = (void*)&memory_writer;
   1086     }
   1087     if (!quiet && !short_output) {
   1088       fprintf(stderr, "No output file specified (no -o flag). Encoding will\n");
   1089       fprintf(stderr, "be performed, but its results discarded.\n\n");
   1090     }
   1091   }
   1092   if (!quiet) {
   1093     picture.stats = &stats;
   1094     picture.user_data = (void*)in_file;
   1095   }
   1096 
   1097   // Crop & resize.
   1098   if (verbose) {
   1099     StopwatchReset(&stop_watch);
   1100   }
   1101   if (crop != 0) {
   1102     // We use self-cropping using a view.
   1103     if (!WebPPictureView(&picture, crop_x, crop_y, crop_w, crop_h, &picture)) {
   1104       fprintf(stderr, "Error! Cannot crop picture\n");
   1105       goto Error;
   1106     }
   1107   }
   1108   ApplyResizeMode(resize_mode, &picture, &resize_w, &resize_h);
   1109   if ((resize_w | resize_h) > 0) {
   1110     WebPPicture picture_no_alpha;
   1111     if (config.exact) {
   1112       // If -exact, we can't premultiply RGB by A otherwise RGB is lost if A=0.
   1113       // We rescale an opaque copy and assemble scaled A and non-premultiplied
   1114       // RGB channels. This is slower but it's a very uncommon use case. Color
   1115       // leak at sharp alpha edges is possible.
   1116       if (!WebPPictureCopy(&picture, &picture_no_alpha)) {
   1117         fprintf(stderr, "Error! Cannot copy temporary picture\n");
   1118         goto Error;
   1119       }
   1120 
   1121       // We enforced picture.use_argb = 1 above. Now, remove the alpha values.
   1122       {
   1123         int x, y;
   1124         uint32_t* argb_no_alpha = picture_no_alpha.argb;
   1125         for (y = 0; y < picture_no_alpha.height; ++y) {
   1126           for (x = 0; x < picture_no_alpha.width; ++x) {
   1127             argb_no_alpha[x] |= 0xff000000;  // Opaque copy.
   1128           }
   1129           argb_no_alpha += picture_no_alpha.argb_stride;
   1130         }
   1131       }
   1132 
   1133       if (!WebPPictureRescale(&picture_no_alpha, resize_w, resize_h)) {
   1134         fprintf(stderr, "Error! Cannot resize temporary picture\n");
   1135         goto Error;
   1136       }
   1137     }
   1138 
   1139     if (!WebPPictureRescale(&picture, resize_w, resize_h)) {
   1140       fprintf(stderr, "Error! Cannot resize picture\n");
   1141       goto Error;
   1142     }
   1143 
   1144     if (config.exact) {  // Put back the alpha information.
   1145       int x, y;
   1146       uint32_t* argb_no_alpha = picture_no_alpha.argb;
   1147       uint32_t* argb = picture.argb;
   1148       for (y = 0; y < picture_no_alpha.height; ++y) {
   1149         for (x = 0; x < picture_no_alpha.width; ++x) {
   1150           argb[x] = (argb[x] & 0xff000000) | (argb_no_alpha[x] & 0x00ffffff);
   1151         }
   1152         argb_no_alpha += picture_no_alpha.argb_stride;
   1153         argb += picture.argb_stride;
   1154       }
   1155       WebPPictureFree(&picture_no_alpha);
   1156     }
   1157   }
   1158   if (verbose && (crop != 0 || (resize_w | resize_h) > 0)) {
   1159     const double preproc_time = StopwatchReadAndReset(&stop_watch);
   1160     fprintf(stderr, "Time to crop/resize picture: %.3fs\n", preproc_time);
   1161   }
   1162 
   1163   if (picture.extra_info_type > 0) {
   1164     AllocExtraInfo(&picture);
   1165   }
   1166   // Save original picture for later comparison. Only for lossy as lossless does
   1167   // not modify 'picture' (even near-lossless).
   1168   if (print_distortion >= 0 && !config.lossless &&
   1169       !WebPPictureCopy(&picture, &original_picture)) {
   1170     fprintf(stderr, "Error! Cannot copy temporary picture\n");
   1171     goto Error;
   1172   }
   1173 
   1174   // Compress.
   1175   if (verbose) {
   1176     StopwatchReset(&stop_watch);
   1177   }
   1178   if (!WebPEncode(&config, &picture)) {
   1179     fprintf(stderr, "Error! Cannot encode picture as WebP\n");
   1180     fprintf(stderr, "Error code: %d (%s)\n",
   1181             picture.error_code, kErrorMessages[picture.error_code]);
   1182     goto Error;
   1183   }
   1184   if (verbose) {
   1185     const double encode_time = StopwatchReadAndReset(&stop_watch);
   1186     fprintf(stderr, "Time to encode picture: %.3fs\n", encode_time);
   1187   }
   1188 
   1189   // Get the decompressed image for the lossless pipeline.
   1190   if (!quiet && print_distortion >= 0 && config.lossless) {
   1191     if (config.near_lossless == 100) {
   1192       // Pure lossless: image was not modified, make 'original_picture' a view
   1193       // of 'picture' by copying all members except the freeable pointers.
   1194       original_picture = picture;
   1195       original_picture.memory_ = original_picture.memory_argb_ = NULL;
   1196     } else {
   1197       // Decode the bitstream stored in 'memory_writer' to get the altered image
   1198       // to 'picture'; save the 'original_picture' beforehand.
   1199       assert(use_memory_writer);
   1200       original_picture = picture;
   1201       if (!WebPPictureInit(&picture)) {  // Do not free 'picture'.
   1202         fprintf(stderr, "Error! Version mismatch!\n");
   1203         goto Error;
   1204       }
   1205 
   1206       picture.use_argb = 1;
   1207       if (!ReadWebP(
   1208               memory_writer.mem, memory_writer.size, &picture,
   1209               /*keep_alpha=*/WebPPictureHasTransparency(&original_picture),
   1210               /*metadata=*/NULL)) {
   1211         fprintf(stderr, "Error! Cannot decode encoded WebP bitstream\n");
   1212         fprintf(stderr, "Error code: %d (%s)\n", picture.error_code,
   1213                 kErrorMessages[picture.error_code]);
   1214         goto Error;
   1215       }
   1216       picture.stats = original_picture.stats;
   1217     }
   1218     original_picture.stats = NULL;
   1219   }
   1220 
   1221   // Write the YUV planes to a PGM file. Only available for lossy.
   1222   if (dump_file) {
   1223     if (picture.use_argb) {
   1224       fprintf(stderr, "Warning: can't dump file (-d option) "
   1225                       "in lossless mode.\n");
   1226     } else if (!DumpPicture(&picture, dump_file)) {
   1227       WFPRINTF(stderr, "Warning, couldn't dump picture %s\n",
   1228                (const W_CHAR*)dump_file);
   1229     }
   1230   }
   1231 
   1232   if (use_memory_writer && out != NULL &&
   1233       !WriteWebPWithMetadata(out, &picture, &memory_writer, &metadata,
   1234                              keep_metadata, &metadata_written)) {
   1235     fprintf(stderr, "Error writing WebP file!\n");
   1236     goto Error;
   1237   }
   1238 
   1239   if (out == NULL && keep_metadata) {
   1240     // output is disabled, just display the metadata stats.
   1241     const struct {
   1242       const MetadataPayload* const payload;
   1243       int flag;
   1244     } *iter, info[] = {{&metadata.exif, METADATA_EXIF},
   1245                        {&metadata.iccp, METADATA_ICC},
   1246                        {&metadata.xmp, METADATA_XMP},
   1247                        {NULL, 0}};
   1248     uint32_t unused1 = 0;
   1249     uint64_t unused2 = 0;
   1250 
   1251     for (iter = info; iter->payload != NULL; ++iter) {
   1252       if (UpdateFlagsAndSize(iter->payload, !!(keep_metadata & iter->flag),
   1253                              /*flag=*/0, &unused1, &unused2)) {
   1254         metadata_written |= iter->flag;
   1255       }
   1256     }
   1257   }
   1258 
   1259   if (!quiet) {
   1260     if (!short_output || print_distortion < 0) {
   1261       if (config.lossless) {
   1262         PrintExtraInfoLossless(&picture, short_output, in_file);
   1263       } else {
   1264         PrintExtraInfoLossy(&picture, short_output, config.low_memory, in_file);
   1265       }
   1266     }
   1267     if (!short_output && picture.extra_info_type > 0) {
   1268       PrintMapInfo(&picture);
   1269     }
   1270     if (print_distortion >= 0) {    // print distortion
   1271       static const char* distortion_names[] = { "PSNR", "SSIM", "LSIM" };
   1272       float values[5];
   1273       if (!WebPPictureDistortion(&picture, &original_picture,
   1274                                  print_distortion, values)) {
   1275         fprintf(stderr, "Error while computing the distortion.\n");
   1276         goto Error;
   1277       }
   1278       if (!short_output) {
   1279         fprintf(stderr, "%s: ", distortion_names[print_distortion]);
   1280         fprintf(stderr, "B:%.2f G:%.2f R:%.2f A:%.2f  Total:%.2f\n",
   1281                 values[0], values[1], values[2], values[3], values[4]);
   1282       } else {
   1283         fprintf(stderr, "%7d %.4f\n", picture.stats->coded_size, values[4]);
   1284       }
   1285     }
   1286     if (!short_output) {
   1287       PrintMetadataInfo(&metadata, metadata_written);
   1288     }
   1289   }
   1290   return_value = EXIT_SUCCESS;
   1291 
   1292  Error:
   1293   WebPMemoryWriterClear(&memory_writer);
   1294   WebPFree(picture.extra_info);
   1295   MetadataFree(&metadata);
   1296   WebPPictureFree(&picture);
   1297   WebPPictureFree(&original_picture);
   1298   if (out != NULL && out != stdout) {
   1299     fclose(out);
   1300   }
   1301 
   1302   FREE_WARGV_AND_RETURN(return_value);
   1303 }
   1304 
   1305 //------------------------------------------------------------------------------