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