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