github.com/cellofellow/gopkg@v0.0.0-20140722061823-eec0544a62ad/image/webp/libwebp/examples/dwebp.c (about) 1 // Copyright 2010 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 // Command-line tool for decoding a WebP image. 11 // 12 // Author: Skal (pascal.massimino@gmail.com) 13 14 #include <assert.h> 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 #ifdef WEBP_HAVE_PNG 24 #include <png.h> 25 #endif 26 27 #ifdef HAVE_WINCODEC_H 28 #ifdef __MINGW32__ 29 #define INITGUID // Without this GUIDs are declared extern and fail to link 30 #endif 31 #define CINTERFACE 32 #define COBJMACROS 33 #define _WIN32_IE 0x500 // Workaround bug in shlwapi.h when compiling C++ 34 // code with COBJMACROS. 35 #include <ole2.h> // CreateStreamOnHGlobal() 36 #include <shlwapi.h> 37 #include <windows.h> 38 #include <wincodec.h> 39 #endif 40 41 #if defined(_WIN32) 42 #include <fcntl.h> // for _O_BINARY 43 #include <io.h> // for _setmode() 44 #endif 45 46 #include "webp/decode.h" 47 #include "./example_util.h" 48 #include "./stopwatch.h" 49 50 static int verbose = 0; 51 #ifndef WEBP_DLL 52 #ifdef __cplusplus 53 extern "C" { 54 #endif 55 56 extern void* VP8GetCPUInfo; // opaque forward declaration. 57 58 #ifdef __cplusplus 59 } // extern "C" 60 #endif 61 #endif // WEBP_DLL 62 63 //------------------------------------------------------------------------------ 64 65 // Output types 66 typedef enum { 67 PNG = 0, 68 PAM, 69 PPM, 70 PGM, 71 BMP, 72 TIFF, 73 YUV, 74 ALPHA_PLANE_ONLY // this is for experimenting only 75 } OutputFileFormat; 76 77 #ifdef HAVE_WINCODEC_H 78 79 #define IFS(fn) \ 80 do { \ 81 if (SUCCEEDED(hr)) { \ 82 hr = (fn); \ 83 if (FAILED(hr)) fprintf(stderr, #fn " failed %08lx\n", hr); \ 84 } \ 85 } while (0) 86 87 #ifdef __cplusplus 88 #define MAKE_REFGUID(x) (x) 89 #else 90 #define MAKE_REFGUID(x) &(x) 91 #endif 92 93 static HRESULT CreateOutputStream(const char* out_file_name, 94 int write_to_mem, IStream** stream) { 95 HRESULT hr = S_OK; 96 if (write_to_mem) { 97 // Output to a memory buffer. This is freed when 'stream' is released. 98 IFS(CreateStreamOnHGlobal(NULL, TRUE, stream)); 99 } else { 100 IFS(SHCreateStreamOnFileA(out_file_name, STGM_WRITE | STGM_CREATE, stream)); 101 } 102 if (FAILED(hr)) { 103 fprintf(stderr, "Error opening output file %s (%08lx)\n", 104 out_file_name, hr); 105 } 106 return hr; 107 } 108 109 static HRESULT WriteUsingWIC(const char* out_file_name, int use_stdout, 110 REFGUID container_guid, 111 uint8_t* rgb, int stride, 112 uint32_t width, uint32_t height, int has_alpha) { 113 HRESULT hr = S_OK; 114 IWICImagingFactory* factory = NULL; 115 IWICBitmapFrameEncode* frame = NULL; 116 IWICBitmapEncoder* encoder = NULL; 117 IStream* stream = NULL; 118 WICPixelFormatGUID pixel_format = has_alpha ? GUID_WICPixelFormat32bppBGRA 119 : GUID_WICPixelFormat24bppBGR; 120 121 IFS(CoInitialize(NULL)); 122 IFS(CoCreateInstance(MAKE_REFGUID(CLSID_WICImagingFactory), NULL, 123 CLSCTX_INPROC_SERVER, 124 MAKE_REFGUID(IID_IWICImagingFactory), 125 (LPVOID*)&factory)); 126 if (hr == REGDB_E_CLASSNOTREG) { 127 fprintf(stderr, 128 "Couldn't access Windows Imaging Component (are you running " 129 "Windows XP SP3 or newer?). PNG support not available. " 130 "Use -ppm or -pgm for available PPM and PGM formats.\n"); 131 } 132 IFS(CreateOutputStream(out_file_name, use_stdout, &stream)); 133 IFS(IWICImagingFactory_CreateEncoder(factory, container_guid, NULL, 134 &encoder)); 135 IFS(IWICBitmapEncoder_Initialize(encoder, stream, 136 WICBitmapEncoderNoCache)); 137 IFS(IWICBitmapEncoder_CreateNewFrame(encoder, &frame, NULL)); 138 IFS(IWICBitmapFrameEncode_Initialize(frame, NULL)); 139 IFS(IWICBitmapFrameEncode_SetSize(frame, width, height)); 140 IFS(IWICBitmapFrameEncode_SetPixelFormat(frame, &pixel_format)); 141 IFS(IWICBitmapFrameEncode_WritePixels(frame, height, stride, 142 height * stride, rgb)); 143 IFS(IWICBitmapFrameEncode_Commit(frame)); 144 IFS(IWICBitmapEncoder_Commit(encoder)); 145 146 if (SUCCEEDED(hr) && use_stdout) { 147 HGLOBAL image; 148 IFS(GetHGlobalFromStream(stream, &image)); 149 if (SUCCEEDED(hr)) { 150 HANDLE std_output = GetStdHandle(STD_OUTPUT_HANDLE); 151 DWORD mode; 152 const BOOL update_mode = GetConsoleMode(std_output, &mode); 153 const void* const image_mem = GlobalLock(image); 154 DWORD bytes_written = 0; 155 156 // Clear output processing if necessary, then output the image. 157 if (update_mode) SetConsoleMode(std_output, 0); 158 if (!WriteFile(std_output, image_mem, (DWORD)GlobalSize(image), 159 &bytes_written, NULL) || 160 bytes_written != GlobalSize(image)) { 161 hr = E_FAIL; 162 } 163 if (update_mode) SetConsoleMode(std_output, mode); 164 GlobalUnlock(image); 165 } 166 } 167 168 if (frame != NULL) IUnknown_Release(frame); 169 if (encoder != NULL) IUnknown_Release(encoder); 170 if (factory != NULL) IUnknown_Release(factory); 171 if (stream != NULL) IUnknown_Release(stream); 172 return hr; 173 } 174 175 static int WritePNG(const char* out_file_name, int use_stdout, 176 const WebPDecBuffer* const buffer) { 177 const uint32_t width = buffer->width; 178 const uint32_t height = buffer->height; 179 uint8_t* const rgb = buffer->u.RGBA.rgba; 180 const int stride = buffer->u.RGBA.stride; 181 const int has_alpha = (buffer->colorspace == MODE_BGRA); 182 183 return SUCCEEDED(WriteUsingWIC(out_file_name, use_stdout, 184 MAKE_REFGUID(GUID_ContainerFormatPng), 185 rgb, stride, width, height, has_alpha)); 186 } 187 188 #elif defined(WEBP_HAVE_PNG) // !HAVE_WINCODEC_H 189 static void PNGAPI PNGErrorFunction(png_structp png, png_const_charp dummy) { 190 (void)dummy; // remove variable-unused warning 191 longjmp(png_jmpbuf(png), 1); 192 } 193 194 static int WritePNG(FILE* out_file, const WebPDecBuffer* const buffer) { 195 const uint32_t width = buffer->width; 196 const uint32_t height = buffer->height; 197 uint8_t* const rgb = buffer->u.RGBA.rgba; 198 const int stride = buffer->u.RGBA.stride; 199 const int has_alpha = (buffer->colorspace == MODE_RGBA); 200 png_structp png; 201 png_infop info; 202 png_uint_32 y; 203 204 png = png_create_write_struct(PNG_LIBPNG_VER_STRING, 205 NULL, PNGErrorFunction, NULL); 206 if (png == NULL) { 207 return 0; 208 } 209 info = png_create_info_struct(png); 210 if (info == NULL) { 211 png_destroy_write_struct(&png, NULL); 212 return 0; 213 } 214 if (setjmp(png_jmpbuf(png))) { 215 png_destroy_write_struct(&png, &info); 216 return 0; 217 } 218 png_init_io(png, out_file); 219 png_set_IHDR(png, info, width, height, 8, 220 has_alpha ? PNG_COLOR_TYPE_RGBA : PNG_COLOR_TYPE_RGB, 221 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, 222 PNG_FILTER_TYPE_DEFAULT); 223 png_write_info(png, info); 224 for (y = 0; y < height; ++y) { 225 png_bytep row = rgb + y * stride; 226 png_write_rows(png, &row, 1); 227 } 228 png_write_end(png, info); 229 png_destroy_write_struct(&png, &info); 230 return 1; 231 } 232 #else // !HAVE_WINCODEC_H && !WEBP_HAVE_PNG 233 static int WritePNG(FILE* out_file, const WebPDecBuffer* const buffer) { 234 (void)out_file; 235 (void)buffer; 236 fprintf(stderr, "PNG support not compiled. Please install the libpng " 237 "development package before building.\n"); 238 fprintf(stderr, "You can run with -ppm flag to decode in PPM format.\n"); 239 return 0; 240 } 241 #endif 242 243 static int WritePPM(FILE* fout, const WebPDecBuffer* const buffer, int alpha) { 244 const uint32_t width = buffer->width; 245 const uint32_t height = buffer->height; 246 const uint8_t* const rgb = buffer->u.RGBA.rgba; 247 const int stride = buffer->u.RGBA.stride; 248 const size_t bytes_per_px = alpha ? 4 : 3; 249 uint32_t y; 250 251 if (alpha) { 252 fprintf(fout, "P7\nWIDTH %d\nHEIGHT %d\nDEPTH 4\nMAXVAL 255\n" 253 "TUPLTYPE RGB_ALPHA\nENDHDR\n", width, height); 254 } else { 255 fprintf(fout, "P6\n%d %d\n255\n", width, height); 256 } 257 for (y = 0; y < height; ++y) { 258 if (fwrite(rgb + y * stride, width, bytes_per_px, fout) != bytes_per_px) { 259 return 0; 260 } 261 } 262 return 1; 263 } 264 265 static void PutLE16(uint8_t* const dst, uint32_t value) { 266 dst[0] = (value >> 0) & 0xff; 267 dst[1] = (value >> 8) & 0xff; 268 } 269 270 static void PutLE32(uint8_t* const dst, uint32_t value) { 271 PutLE16(dst + 0, (value >> 0) & 0xffff); 272 PutLE16(dst + 2, (value >> 16) & 0xffff); 273 } 274 275 #define BMP_HEADER_SIZE 54 276 static int WriteBMP(FILE* fout, const WebPDecBuffer* const buffer) { 277 const int has_alpha = (buffer->colorspace != MODE_BGR); 278 const uint32_t width = buffer->width; 279 const uint32_t height = buffer->height; 280 const uint8_t* const rgba = buffer->u.RGBA.rgba; 281 const int stride = buffer->u.RGBA.stride; 282 const uint32_t bytes_per_px = has_alpha ? 4 : 3; 283 uint32_t y; 284 const uint32_t line_size = bytes_per_px * width; 285 const uint32_t bmp_stride = (line_size + 3) & ~3; // pad to 4 286 const uint32_t total_size = bmp_stride * height + BMP_HEADER_SIZE; 287 uint8_t bmp_header[BMP_HEADER_SIZE] = { 0 }; 288 289 // bitmap file header 290 PutLE16(bmp_header + 0, 0x4d42); // signature 'BM' 291 PutLE32(bmp_header + 2, total_size); // size including header 292 PutLE32(bmp_header + 6, 0); // reserved 293 PutLE32(bmp_header + 10, BMP_HEADER_SIZE); // offset to pixel array 294 // bitmap info header 295 PutLE32(bmp_header + 14, 40); // DIB header size 296 PutLE32(bmp_header + 18, width); // dimensions 297 PutLE32(bmp_header + 22, -(int)height); // vertical flip! 298 PutLE16(bmp_header + 26, 1); // number of planes 299 PutLE16(bmp_header + 28, bytes_per_px * 8); // bits per pixel 300 PutLE32(bmp_header + 30, 0); // no compression (BI_RGB) 301 PutLE32(bmp_header + 34, 0); // image size (dummy) 302 PutLE32(bmp_header + 38, 2400); // x pixels/meter 303 PutLE32(bmp_header + 42, 2400); // y pixels/meter 304 PutLE32(bmp_header + 46, 0); // number of palette colors 305 PutLE32(bmp_header + 50, 0); // important color count 306 307 // TODO(skal): color profile 308 309 // write header 310 if (fwrite(bmp_header, sizeof(bmp_header), 1, fout) != 1) { 311 return 0; 312 } 313 314 // write pixel array 315 for (y = 0; y < height; ++y) { 316 if (fwrite(rgba + y * stride, line_size, 1, fout) != 1) { 317 return 0; 318 } 319 // write padding zeroes 320 if (bmp_stride != line_size) { 321 const uint8_t zeroes[3] = { 0 }; 322 if (fwrite(zeroes, bmp_stride - line_size, 1, fout) != 1) { 323 return 0; 324 } 325 } 326 } 327 return 1; 328 } 329 #undef BMP_HEADER_SIZE 330 331 #define NUM_IFD_ENTRIES 15 332 #define EXTRA_DATA_SIZE 16 333 // 10b for signature/header + n * 12b entries + 4b for IFD terminator: 334 #define EXTRA_DATA_OFFSET (10 + 12 * NUM_IFD_ENTRIES + 4) 335 #define TIFF_HEADER_SIZE (EXTRA_DATA_OFFSET + EXTRA_DATA_SIZE) 336 337 static int WriteTIFF(FILE* fout, const WebPDecBuffer* const buffer) { 338 const int has_alpha = (buffer->colorspace != MODE_RGB); 339 const uint32_t width = buffer->width; 340 const uint32_t height = buffer->height; 341 const uint8_t* const rgba = buffer->u.RGBA.rgba; 342 const int stride = buffer->u.RGBA.stride; 343 const uint8_t bytes_per_px = has_alpha ? 4 : 3; 344 // For non-alpha case, we omit tag 0x152 (ExtraSamples). 345 const uint8_t num_ifd_entries = has_alpha ? NUM_IFD_ENTRIES 346 : NUM_IFD_ENTRIES - 1; 347 uint8_t tiff_header[TIFF_HEADER_SIZE] = { 348 0x49, 0x49, 0x2a, 0x00, // little endian signature 349 8, 0, 0, 0, // offset to the unique IFD that follows 350 // IFD (offset = 8). Entries must be written in increasing tag order. 351 num_ifd_entries, 0, // Number of entries in the IFD (12 bytes each). 352 0x00, 0x01, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, // 10: Width (TBD) 353 0x01, 0x01, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, // 22: Height (TBD) 354 0x02, 0x01, 3, 0, bytes_per_px, 0, 0, 0, // 34: BitsPerSample: 8888 355 EXTRA_DATA_OFFSET + 0, 0, 0, 0, 356 0x03, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0, // 46: Compression: none 357 0x06, 0x01, 3, 0, 1, 0, 0, 0, 2, 0, 0, 0, // 58: Photometric: RGB 358 0x11, 0x01, 4, 0, 1, 0, 0, 0, // 70: Strips offset: 359 TIFF_HEADER_SIZE, 0, 0, 0, // data follows header 360 0x12, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0, // 82: Orientation: topleft 361 0x15, 0x01, 3, 0, 1, 0, 0, 0, // 94: SamplesPerPixels 362 bytes_per_px, 0, 0, 0, 363 0x16, 0x01, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, // 106: Rows per strip (TBD) 364 0x17, 0x01, 4, 0, 1, 0, 0, 0, 0, 0, 0, 0, // 118: StripByteCount (TBD) 365 0x1a, 0x01, 5, 0, 1, 0, 0, 0, // 130: X-resolution 366 EXTRA_DATA_OFFSET + 8, 0, 0, 0, 367 0x1b, 0x01, 5, 0, 1, 0, 0, 0, // 142: Y-resolution 368 EXTRA_DATA_OFFSET + 8, 0, 0, 0, 369 0x1c, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0, // 154: PlanarConfiguration 370 0x28, 0x01, 3, 0, 1, 0, 0, 0, 2, 0, 0, 0, // 166: ResolutionUnit (inch) 371 0x52, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0, // 178: ExtraSamples: rgbA 372 0, 0, 0, 0, // 190: IFD terminator 373 // EXTRA_DATA_OFFSET: 374 8, 0, 8, 0, 8, 0, 8, 0, // BitsPerSample 375 72, 0, 0, 0, 1, 0, 0, 0 // 72 pixels/inch, for X/Y-resolution 376 }; 377 uint32_t y; 378 379 // Fill placeholders in IFD: 380 PutLE32(tiff_header + 10 + 8, width); 381 PutLE32(tiff_header + 22 + 8, height); 382 PutLE32(tiff_header + 106 + 8, height); 383 PutLE32(tiff_header + 118 + 8, width * bytes_per_px * height); 384 if (!has_alpha) PutLE32(tiff_header + 178, 0); // IFD terminator 385 386 // write header 387 if (fwrite(tiff_header, sizeof(tiff_header), 1, fout) != 1) { 388 return 0; 389 } 390 // write pixel values 391 for (y = 0; y < height; ++y) { 392 if (fwrite(rgba + y * stride, bytes_per_px, width, fout) != width) { 393 return 0; 394 } 395 } 396 397 return 1; 398 } 399 400 #undef TIFF_HEADER_SIZE 401 #undef EXTRA_DATA_OFFSET 402 #undef EXTRA_DATA_SIZE 403 #undef NUM_IFD_ENTRIES 404 405 static int WriteAlphaPlane(FILE* fout, const WebPDecBuffer* const buffer) { 406 const uint32_t width = buffer->width; 407 const uint32_t height = buffer->height; 408 const uint8_t* const a = buffer->u.YUVA.a; 409 const int a_stride = buffer->u.YUVA.a_stride; 410 uint32_t y; 411 assert(a != NULL); 412 fprintf(fout, "P5\n%d %d\n255\n", width, height); 413 for (y = 0; y < height; ++y) { 414 if (fwrite(a + y * a_stride, width, 1, fout) != 1) { 415 return 0; 416 } 417 } 418 return 1; 419 } 420 421 // format=PGM: save a grayscale PGM file using the IMC4 layout 422 // (http://www.fourcc.org/yuv.php#IMC4). This is a very convenient format for 423 // viewing the samples, esp. for odd dimensions. 424 // format=YUV: just save the Y/U/V/A planes sequentially without header. 425 static int WritePGMOrYUV(FILE* fout, const WebPDecBuffer* const buffer, 426 OutputFileFormat format) { 427 const int width = buffer->width; 428 const int height = buffer->height; 429 const WebPYUVABuffer* const yuv = &buffer->u.YUVA; 430 int ok = 1; 431 int y; 432 const int pad = (format == YUV) ? 0 : 1; 433 const int uv_width = (width + 1) / 2; 434 const int uv_height = (height + 1) / 2; 435 const int out_stride = (width + pad) & ~pad; 436 const int a_height = yuv->a ? height : 0; 437 if (format == PGM) { 438 fprintf(fout, "P5\n%d %d\n255\n", 439 out_stride, height + uv_height + a_height); 440 } 441 for (y = 0; ok && y < height; ++y) { 442 ok &= (fwrite(yuv->y + y * yuv->y_stride, width, 1, fout) == 1); 443 if (format == PGM) { 444 if (width & 1) fputc(0, fout); // padding byte 445 } 446 } 447 if (format == PGM) { // IMC4 layout 448 for (y = 0; ok && y < uv_height; ++y) { 449 ok &= (fwrite(yuv->u + y * yuv->u_stride, uv_width, 1, fout) == 1); 450 ok &= (fwrite(yuv->v + y * yuv->v_stride, uv_width, 1, fout) == 1); 451 } 452 } else { 453 for (y = 0; ok && y < uv_height; ++y) { 454 ok &= (fwrite(yuv->u + y * yuv->u_stride, uv_width, 1, fout) == 1); 455 } 456 for (y = 0; ok && y < uv_height; ++y) { 457 ok &= (fwrite(yuv->v + y * yuv->v_stride, uv_width, 1, fout) == 1); 458 } 459 } 460 for (y = 0; ok && y < a_height; ++y) { 461 ok &= (fwrite(yuv->a + y * yuv->a_stride, width, 1, fout) == 1); 462 if (format == PGM) { 463 if (width & 1) fputc(0, fout); // padding byte 464 } 465 } 466 return ok; 467 } 468 469 static int SaveOutput(const WebPDecBuffer* const buffer, 470 OutputFileFormat format, const char* const out_file) { 471 FILE* fout = NULL; 472 int needs_open_file = 1; 473 const int use_stdout = !strcmp(out_file, "-"); 474 int ok = 1; 475 Stopwatch stop_watch; 476 477 if (verbose) { 478 StopwatchReset(&stop_watch); 479 } 480 481 #ifdef HAVE_WINCODEC_H 482 needs_open_file = (format != PNG); 483 #endif 484 485 #if defined(_WIN32) 486 if (use_stdout && _setmode(_fileno(stdout), _O_BINARY) == -1) { 487 fprintf(stderr, "Failed to reopen stdout in O_BINARY mode.\n"); 488 return -1; 489 } 490 #endif 491 492 if (needs_open_file) { 493 fout = use_stdout ? stdout : fopen(out_file, "wb"); 494 if (fout == NULL) { 495 fprintf(stderr, "Error opening output file %s\n", out_file); 496 return 0; 497 } 498 } 499 500 if (format == PNG) { 501 #ifdef HAVE_WINCODEC_H 502 ok &= WritePNG(out_file, use_stdout, buffer); 503 #else 504 ok &= WritePNG(fout, buffer); 505 #endif 506 } else if (format == PAM) { 507 ok &= WritePPM(fout, buffer, 1); 508 } else if (format == PPM) { 509 ok &= WritePPM(fout, buffer, 0); 510 } else if (format == BMP) { 511 ok &= WriteBMP(fout, buffer); 512 } else if (format == TIFF) { 513 ok &= WriteTIFF(fout, buffer); 514 } else if (format == PGM || format == YUV) { 515 ok &= WritePGMOrYUV(fout, buffer, format); 516 } else if (format == ALPHA_PLANE_ONLY) { 517 ok &= WriteAlphaPlane(fout, buffer); 518 } 519 if (fout != NULL && fout != stdout) { 520 fclose(fout); 521 } 522 if (ok) { 523 if (use_stdout) { 524 fprintf(stderr, "Saved to stdout\n"); 525 } else { 526 fprintf(stderr, "Saved file %s\n", out_file); 527 } 528 if (verbose) { 529 const double write_time = StopwatchReadAndReset(&stop_watch); 530 fprintf(stderr, "Time to write output: %.3fs\n", write_time); 531 } 532 } else { 533 if (use_stdout) { 534 fprintf(stderr, "Error writing to stdout !!\n"); 535 } else { 536 fprintf(stderr, "Error writing file %s !!\n", out_file); 537 } 538 } 539 return ok; 540 } 541 542 static void Help(void) { 543 printf("Usage: dwebp in_file [options] [-o out_file]\n\n" 544 "Decodes the WebP image file to PNG format [Default]\n" 545 "Use following options to convert into alternate image formats:\n" 546 " -pam ......... save the raw RGBA samples as a color PAM\n" 547 " -ppm ......... save the raw RGB samples as a color PPM\n" 548 " -bmp ......... save as uncompressed BMP format\n" 549 " -tiff ........ save as uncompressed TIFF format\n" 550 " -pgm ......... save the raw YUV samples as a grayscale PGM\n" 551 " file with IMC4 layout\n" 552 " -yuv ......... save the raw YUV samples in flat layout\n" 553 "\n" 554 " Other options are:\n" 555 " -version .... print version number and exit.\n" 556 " -nofancy ..... don't use the fancy YUV420 upscaler.\n" 557 " -nofilter .... disable in-loop filtering.\n" 558 " -nodither .... disable dithering.\n" 559 " -dither <d> .. dithering strength (in 0..100)\n" 560 " -mt .......... use multi-threading\n" 561 " -crop <x> <y> <w> <h> ... crop output with the given rectangle\n" 562 " -scale <w> <h> .......... scale the output (*after* any cropping)\n" 563 " -alpha ....... only save the alpha plane.\n" 564 " -incremental . use incremental decoding (useful for tests)\n" 565 " -h ....... this help message.\n" 566 " -v ....... verbose (e.g. print encoding/decoding times)\n" 567 #ifndef WEBP_DLL 568 " -noasm ....... disable all assembly optimizations.\n" 569 #endif 570 ); 571 } 572 573 static const char* const kStatusMessages[] = { 574 "OK", "OUT_OF_MEMORY", "INVALID_PARAM", "BITSTREAM_ERROR", 575 "UNSUPPORTED_FEATURE", "SUSPENDED", "USER_ABORT", "NOT_ENOUGH_DATA" 576 }; 577 578 static const char* const kFormatType[] = { 579 "unspecified", "lossy", "lossless" 580 }; 581 582 int main(int argc, const char *argv[]) { 583 int ok = 0; 584 const char *in_file = NULL; 585 const char *out_file = NULL; 586 587 WebPDecoderConfig config; 588 WebPDecBuffer* const output_buffer = &config.output; 589 WebPBitstreamFeatures* const bitstream = &config.input; 590 OutputFileFormat format = PNG; 591 int incremental = 0; 592 int c; 593 594 if (!WebPInitDecoderConfig(&config)) { 595 fprintf(stderr, "Library version mismatch!\n"); 596 return -1; 597 } 598 599 for (c = 1; c < argc; ++c) { 600 if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) { 601 Help(); 602 return 0; 603 } else if (!strcmp(argv[c], "-o") && c < argc - 1) { 604 out_file = argv[++c]; 605 } else if (!strcmp(argv[c], "-alpha")) { 606 format = ALPHA_PLANE_ONLY; 607 } else if (!strcmp(argv[c], "-nofancy")) { 608 config.options.no_fancy_upsampling = 1; 609 } else if (!strcmp(argv[c], "-nofilter")) { 610 config.options.bypass_filtering = 1; 611 } else if (!strcmp(argv[c], "-pam")) { 612 format = PAM; 613 } else if (!strcmp(argv[c], "-ppm")) { 614 format = PPM; 615 } else if (!strcmp(argv[c], "-bmp")) { 616 format = BMP; 617 } else if (!strcmp(argv[c], "-tiff")) { 618 format = TIFF; 619 } else if (!strcmp(argv[c], "-version")) { 620 const int version = WebPGetDecoderVersion(); 621 printf("%d.%d.%d\n", 622 (version >> 16) & 0xff, (version >> 8) & 0xff, version & 0xff); 623 return 0; 624 } else if (!strcmp(argv[c], "-pgm")) { 625 format = PGM; 626 } else if (!strcmp(argv[c], "-yuv")) { 627 format = YUV; 628 } else if (!strcmp(argv[c], "-mt")) { 629 config.options.use_threads = 1; 630 } else if (!strcmp(argv[c], "-nodither")) { 631 config.options.dithering_strength = 0; 632 } else if (!strcmp(argv[c], "-dither") && c < argc - 1) { 633 config.options.dithering_strength = strtol(argv[++c], NULL, 0); 634 } else if (!strcmp(argv[c], "-crop") && c < argc - 4) { 635 config.options.use_cropping = 1; 636 config.options.crop_left = strtol(argv[++c], NULL, 0); 637 config.options.crop_top = strtol(argv[++c], NULL, 0); 638 config.options.crop_width = strtol(argv[++c], NULL, 0); 639 config.options.crop_height = strtol(argv[++c], NULL, 0); 640 } else if (!strcmp(argv[c], "-scale") && c < argc - 2) { 641 config.options.use_scaling = 1; 642 config.options.scaled_width = strtol(argv[++c], NULL, 0); 643 config.options.scaled_height = strtol(argv[++c], NULL, 0); 644 } else if (!strcmp(argv[c], "-v")) { 645 verbose = 1; 646 #ifndef WEBP_DLL 647 } else if (!strcmp(argv[c], "-noasm")) { 648 VP8GetCPUInfo = NULL; 649 #endif 650 } else if (!strcmp(argv[c], "-incremental")) { 651 incremental = 1; 652 } else if (!strcmp(argv[c], "--")) { 653 if (c < argc - 1) in_file = argv[++c]; 654 break; 655 } else if (argv[c][0] == '-') { 656 fprintf(stderr, "Unknown option '%s'\n", argv[c]); 657 Help(); 658 return -1; 659 } else { 660 in_file = argv[c]; 661 } 662 } 663 664 if (in_file == NULL) { 665 fprintf(stderr, "missing input file!!\n"); 666 Help(); 667 return -1; 668 } 669 670 { 671 Stopwatch stop_watch; 672 VP8StatusCode status = VP8_STATUS_OK; 673 size_t data_size = 0; 674 const uint8_t* data = NULL; 675 676 if (!ExUtilReadFile(in_file, &data, &data_size)) return -1; 677 678 if (verbose) { 679 StopwatchReset(&stop_watch); 680 } 681 682 status = WebPGetFeatures(data, data_size, bitstream); 683 if (status != VP8_STATUS_OK) { 684 goto end; 685 } 686 687 if (bitstream->has_animation) { 688 fprintf(stderr, 689 "Error! Decoding of an animated WebP file is not supported.\n" 690 " Use webpmux to extract the individual frames or\n" 691 " vwebp to view this image.\n"); 692 } 693 694 switch (format) { 695 case PNG: 696 #ifdef HAVE_WINCODEC_H 697 output_buffer->colorspace = bitstream->has_alpha ? MODE_BGRA : MODE_BGR; 698 #else 699 output_buffer->colorspace = bitstream->has_alpha ? MODE_RGBA : MODE_RGB; 700 #endif 701 break; 702 case PAM: 703 output_buffer->colorspace = MODE_RGBA; 704 break; 705 case PPM: 706 output_buffer->colorspace = MODE_RGB; // drops alpha for PPM 707 break; 708 case BMP: 709 output_buffer->colorspace = bitstream->has_alpha ? MODE_BGRA : MODE_BGR; 710 break; 711 case TIFF: // note: force pre-multiplied alpha 712 output_buffer->colorspace = 713 bitstream->has_alpha ? MODE_rgbA : MODE_RGB; 714 break; 715 case PGM: 716 case YUV: 717 output_buffer->colorspace = bitstream->has_alpha ? MODE_YUVA : MODE_YUV; 718 break; 719 case ALPHA_PLANE_ONLY: 720 output_buffer->colorspace = MODE_YUVA; 721 break; 722 default: 723 free((void*)data); 724 return -1; 725 } 726 727 // Decoding call. 728 if (!incremental) { 729 status = WebPDecode(data, data_size, &config); 730 } else { 731 WebPIDecoder* const idec = WebPIDecode(data, data_size, &config); 732 if (idec == NULL) { 733 fprintf(stderr, "Failed during WebPINewDecoder().\n"); 734 status = VP8_STATUS_OUT_OF_MEMORY; 735 goto end; 736 } else { 737 status = WebPIUpdate(idec, data, data_size); 738 WebPIDelete(idec); 739 } 740 } 741 742 if (verbose) { 743 const double decode_time = StopwatchReadAndReset(&stop_watch); 744 fprintf(stderr, "Time to decode picture: %.3fs\n", decode_time); 745 } 746 end: 747 free((void*)data); 748 ok = (status == VP8_STATUS_OK); 749 if (!ok) { 750 fprintf(stderr, "Decoding of %s failed.\n", in_file); 751 fprintf(stderr, "Status: %d (%s)\n", status, kStatusMessages[status]); 752 goto Exit; 753 } 754 } 755 756 if (out_file != NULL) { 757 fprintf(stderr, "Decoded %s. Dimensions: %d x %d %s. Format: %s. " 758 "Now saving...\n", 759 in_file, output_buffer->width, output_buffer->height, 760 bitstream->has_alpha ? " (with alpha)" : "", 761 kFormatType[bitstream->format]); 762 ok = SaveOutput(output_buffer, format, out_file); 763 } else { 764 fprintf(stderr, "File %s can be decoded " 765 "(dimensions: %d x %d %s. Format: %s).\n", 766 in_file, output_buffer->width, output_buffer->height, 767 bitstream->has_alpha ? " (with alpha)" : "", 768 kFormatType[bitstream->format]); 769 fprintf(stderr, "Nothing written; " 770 "use -o flag to save the result as e.g. PNG.\n"); 771 } 772 Exit: 773 WebPFreeDecBuffer(output_buffer); 774 return ok ? 0 : -1; 775 } 776 777 //------------------------------------------------------------------------------