Vikas Arora | 0406ce1 | 2013-08-09 15:57:12 -0700 | [diff] [blame] | 1 | // Copyright 2010 Google Inc. All Rights Reserved. |
Eric Hassold | 9aea642 | 2011-01-04 17:22:46 -0800 | [diff] [blame] | 2 | // |
Vikas Arora | 0406ce1 | 2013-08-09 15:57:12 -0700 | [diff] [blame] | 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. |
Eric Hassold | 9aea642 | 2011-01-04 17:22:46 -0800 | [diff] [blame] | 8 | // ----------------------------------------------------------------------------- |
| 9 | // |
Vikas Arora | 0406ce1 | 2013-08-09 15:57:12 -0700 | [diff] [blame] | 10 | // Command-line tool for decoding a WebP image. |
Eric Hassold | 9aea642 | 2011-01-04 17:22:46 -0800 | [diff] [blame] | 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 | |
Vikas Arora | 0406ce1 | 2013-08-09 15:57:12 -0700 | [diff] [blame] | 19 | #ifdef HAVE_CONFIG_H |
| 20 | #include "config.h" |
| 21 | #endif |
Eric Hassold | 9aea642 | 2011-01-04 17:22:46 -0800 | [diff] [blame] | 22 | |
Vikas Arora | 0406ce1 | 2013-08-09 15:57:12 -0700 | [diff] [blame] | 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 <shlwapi.h> |
| 36 | #include <windows.h> |
| 37 | #include <wincodec.h> |
| 38 | #endif |
| 39 | |
| 40 | #include "webp/decode.h" |
| 41 | #include "./example_util.h" |
| 42 | #include "./stopwatch.h" |
| 43 | |
| 44 | static int verbose = 0; |
| 45 | #ifndef WEBP_DLL |
Eric Hassold | 9aea642 | 2011-01-04 17:22:46 -0800 | [diff] [blame] | 46 | #if defined(__cplusplus) || defined(c_plusplus) |
| 47 | extern "C" { |
| 48 | #endif |
| 49 | |
Vikas Arora | 0406ce1 | 2013-08-09 15:57:12 -0700 | [diff] [blame] | 50 | extern void* VP8GetCPUInfo; // opaque forward declaration. |
Eric Hassold | 9aea642 | 2011-01-04 17:22:46 -0800 | [diff] [blame] | 51 | |
Vikas Arora | 0406ce1 | 2013-08-09 15:57:12 -0700 | [diff] [blame] | 52 | #if defined(__cplusplus) || defined(c_plusplus) |
| 53 | } // extern "C" |
| 54 | #endif |
| 55 | #endif // WEBP_DLL |
| 56 | |
| 57 | //------------------------------------------------------------------------------ |
| 58 | |
| 59 | // Output types |
| 60 | typedef enum { |
| 61 | PNG = 0, |
| 62 | PAM, |
| 63 | PPM, |
| 64 | PGM, |
| 65 | YUV, |
| 66 | ALPHA_PLANE_ONLY // this is for experimenting only |
| 67 | } OutputFileFormat; |
| 68 | |
| 69 | #ifdef HAVE_WINCODEC_H |
| 70 | |
| 71 | #define IFS(fn) \ |
| 72 | do { \ |
| 73 | if (SUCCEEDED(hr)) { \ |
| 74 | hr = (fn); \ |
| 75 | if (FAILED(hr)) fprintf(stderr, #fn " failed %08lx\n", hr); \ |
| 76 | } \ |
| 77 | } while (0) |
| 78 | |
| 79 | #ifdef __cplusplus |
| 80 | #define MAKE_REFGUID(x) (x) |
| 81 | #else |
| 82 | #define MAKE_REFGUID(x) &(x) |
| 83 | #endif |
| 84 | |
| 85 | static HRESULT CreateOutputStream(const char* out_file_name, IStream** stream) { |
| 86 | HRESULT hr = S_OK; |
| 87 | IFS(SHCreateStreamOnFileA(out_file_name, STGM_WRITE | STGM_CREATE, stream)); |
| 88 | if (FAILED(hr)) { |
| 89 | fprintf(stderr, "Error opening output file %s (%08lx)\n", |
| 90 | out_file_name, hr); |
| 91 | } |
| 92 | return hr; |
| 93 | } |
| 94 | |
| 95 | static HRESULT WriteUsingWIC(const char* out_file_name, REFGUID container_guid, |
| 96 | unsigned char* rgb, int stride, |
| 97 | uint32_t width, uint32_t height, int has_alpha) { |
| 98 | HRESULT hr = S_OK; |
| 99 | IWICImagingFactory* factory = NULL; |
| 100 | IWICBitmapFrameEncode* frame = NULL; |
| 101 | IWICBitmapEncoder* encoder = NULL; |
| 102 | IStream* stream = NULL; |
| 103 | WICPixelFormatGUID pixel_format = has_alpha ? GUID_WICPixelFormat32bppBGRA |
| 104 | : GUID_WICPixelFormat24bppBGR; |
| 105 | |
| 106 | IFS(CoInitialize(NULL)); |
| 107 | IFS(CoCreateInstance(MAKE_REFGUID(CLSID_WICImagingFactory), NULL, |
| 108 | CLSCTX_INPROC_SERVER, |
| 109 | MAKE_REFGUID(IID_IWICImagingFactory), |
| 110 | (LPVOID*)&factory)); |
| 111 | if (hr == REGDB_E_CLASSNOTREG) { |
| 112 | fprintf(stderr, |
| 113 | "Couldn't access Windows Imaging Component (are you running " |
| 114 | "Windows XP SP3 or newer?). PNG support not available. " |
| 115 | "Use -ppm or -pgm for available PPM and PGM formats.\n"); |
| 116 | } |
| 117 | IFS(CreateOutputStream(out_file_name, &stream)); |
| 118 | IFS(IWICImagingFactory_CreateEncoder(factory, container_guid, NULL, |
| 119 | &encoder)); |
| 120 | IFS(IWICBitmapEncoder_Initialize(encoder, stream, |
| 121 | WICBitmapEncoderNoCache)); |
| 122 | IFS(IWICBitmapEncoder_CreateNewFrame(encoder, &frame, NULL)); |
| 123 | IFS(IWICBitmapFrameEncode_Initialize(frame, NULL)); |
| 124 | IFS(IWICBitmapFrameEncode_SetSize(frame, width, height)); |
| 125 | IFS(IWICBitmapFrameEncode_SetPixelFormat(frame, &pixel_format)); |
| 126 | IFS(IWICBitmapFrameEncode_WritePixels(frame, height, stride, |
| 127 | height * stride, rgb)); |
| 128 | IFS(IWICBitmapFrameEncode_Commit(frame)); |
| 129 | IFS(IWICBitmapEncoder_Commit(encoder)); |
| 130 | |
| 131 | if (frame != NULL) IUnknown_Release(frame); |
| 132 | if (encoder != NULL) IUnknown_Release(encoder); |
| 133 | if (factory != NULL) IUnknown_Release(factory); |
| 134 | if (stream != NULL) IUnknown_Release(stream); |
| 135 | return hr; |
| 136 | } |
| 137 | |
| 138 | static int WritePNG(const char* out_file_name, |
| 139 | const WebPDecBuffer* const buffer) { |
| 140 | const uint32_t width = buffer->width; |
| 141 | const uint32_t height = buffer->height; |
| 142 | unsigned char* const rgb = buffer->u.RGBA.rgba; |
| 143 | const int stride = buffer->u.RGBA.stride; |
| 144 | const int has_alpha = (buffer->colorspace == MODE_BGRA); |
| 145 | |
| 146 | return SUCCEEDED(WriteUsingWIC(out_file_name, |
| 147 | MAKE_REFGUID(GUID_ContainerFormatPng), |
| 148 | rgb, stride, width, height, has_alpha)); |
| 149 | } |
| 150 | |
| 151 | #elif defined(WEBP_HAVE_PNG) // !HAVE_WINCODEC_H |
| 152 | static void PNGAPI error_function(png_structp png, png_const_charp dummy) { |
| 153 | (void)dummy; // remove variable-unused warning |
| 154 | longjmp(png_jmpbuf(png), 1); |
| 155 | } |
| 156 | |
| 157 | static int WritePNG(FILE* out_file, const WebPDecBuffer* const buffer) { |
| 158 | const uint32_t width = buffer->width; |
| 159 | const uint32_t height = buffer->height; |
| 160 | unsigned char* const rgb = buffer->u.RGBA.rgba; |
| 161 | const int stride = buffer->u.RGBA.stride; |
| 162 | const int has_alpha = (buffer->colorspace == MODE_RGBA); |
| 163 | png_structp png; |
| 164 | png_infop info; |
| 165 | png_uint_32 y; |
| 166 | |
| 167 | png = png_create_write_struct(PNG_LIBPNG_VER_STRING, |
| 168 | NULL, error_function, NULL); |
| 169 | if (png == NULL) { |
| 170 | return 0; |
| 171 | } |
| 172 | info = png_create_info_struct(png); |
| 173 | if (info == NULL) { |
| 174 | png_destroy_write_struct(&png, NULL); |
| 175 | return 0; |
| 176 | } |
| 177 | if (setjmp(png_jmpbuf(png))) { |
| 178 | png_destroy_write_struct(&png, &info); |
| 179 | return 0; |
| 180 | } |
| 181 | png_init_io(png, out_file); |
| 182 | png_set_IHDR(png, info, width, height, 8, |
| 183 | has_alpha ? PNG_COLOR_TYPE_RGBA : PNG_COLOR_TYPE_RGB, |
| 184 | PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, |
| 185 | PNG_FILTER_TYPE_DEFAULT); |
| 186 | png_write_info(png, info); |
| 187 | for (y = 0; y < height; ++y) { |
| 188 | png_bytep row = rgb + y * stride; |
| 189 | png_write_rows(png, &row, 1); |
| 190 | } |
| 191 | png_write_end(png, info); |
| 192 | png_destroy_write_struct(&png, &info); |
| 193 | return 1; |
| 194 | } |
| 195 | #else // !HAVE_WINCODEC_H && !WEBP_HAVE_PNG |
| 196 | static int WritePNG(FILE* out_file, const WebPDecBuffer* const buffer) { |
| 197 | (void)out_file; |
| 198 | (void)buffer; |
| 199 | fprintf(stderr, "PNG support not compiled. Please install the libpng " |
| 200 | "development package before building.\n"); |
| 201 | fprintf(stderr, "You can run with -ppm flag to decode in PPM format.\n"); |
| 202 | return 0; |
| 203 | } |
| 204 | #endif |
| 205 | |
| 206 | static int WritePPM(FILE* fout, const WebPDecBuffer* const buffer, int alpha) { |
| 207 | const uint32_t width = buffer->width; |
| 208 | const uint32_t height = buffer->height; |
| 209 | const unsigned char* const rgb = buffer->u.RGBA.rgba; |
| 210 | const int stride = buffer->u.RGBA.stride; |
| 211 | const size_t bytes_per_px = alpha ? 4 : 3; |
| 212 | uint32_t y; |
| 213 | |
| 214 | if (alpha) { |
| 215 | fprintf(fout, "P7\nWIDTH %d\nHEIGHT %d\nDEPTH 4\nMAXVAL 255\n" |
| 216 | "TUPLTYPE RGB_ALPHA\nENDHDR\n", width, height); |
| 217 | } else { |
| 218 | fprintf(fout, "P6\n%d %d\n255\n", width, height); |
| 219 | } |
| 220 | for (y = 0; y < height; ++y) { |
| 221 | if (fwrite(rgb + y * stride, width, bytes_per_px, fout) != bytes_per_px) { |
| 222 | return 0; |
| 223 | } |
| 224 | } |
| 225 | return 1; |
| 226 | } |
| 227 | |
| 228 | static int WriteAlphaPlane(FILE* fout, const WebPDecBuffer* const buffer) { |
| 229 | const uint32_t width = buffer->width; |
| 230 | const uint32_t height = buffer->height; |
| 231 | const unsigned char* const a = buffer->u.YUVA.a; |
| 232 | const int a_stride = buffer->u.YUVA.a_stride; |
| 233 | uint32_t y; |
| 234 | assert(a != NULL); |
| 235 | fprintf(fout, "P5\n%d %d\n255\n", width, height); |
| 236 | for (y = 0; y < height; ++y) { |
| 237 | if (fwrite(a + y * a_stride, width, 1, fout) != 1) { |
| 238 | return 0; |
| 239 | } |
| 240 | } |
| 241 | return 1; |
| 242 | } |
| 243 | |
| 244 | // format=PGM: save a grayscale PGM file using the IMC4 layout |
| 245 | // (http://www.fourcc.org/yuv.php#IMC4). This is a very convenient format for |
| 246 | // viewing the samples, esp. for odd dimensions. |
| 247 | // format=YUV: just save the Y/U/V/A planes sequentially without header. |
| 248 | static int WritePGMOrYUV(FILE* fout, const WebPDecBuffer* const buffer, |
| 249 | OutputFileFormat format) { |
| 250 | const int width = buffer->width; |
| 251 | const int height = buffer->height; |
| 252 | const WebPYUVABuffer* const yuv = &buffer->u.YUVA; |
| 253 | int ok = 1; |
| 254 | int y; |
| 255 | const int pad = (format == YUV) ? 0 : 1; |
| 256 | const int uv_width = (width + 1) / 2; |
| 257 | const int uv_height = (height + 1) / 2; |
| 258 | const int out_stride = (width + pad) & ~pad; |
| 259 | const int a_height = yuv->a ? height : 0; |
| 260 | if (format == PGM) { |
| 261 | fprintf(fout, "P5\n%d %d\n255\n", |
| 262 | out_stride, height + uv_height + a_height); |
| 263 | } |
| 264 | for (y = 0; ok && y < height; ++y) { |
| 265 | ok &= (fwrite(yuv->y + y * yuv->y_stride, width, 1, fout) == 1); |
| 266 | if (format == PGM) { |
| 267 | if (width & 1) fputc(0, fout); // padding byte |
| 268 | } |
| 269 | } |
| 270 | if (format == PGM) { // IMC4 layout |
| 271 | for (y = 0; ok && y < uv_height; ++y) { |
| 272 | ok &= (fwrite(yuv->u + y * yuv->u_stride, uv_width, 1, fout) == 1); |
| 273 | ok &= (fwrite(yuv->v + y * yuv->v_stride, uv_width, 1, fout) == 1); |
| 274 | } |
| 275 | } else { |
| 276 | for (y = 0; ok && y < uv_height; ++y) { |
| 277 | ok &= (fwrite(yuv->u + y * yuv->u_stride, uv_width, 1, fout) == 1); |
| 278 | } |
| 279 | for (y = 0; ok && y < uv_height; ++y) { |
| 280 | ok &= (fwrite(yuv->v + y * yuv->v_stride, uv_width, 1, fout) == 1); |
| 281 | } |
| 282 | } |
| 283 | for (y = 0; ok && y < a_height; ++y) { |
| 284 | ok &= (fwrite(yuv->a + y * yuv->a_stride, width, 1, fout) == 1); |
| 285 | if (format == PGM) { |
| 286 | if (width & 1) fputc(0, fout); // padding byte |
| 287 | } |
| 288 | } |
| 289 | return ok; |
| 290 | } |
| 291 | |
| 292 | static void SaveOutput(const WebPDecBuffer* const buffer, |
| 293 | OutputFileFormat format, const char* const out_file) { |
| 294 | FILE* fout = NULL; |
| 295 | int needs_open_file = 1; |
| 296 | int ok = 1; |
| 297 | Stopwatch stop_watch; |
| 298 | |
| 299 | if (verbose) |
| 300 | StopwatchReadAndReset(&stop_watch); |
| 301 | |
| 302 | #ifdef HAVE_WINCODEC_H |
| 303 | needs_open_file = (format != PNG); |
| 304 | #endif |
| 305 | if (needs_open_file) { |
| 306 | fout = fopen(out_file, "wb"); |
| 307 | if (!fout) { |
| 308 | fprintf(stderr, "Error opening output file %s\n", out_file); |
| 309 | return; |
| 310 | } |
| 311 | } |
| 312 | |
| 313 | if (format == PNG) { |
| 314 | #ifdef HAVE_WINCODEC_H |
| 315 | ok &= WritePNG(out_file, buffer); |
| 316 | #else |
| 317 | ok &= WritePNG(fout, buffer); |
| 318 | #endif |
| 319 | } else if (format == PAM) { |
| 320 | ok &= WritePPM(fout, buffer, 1); |
| 321 | } else if (format == PPM) { |
| 322 | ok &= WritePPM(fout, buffer, 0); |
| 323 | } else if (format == PGM || format == YUV) { |
| 324 | ok &= WritePGMOrYUV(fout, buffer, format); |
| 325 | } else if (format == ALPHA_PLANE_ONLY) { |
| 326 | ok &= WriteAlphaPlane(fout, buffer); |
| 327 | } |
| 328 | if (fout) { |
| 329 | fclose(fout); |
| 330 | } |
| 331 | if (ok) { |
| 332 | printf("Saved file %s\n", out_file); |
| 333 | if (verbose) { |
| 334 | const double write_time = StopwatchReadAndReset(&stop_watch); |
| 335 | printf("Time to write output: %.3fs\n", write_time); |
| 336 | } |
| 337 | } else { |
| 338 | fprintf(stderr, "Error writing file %s !!\n", out_file); |
| 339 | } |
| 340 | } |
| 341 | |
| 342 | static void Help(void) { |
| 343 | printf("Usage: dwebp in_file [options] [-o out_file]\n\n" |
| 344 | "Decodes the WebP image file to PNG format [Default]\n" |
| 345 | "Use following options to convert into alternate image formats:\n" |
| 346 | " -pam ......... save the raw RGBA samples as a color PAM\n" |
| 347 | " -ppm ......... save the raw RGB samples as a color PPM\n" |
| 348 | " -pgm ......... save the raw YUV samples as a grayscale PGM\n" |
| 349 | " file with IMC4 layout.\n" |
| 350 | " -yuv ......... save the raw YUV samples in flat layout.\n" |
| 351 | "\n" |
| 352 | " Other options are:\n" |
| 353 | " -version .... print version number and exit.\n" |
| 354 | " -nofancy ..... don't use the fancy YUV420 upscaler.\n" |
| 355 | " -nofilter .... disable in-loop filtering.\n" |
| 356 | " -mt .......... use multi-threading\n" |
| 357 | " -crop <x> <y> <w> <h> ... crop output with the given rectangle\n" |
| 358 | " -scale <w> <h> .......... scale the output (*after* any cropping)\n" |
| 359 | " -alpha ....... only save the alpha plane.\n" |
| 360 | " -h ....... this help message.\n" |
| 361 | " -v ....... verbose (e.g. print encoding/decoding times)\n" |
| 362 | #ifndef WEBP_DLL |
| 363 | " -noasm ....... disable all assembly optimizations.\n" |
| 364 | #endif |
Eric Hassold | 9aea642 | 2011-01-04 17:22:46 -0800 | [diff] [blame] | 365 | ); |
| 366 | } |
| 367 | |
Vikas Arora | 0406ce1 | 2013-08-09 15:57:12 -0700 | [diff] [blame] | 368 | static const char* const kStatusMessages[] = { |
| 369 | "OK", "OUT_OF_MEMORY", "INVALID_PARAM", "BITSTREAM_ERROR", |
| 370 | "UNSUPPORTED_FEATURE", "SUSPENDED", "USER_ABORT", "NOT_ENOUGH_DATA" |
| 371 | }; |
| 372 | |
| 373 | int main(int argc, const char *argv[]) { |
Eric Hassold | 9aea642 | 2011-01-04 17:22:46 -0800 | [diff] [blame] | 374 | const char *in_file = NULL; |
| 375 | const char *out_file = NULL; |
Eric Hassold | 9aea642 | 2011-01-04 17:22:46 -0800 | [diff] [blame] | 376 | |
Vikas Arora | 0406ce1 | 2013-08-09 15:57:12 -0700 | [diff] [blame] | 377 | WebPDecoderConfig config; |
| 378 | WebPDecBuffer* const output_buffer = &config.output; |
| 379 | WebPBitstreamFeatures* const bitstream = &config.input; |
| 380 | OutputFileFormat format = PNG; |
Eric Hassold | 9aea642 | 2011-01-04 17:22:46 -0800 | [diff] [blame] | 381 | int c; |
Vikas Arora | 0406ce1 | 2013-08-09 15:57:12 -0700 | [diff] [blame] | 382 | |
| 383 | if (!WebPInitDecoderConfig(&config)) { |
| 384 | fprintf(stderr, "Library version mismatch!\n"); |
| 385 | return -1; |
| 386 | } |
| 387 | |
Eric Hassold | 9aea642 | 2011-01-04 17:22:46 -0800 | [diff] [blame] | 388 | for (c = 1; c < argc; ++c) { |
Vikas Arora | 0406ce1 | 2013-08-09 15:57:12 -0700 | [diff] [blame] | 389 | if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) { |
| 390 | Help(); |
Eric Hassold | 9aea642 | 2011-01-04 17:22:46 -0800 | [diff] [blame] | 391 | return 0; |
| 392 | } else if (!strcmp(argv[c], "-o") && c < argc - 1) { |
| 393 | out_file = argv[++c]; |
Vikas Arora | 0406ce1 | 2013-08-09 15:57:12 -0700 | [diff] [blame] | 394 | } else if (!strcmp(argv[c], "-alpha")) { |
| 395 | format = ALPHA_PLANE_ONLY; |
| 396 | } else if (!strcmp(argv[c], "-nofancy")) { |
| 397 | config.options.no_fancy_upsampling = 1; |
| 398 | } else if (!strcmp(argv[c], "-nofilter")) { |
| 399 | config.options.bypass_filtering = 1; |
| 400 | } else if (!strcmp(argv[c], "-pam")) { |
| 401 | format = PAM; |
| 402 | } else if (!strcmp(argv[c], "-ppm")) { |
| 403 | format = PPM; |
| 404 | } else if (!strcmp(argv[c], "-version")) { |
| 405 | const int version = WebPGetDecoderVersion(); |
| 406 | printf("%d.%d.%d\n", |
| 407 | (version >> 16) & 0xff, (version >> 8) & 0xff, version & 0xff); |
| 408 | return 0; |
| 409 | } else if (!strcmp(argv[c], "-pgm")) { |
| 410 | format = PGM; |
| 411 | } else if (!strcmp(argv[c], "-yuv")) { |
| 412 | format = YUV; |
| 413 | } else if (!strcmp(argv[c], "-mt")) { |
| 414 | config.options.use_threads = 1; |
| 415 | } else if (!strcmp(argv[c], "-crop") && c < argc - 4) { |
| 416 | config.options.use_cropping = 1; |
| 417 | config.options.crop_left = strtol(argv[++c], NULL, 0); |
| 418 | config.options.crop_top = strtol(argv[++c], NULL, 0); |
| 419 | config.options.crop_width = strtol(argv[++c], NULL, 0); |
| 420 | config.options.crop_height = strtol(argv[++c], NULL, 0); |
| 421 | } else if (!strcmp(argv[c], "-scale") && c < argc - 2) { |
| 422 | config.options.use_scaling = 1; |
| 423 | config.options.scaled_width = strtol(argv[++c], NULL, 0); |
| 424 | config.options.scaled_height = strtol(argv[++c], NULL, 0); |
| 425 | } else if (!strcmp(argv[c], "-v")) { |
| 426 | verbose = 1; |
| 427 | #ifndef WEBP_DLL |
| 428 | } else if (!strcmp(argv[c], "-noasm")) { |
| 429 | VP8GetCPUInfo = NULL; |
| 430 | #endif |
Eric Hassold | 9aea642 | 2011-01-04 17:22:46 -0800 | [diff] [blame] | 431 | } else if (argv[c][0] == '-') { |
Vikas Arora | 0406ce1 | 2013-08-09 15:57:12 -0700 | [diff] [blame] | 432 | fprintf(stderr, "Unknown option '%s'\n", argv[c]); |
| 433 | Help(); |
Eric Hassold | 9aea642 | 2011-01-04 17:22:46 -0800 | [diff] [blame] | 434 | return -1; |
| 435 | } else { |
| 436 | in_file = argv[c]; |
| 437 | } |
| 438 | } |
| 439 | |
| 440 | if (in_file == NULL) { |
Vikas Arora | 0406ce1 | 2013-08-09 15:57:12 -0700 | [diff] [blame] | 441 | fprintf(stderr, "missing input file!!\n"); |
| 442 | Help(); |
Eric Hassold | 9aea642 | 2011-01-04 17:22:46 -0800 | [diff] [blame] | 443 | return -1; |
| 444 | } |
| 445 | |
| 446 | { |
Vikas Arora | 0406ce1 | 2013-08-09 15:57:12 -0700 | [diff] [blame] | 447 | Stopwatch stop_watch; |
| 448 | VP8StatusCode status = VP8_STATUS_OK; |
| 449 | int ok; |
| 450 | size_t data_size = 0; |
| 451 | const uint8_t* data = NULL; |
| 452 | |
| 453 | if (!ExUtilReadFile(in_file, &data, &data_size)) return -1; |
| 454 | |
| 455 | if (verbose) |
| 456 | StopwatchReadAndReset(&stop_watch); |
| 457 | |
| 458 | status = WebPGetFeatures(data, data_size, bitstream); |
| 459 | if (status != VP8_STATUS_OK) { |
| 460 | goto end; |
Eric Hassold | 9aea642 | 2011-01-04 17:22:46 -0800 | [diff] [blame] | 461 | } |
Vikas Arora | 0406ce1 | 2013-08-09 15:57:12 -0700 | [diff] [blame] | 462 | |
| 463 | if (bitstream->has_animation) { |
| 464 | fprintf(stderr, |
| 465 | "Error! Decoding of an animated WebP file is not supported.\n" |
| 466 | " Use webpmux to extract the individual frames or\n" |
| 467 | " vwebp to view this image.\n"); |
| 468 | } |
| 469 | |
| 470 | switch (format) { |
| 471 | case PNG: |
| 472 | #ifdef HAVE_WINCODEC_H |
| 473 | output_buffer->colorspace = bitstream->has_alpha ? MODE_BGRA : MODE_BGR; |
| 474 | #else |
| 475 | output_buffer->colorspace = bitstream->has_alpha ? MODE_RGBA : MODE_RGB; |
| 476 | #endif |
| 477 | break; |
| 478 | case PAM: |
| 479 | output_buffer->colorspace = MODE_RGBA; |
| 480 | break; |
| 481 | case PPM: |
| 482 | output_buffer->colorspace = MODE_RGB; // drops alpha for PPM |
| 483 | break; |
| 484 | case PGM: |
| 485 | case YUV: |
| 486 | output_buffer->colorspace = bitstream->has_alpha ? MODE_YUVA : MODE_YUV; |
| 487 | break; |
| 488 | case ALPHA_PLANE_ONLY: |
| 489 | output_buffer->colorspace = MODE_YUVA; |
| 490 | break; |
| 491 | default: |
| 492 | free((void*)data); |
| 493 | return -1; |
| 494 | } |
| 495 | status = WebPDecode(data, data_size, &config); |
| 496 | |
| 497 | if (verbose) { |
| 498 | const double decode_time = StopwatchReadAndReset(&stop_watch); |
| 499 | printf("Time to decode picture: %.3fs\n", decode_time); |
| 500 | } |
| 501 | end: |
| 502 | free((void*)data); |
| 503 | ok = (status == VP8_STATUS_OK); |
Eric Hassold | 9aea642 | 2011-01-04 17:22:46 -0800 | [diff] [blame] | 504 | if (!ok) { |
Vikas Arora | 0406ce1 | 2013-08-09 15:57:12 -0700 | [diff] [blame] | 505 | fprintf(stderr, "Decoding of %s failed.\n", in_file); |
| 506 | fprintf(stderr, "Status: %d (%s)\n", status, kStatusMessages[status]); |
Eric Hassold | 9aea642 | 2011-01-04 17:22:46 -0800 | [diff] [blame] | 507 | return -1; |
| 508 | } |
Eric Hassold | 9aea642 | 2011-01-04 17:22:46 -0800 | [diff] [blame] | 509 | } |
| 510 | |
| 511 | if (out_file) { |
Vikas Arora | 0406ce1 | 2013-08-09 15:57:12 -0700 | [diff] [blame] | 512 | printf("Decoded %s. Dimensions: %d x %d%s. Now saving...\n", in_file, |
| 513 | output_buffer->width, output_buffer->height, |
| 514 | bitstream->has_alpha ? " (with alpha)" : ""); |
| 515 | SaveOutput(output_buffer, format, out_file); |
| 516 | } else { |
| 517 | printf("File %s can be decoded (dimensions: %d x %d)%s.\n", |
| 518 | in_file, output_buffer->width, output_buffer->height, |
| 519 | bitstream->has_alpha ? " (with alpha)" : ""); |
| 520 | printf("Nothing written; use -o flag to save the result as e.g. PNG.\n"); |
Eric Hassold | 9aea642 | 2011-01-04 17:22:46 -0800 | [diff] [blame] | 521 | } |
Vikas Arora | 0406ce1 | 2013-08-09 15:57:12 -0700 | [diff] [blame] | 522 | WebPFreeDecBuffer(output_buffer); |
Eric Hassold | 9aea642 | 2011-01-04 17:22:46 -0800 | [diff] [blame] | 523 | |
| 524 | return 0; |
| 525 | } |
| 526 | |
Vikas Arora | 0406ce1 | 2013-08-09 15:57:12 -0700 | [diff] [blame] | 527 | //------------------------------------------------------------------------------ |