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 //------------------------------------------------------------------------------