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