go-libwebp

Experimental translation from libwebp to Go source.
Log | Files | Refs | README | LICENSE

image_enc.c (22159B)


      1 // Copyright 2016 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 // Save image
     11 
     12 #include "./image_enc.h"
     13 
     14 #include <assert.h>
     15 #include <stdio.h>
     16 #include <string.h>
     17 
     18 #ifdef WEBP_HAVE_PNG
     19 #include <png.h>
     20 #include <setjmp.h>   // note: this must be included *after* png.h
     21 #endif
     22 
     23 #ifdef HAVE_WINCODEC_H
     24 #ifdef __MINGW32__
     25 #define INITGUID  // Without this GUIDs are declared extern and fail to link
     26 #endif
     27 #define CINTERFACE
     28 #define COBJMACROS
     29 #define _WIN32_IE 0x500  // Workaround bug in shlwapi.h when compiling C++
     30                          // code with COBJMACROS.
     31 #include <ole2.h>  // CreateStreamOnHGlobal()
     32 #include <shlwapi.h>
     33 #include <tchar.h>
     34 #include <windows.h>
     35 #include <wincodec.h>
     36 #endif
     37 
     38 #include "../examples/unicode.h"
     39 #include "./imageio_util.h"
     40 #include "webp/decode.h"
     41 #include "webp/types.h"
     42 
     43 //------------------------------------------------------------------------------
     44 // PNG
     45 
     46 #ifdef HAVE_WINCODEC_H
     47 
     48 #define IFS(fn)                                                     \
     49   do {                                                              \
     50     if (SUCCEEDED(hr)) {                                            \
     51       hr = (fn);                                                    \
     52       if (FAILED(hr)) fprintf(stderr, #fn " failed %08lx\n", hr);   \
     53     }                                                               \
     54   } while (0)
     55 
     56 #ifdef __cplusplus
     57 #define MAKE_REFGUID(x) (x)
     58 #else
     59 #define MAKE_REFGUID(x) &(x)
     60 #endif
     61 
     62 static HRESULT CreateOutputStream(const char* out_file_name,
     63                                   int write_to_mem, IStream** stream) {
     64   HRESULT hr = S_OK;
     65   if (write_to_mem) {
     66     // Output to a memory buffer. This is freed when 'stream' is released.
     67     IFS(CreateStreamOnHGlobal(NULL, TRUE, stream));
     68   } else {
     69     IFS(SHCreateStreamOnFile((const LPTSTR)out_file_name,
     70                              STGM_WRITE | STGM_CREATE, stream));
     71   }
     72   if (FAILED(hr)) {
     73     _ftprintf(stderr, _T("Error opening output file %s (%08lx)\n"),
     74               (const LPTSTR)out_file_name, hr);
     75   }
     76   return hr;
     77 }
     78 
     79 static HRESULT WriteUsingWIC(const char* out_file_name, int use_stdout,
     80                              REFGUID container_guid,
     81                              uint8_t* rgb, int stride,
     82                              uint32_t width, uint32_t height, int has_alpha) {
     83   HRESULT hr = S_OK;
     84   IWICImagingFactory* factory = NULL;
     85   IWICBitmapFrameEncode* frame = NULL;
     86   IWICBitmapEncoder* encoder = NULL;
     87   IStream* stream = NULL;
     88   WICPixelFormatGUID pixel_format = has_alpha ? GUID_WICPixelFormat32bppBGRA
     89                                               : GUID_WICPixelFormat24bppBGR;
     90 
     91   if (out_file_name == NULL || rgb == NULL) return E_INVALIDARG;
     92 
     93   IFS(CoInitialize(NULL));
     94   IFS(CoCreateInstance(MAKE_REFGUID(CLSID_WICImagingFactory), NULL,
     95                        CLSCTX_INPROC_SERVER,
     96                        MAKE_REFGUID(IID_IWICImagingFactory),
     97                        (LPVOID*)&factory));
     98   if (hr == REGDB_E_CLASSNOTREG) {
     99     fprintf(stderr,
    100             "Couldn't access Windows Imaging Component (are you running "
    101             "Windows XP SP3 or newer?). PNG support not available. "
    102             "Use -ppm or -pgm for available PPM and PGM formats.\n");
    103   }
    104   IFS(CreateOutputStream(out_file_name, use_stdout, &stream));
    105   IFS(IWICImagingFactory_CreateEncoder(factory, container_guid, NULL,
    106                                        &encoder));
    107   IFS(IWICBitmapEncoder_Initialize(encoder, stream,
    108                                    WICBitmapEncoderNoCache));
    109   IFS(IWICBitmapEncoder_CreateNewFrame(encoder, &frame, NULL));
    110   IFS(IWICBitmapFrameEncode_Initialize(frame, NULL));
    111   IFS(IWICBitmapFrameEncode_SetSize(frame, width, height));
    112   IFS(IWICBitmapFrameEncode_SetPixelFormat(frame, &pixel_format));
    113   IFS(IWICBitmapFrameEncode_WritePixels(frame, height, stride,
    114                                         height * stride, rgb));
    115   IFS(IWICBitmapFrameEncode_Commit(frame));
    116   IFS(IWICBitmapEncoder_Commit(encoder));
    117 
    118   if (SUCCEEDED(hr) && use_stdout) {
    119     HGLOBAL image;
    120     IFS(GetHGlobalFromStream(stream, &image));
    121     if (SUCCEEDED(hr)) {
    122       HANDLE std_output = GetStdHandle(STD_OUTPUT_HANDLE);
    123       DWORD mode;
    124       const BOOL update_mode = GetConsoleMode(std_output, &mode);
    125       const void* const image_mem = GlobalLock(image);
    126       DWORD bytes_written = 0;
    127 
    128       // Clear output processing if necessary, then output the image.
    129       if (update_mode) SetConsoleMode(std_output, 0);
    130       if (!WriteFile(std_output, image_mem, (DWORD)GlobalSize(image),
    131                      &bytes_written, NULL) ||
    132           bytes_written != GlobalSize(image)) {
    133         hr = E_FAIL;
    134       }
    135       if (update_mode) SetConsoleMode(std_output, mode);
    136       GlobalUnlock(image);
    137     }
    138   }
    139 
    140   if (frame != NULL) IUnknown_Release(frame);
    141   if (encoder != NULL) IUnknown_Release(encoder);
    142   if (factory != NULL) IUnknown_Release(factory);
    143   if (stream != NULL) IUnknown_Release(stream);
    144   return hr;
    145 }
    146 
    147 int WebPWritePNG(const char* out_file_name, int use_stdout,
    148                  const WebPDecBuffer* const buffer) {
    149   const uint32_t width = buffer->width;
    150   const uint32_t height = buffer->height;
    151   uint8_t* const rgb = buffer->u.RGBA.rgba;
    152   const int stride = buffer->u.RGBA.stride;
    153   const int has_alpha = WebPIsAlphaMode(buffer->colorspace);
    154 
    155   return SUCCEEDED(WriteUsingWIC(out_file_name, use_stdout,
    156                                  MAKE_REFGUID(GUID_ContainerFormatPng),
    157                                  rgb, stride, width, height, has_alpha));
    158 }
    159 
    160 #elif defined(WEBP_HAVE_PNG)    // !HAVE_WINCODEC_H
    161 static void PNGAPI PNGErrorFunction(png_structp png, png_const_charp unused) {
    162   (void)unused;  // remove variable-unused warning
    163   longjmp(png_jmpbuf(png), 1);
    164 }
    165 
    166 int WebPWritePNG(FILE* out_file, const WebPDecBuffer* const buffer) {
    167   volatile png_structp png;
    168   volatile png_infop info;
    169 
    170   if (out_file == NULL || buffer == NULL) return 0;
    171 
    172   png = png_create_write_struct(PNG_LIBPNG_VER_STRING,
    173                                 NULL, PNGErrorFunction, NULL);
    174   if (png == NULL) {
    175     return 0;
    176   }
    177   info = png_create_info_struct(png);
    178   if (info == NULL) {
    179     png_destroy_write_struct((png_structpp)&png, NULL);
    180     return 0;
    181   }
    182   if (setjmp(png_jmpbuf(png))) {
    183     png_destroy_write_struct((png_structpp)&png, (png_infopp)&info);
    184     return 0;
    185   }
    186   png_init_io(png, out_file);
    187   {
    188     const uint32_t width = buffer->width;
    189     const uint32_t height = buffer->height;
    190     png_bytep row = buffer->u.RGBA.rgba;
    191     const int stride = buffer->u.RGBA.stride;
    192     const int has_alpha = WebPIsAlphaMode(buffer->colorspace);
    193     uint32_t y;
    194 
    195     png_set_IHDR(png, info, width, height, 8,
    196                  has_alpha ? PNG_COLOR_TYPE_RGBA : PNG_COLOR_TYPE_RGB,
    197                  PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
    198                  PNG_FILTER_TYPE_DEFAULT);
    199     png_write_info(png, info);
    200     for (y = 0; y < height; ++y) {
    201       png_write_rows(png, &row, 1);
    202       row += stride;
    203     }
    204   }
    205   png_write_end(png, info);
    206   png_destroy_write_struct((png_structpp)&png, (png_infopp)&info);
    207   return 1;
    208 }
    209 #else    // !HAVE_WINCODEC_H && !WEBP_HAVE_PNG
    210 int WebPWritePNG(FILE* fout, const WebPDecBuffer* const buffer) {
    211   if (fout == NULL || buffer == NULL) return 0;
    212 
    213   fprintf(stderr, "PNG support not compiled. Please install the libpng "
    214           "development package before building.\n");
    215   fprintf(stderr, "You can run with -ppm flag to decode in PPM format.\n");
    216   return 0;
    217 }
    218 #endif
    219 
    220 //------------------------------------------------------------------------------
    221 // PPM / PAM
    222 
    223 static int WritePPMPAM(FILE* fout, const WebPDecBuffer* const buffer,
    224                        int alpha) {
    225   if (fout == NULL || buffer == NULL) {
    226     return 0;
    227   } else {
    228     const uint32_t width = buffer->width;
    229     const uint32_t height = buffer->height;
    230     const uint8_t* row = buffer->u.RGBA.rgba;
    231     const int stride = buffer->u.RGBA.stride;
    232     const size_t bytes_per_px = alpha ? 4 : 3;
    233     uint32_t y;
    234 
    235     if (row == NULL) return 0;
    236 
    237     if (alpha) {
    238       fprintf(fout, "P7\nWIDTH %u\nHEIGHT %u\nDEPTH 4\nMAXVAL 255\n"
    239                     "TUPLTYPE RGB_ALPHA\nENDHDR\n", width, height);
    240     } else {
    241       fprintf(fout, "P6\n%u %u\n255\n", width, height);
    242     }
    243     for (y = 0; y < height; ++y) {
    244       if (fwrite(row, width, bytes_per_px, fout) != bytes_per_px) {
    245         return 0;
    246       }
    247       row += stride;
    248     }
    249   }
    250   return 1;
    251 }
    252 
    253 int WebPWritePPM(FILE* fout, const WebPDecBuffer* const buffer) {
    254   return WritePPMPAM(fout, buffer, 0);
    255 }
    256 
    257 int WebPWritePAM(FILE* fout, const WebPDecBuffer* const buffer) {
    258   return WritePPMPAM(fout, buffer, 1);
    259 }
    260 
    261 //------------------------------------------------------------------------------
    262 // Raw PGM
    263 
    264 // Save 16b mode (RGBA4444, RGB565, ...) for debugging purpose.
    265 int WebPWrite16bAsPGM(FILE* fout, const WebPDecBuffer* const buffer) {
    266   uint32_t width, height;
    267   uint8_t* rgba;
    268   int stride;
    269   const uint32_t bytes_per_px = 2;
    270   uint32_t y;
    271 
    272   if (fout == NULL || buffer == NULL) return 0;
    273 
    274   width = buffer->width;
    275   height = buffer->height;
    276   rgba = buffer->u.RGBA.rgba;
    277   stride = buffer->u.RGBA.stride;
    278 
    279   if (rgba == NULL) return 0;
    280 
    281   fprintf(fout, "P5\n%u %u\n255\n", width * bytes_per_px, height);
    282   for (y = 0; y < height; ++y) {
    283     if (fwrite(rgba, width, bytes_per_px, fout) != bytes_per_px) {
    284       return 0;
    285     }
    286     rgba += stride;
    287   }
    288   return 1;
    289 }
    290 
    291 //------------------------------------------------------------------------------
    292 // BMP (see https://en.wikipedia.org/wiki/BMP_file_format#Pixel_storage)
    293 
    294 static void PutLE16(uint8_t* const dst, uint32_t value) {
    295   dst[0] = (value >> 0) & 0xff;
    296   dst[1] = (value >> 8) & 0xff;
    297 }
    298 
    299 static void PutLE32(uint8_t* const dst, uint32_t value) {
    300   PutLE16(dst + 0, (value >>  0) & 0xffff);
    301   PutLE16(dst + 2, (value >> 16) & 0xffff);
    302 }
    303 
    304 #define BMP_HEADER_SIZE 54
    305 #define BMP_HEADER_ALPHA_EXTRA_SIZE 16  // for alpha info
    306 int WebPWriteBMP(FILE* fout, const WebPDecBuffer* const buffer) {
    307   int has_alpha, header_size;
    308   uint32_t width, height;
    309   uint8_t* rgba;
    310   int stride;
    311   uint32_t y;
    312   uint32_t bytes_per_px, line_size, image_size, bmp_stride, total_size;
    313   uint8_t bmp_header[BMP_HEADER_SIZE + BMP_HEADER_ALPHA_EXTRA_SIZE] = { 0 };
    314 
    315   if (fout == NULL || buffer == NULL) return 0;
    316 
    317   has_alpha = WebPIsAlphaMode(buffer->colorspace);
    318   header_size = BMP_HEADER_SIZE + (has_alpha ? BMP_HEADER_ALPHA_EXTRA_SIZE : 0);
    319   width = buffer->width;
    320   height = buffer->height;
    321   rgba = buffer->u.RGBA.rgba;
    322   stride = buffer->u.RGBA.stride;
    323   bytes_per_px = has_alpha ? 4 : 3;
    324   line_size = bytes_per_px * width;
    325   bmp_stride = (line_size + 3) & ~3;  // pad to 4
    326   image_size = bmp_stride * height;
    327   total_size = image_size + header_size;
    328 
    329   if (rgba == NULL) return 0;
    330 
    331   // bitmap file header
    332   PutLE16(bmp_header + 0, 0x4d42);                // signature 'BM'
    333   PutLE32(bmp_header + 2, total_size);            // size including header
    334   PutLE32(bmp_header + 6, 0);                     // reserved
    335   PutLE32(bmp_header + 10, header_size);          // offset to pixel array
    336   // bitmap info header
    337   PutLE32(bmp_header + 14, header_size - 14);     // DIB header size
    338   PutLE32(bmp_header + 18, width);                // dimensions
    339   PutLE32(bmp_header + 22, height);               // no vertical flip
    340   PutLE16(bmp_header + 26, 1);                    // number of planes
    341   PutLE16(bmp_header + 28, bytes_per_px * 8);     // bits per pixel
    342   PutLE32(bmp_header + 30, has_alpha ? 3 : 0);    // BI_BITFIELDS or BI_RGB
    343   PutLE32(bmp_header + 34, image_size);
    344   PutLE32(bmp_header + 38, 2400);                 // x pixels/meter
    345   PutLE32(bmp_header + 42, 2400);                 // y pixels/meter
    346   PutLE32(bmp_header + 46, 0);                    // number of palette colors
    347   PutLE32(bmp_header + 50, 0);                    // important color count
    348   if (has_alpha) {  // BITMAPV3INFOHEADER complement
    349     PutLE32(bmp_header + 54, 0x00ff0000);         // red mask
    350     PutLE32(bmp_header + 58, 0x0000ff00);         // green mask
    351     PutLE32(bmp_header + 62, 0x000000ff);         // blue mask
    352     PutLE32(bmp_header + 66, 0xff000000);         // alpha mask
    353   }
    354 
    355   // TODO(skal): color profile
    356 
    357   // write header
    358   if (fwrite(bmp_header, header_size, 1, fout) != 1) {
    359     return 0;
    360   }
    361 
    362   // write pixel array, bottom to top
    363   for (y = 0; y < height; ++y) {
    364     const uint8_t* const src = &rgba[(uint64_t)(height - 1 - y) * stride];
    365     if (fwrite(src, line_size, 1, fout) != 1) {
    366       return 0;
    367     }
    368     // write padding zeroes
    369     if (bmp_stride != line_size) {
    370       const uint8_t zeroes[3] = { 0 };
    371       if (fwrite(zeroes, bmp_stride - line_size, 1, fout) != 1) {
    372         return 0;
    373       }
    374     }
    375   }
    376   return 1;
    377 }
    378 #undef BMP_HEADER_SIZE
    379 #undef BMP_HEADER_ALPHA_EXTRA_SIZE
    380 
    381 //------------------------------------------------------------------------------
    382 // TIFF
    383 
    384 #define NUM_IFD_ENTRIES 15
    385 #define EXTRA_DATA_SIZE 16
    386 // 10b for signature/header + n * 12b entries + 4b for IFD terminator:
    387 #define EXTRA_DATA_OFFSET (10 + 12 * NUM_IFD_ENTRIES + 4)
    388 #define TIFF_HEADER_SIZE (EXTRA_DATA_OFFSET + EXTRA_DATA_SIZE)
    389 
    390 int WebPWriteTIFF(FILE* fout, const WebPDecBuffer* const buffer) {
    391   int has_alpha;
    392   uint32_t width, height;
    393   uint8_t* rgba;
    394   int stride;
    395   uint8_t bytes_per_px = 0;
    396   const uint8_t assoc_alpha = 0;
    397   // For non-alpha case, we omit tag 0x152 (ExtraSamples).
    398   const uint8_t num_ifd_entries = 0;
    399   uint8_t tiff_header[TIFF_HEADER_SIZE] = {
    400     0x49, 0x49, 0x2a, 0x00,   // little endian signature
    401     8, 0, 0, 0,               // offset to the unique IFD that follows
    402     // IFD (offset = 8). Entries must be written in increasing tag order.
    403     num_ifd_entries, 0,       // Number of entries in the IFD (12 bytes each).
    404     0x00, 0x01, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0,    //  10: Width  (TBD)
    405     0x01, 0x01, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0,    //  22: Height (TBD)
    406     0x02, 0x01, 3, 0, bytes_per_px, 0, 0, 0,     //  34: BitsPerSample: 8888
    407         EXTRA_DATA_OFFSET + 0, 0, 0, 0,
    408     0x03, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0,    //  46: Compression: none
    409     0x06, 0x01, 3, 0, 1, 0, 0, 0, 2, 0, 0, 0,    //  58: Photometric: RGB
    410     0x11, 0x01, 4, 0, 1, 0, 0, 0,                //  70: Strips offset:
    411         TIFF_HEADER_SIZE, 0, 0, 0,               //      data follows header
    412     0x12, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0,    //  82: Orientation: topleft
    413     0x15, 0x01, 3, 0, 1, 0, 0, 0,                //  94: SamplesPerPixels
    414         bytes_per_px, 0, 0, 0,
    415     0x16, 0x01, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0,    // 106: Rows per strip (TBD)
    416     0x17, 0x01, 4, 0, 1, 0, 0, 0, 0, 0, 0, 0,    // 118: StripByteCount (TBD)
    417     0x1a, 0x01, 5, 0, 1, 0, 0, 0,                // 130: X-resolution
    418         EXTRA_DATA_OFFSET + 8, 0, 0, 0,
    419     0x1b, 0x01, 5, 0, 1, 0, 0, 0,                // 142: Y-resolution
    420         EXTRA_DATA_OFFSET + 8, 0, 0, 0,
    421     0x1c, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0,    // 154: PlanarConfiguration
    422     0x28, 0x01, 3, 0, 1, 0, 0, 0, 2, 0, 0, 0,    // 166: ResolutionUnit (inch)
    423     0x52, 0x01, 3, 0, 1, 0, 0, 0,
    424         assoc_alpha, 0, 0, 0,                    // 178: ExtraSamples: rgbA/RGBA
    425     0, 0, 0, 0,                                  // 190: IFD terminator
    426     // EXTRA_DATA_OFFSET:
    427     8, 0, 8, 0, 8, 0, 8, 0,      // BitsPerSample
    428     72, 0, 0, 0, 1, 0, 0, 0      // 72 pixels/inch, for X/Y-resolution
    429   };
    430   uint32_t y;
    431 
    432   if (fout == NULL || buffer == NULL) return 0;
    433 
    434   has_alpha = WebPIsAlphaMode(buffer->colorspace);
    435   width = buffer->width;
    436   height = buffer->height;
    437   rgba = buffer->u.RGBA.rgba;
    438   stride = buffer->u.RGBA.stride;
    439 
    440   if (rgba == NULL) return 0;
    441 
    442   // Update bytes_per_px, num_ifd_entries and assoc_alpha.
    443   tiff_header[38] = tiff_header[102] = bytes_per_px = has_alpha ? 4 : 3;
    444   tiff_header[8] = has_alpha ? NUM_IFD_ENTRIES : NUM_IFD_ENTRIES - 1;
    445   tiff_header[186] = WebPIsPremultipliedMode(buffer->colorspace) ? 1 : 2;
    446 
    447   // Fill placeholders in IFD:
    448   PutLE32(tiff_header + 10 + 8, width);
    449   PutLE32(tiff_header + 22 + 8, height);
    450   PutLE32(tiff_header + 106 + 8, height);
    451   PutLE32(tiff_header + 118 + 8, width * bytes_per_px * height);
    452   if (!has_alpha) PutLE32(tiff_header + 178, 0);  // IFD terminator
    453 
    454   // write header
    455   if (fwrite(tiff_header, sizeof(tiff_header), 1, fout) != 1) {
    456     return 0;
    457   }
    458   // write pixel values
    459   for (y = 0; y < height; ++y) {
    460     if (fwrite(rgba, bytes_per_px, width, fout) != width) {
    461       return 0;
    462     }
    463     rgba += stride;
    464   }
    465 
    466   return 1;
    467 }
    468 
    469 #undef TIFF_HEADER_SIZE
    470 #undef EXTRA_DATA_OFFSET
    471 #undef EXTRA_DATA_SIZE
    472 #undef NUM_IFD_ENTRIES
    473 
    474 //------------------------------------------------------------------------------
    475 // Raw Alpha
    476 
    477 int WebPWriteAlphaPlane(FILE* fout, const WebPDecBuffer* const buffer) {
    478   if (fout == NULL || buffer == NULL) {
    479     return 0;
    480   } else {
    481     const uint32_t width = buffer->width;
    482     const uint32_t height = buffer->height;
    483     const uint8_t* a = buffer->u.YUVA.a;
    484     const int a_stride = buffer->u.YUVA.a_stride;
    485     uint32_t y;
    486 
    487     if (a == NULL) return 0;
    488 
    489     fprintf(fout, "P5\n%u %u\n255\n", width, height);
    490     for (y = 0; y < height; ++y) {
    491       if (fwrite(a, width, 1, fout) != 1) return 0;
    492       a += a_stride;
    493     }
    494     return 1;
    495   }
    496 }
    497 
    498 //------------------------------------------------------------------------------
    499 // PGM with IMC4 layout
    500 
    501 int WebPWritePGM(FILE* fout, const WebPDecBuffer* const buffer) {
    502   if (fout == NULL || buffer == NULL) {
    503     return 0;
    504   } else {
    505     const int width = buffer->width;
    506     const int height = buffer->height;
    507     const WebPYUVABuffer* const yuv = &buffer->u.YUVA;
    508     const uint8_t* src_y = yuv->y;
    509     const uint8_t* src_u = yuv->u;
    510     const uint8_t* src_v = yuv->v;
    511     const uint8_t* src_a = yuv->a;
    512     const int uv_width = (width + 1) / 2;
    513     const int uv_height = (height + 1) / 2;
    514     const int a_height = (src_a != NULL) ? height : 0;
    515     int ok = 1;
    516     int y;
    517 
    518     if (src_y == NULL || src_u == NULL || src_v == NULL) return 0;
    519 
    520     fprintf(fout, "P5\n%d %d\n255\n",
    521             (width + 1) & ~1, height + uv_height + a_height);
    522     for (y = 0; ok && y < height; ++y) {
    523       ok &= (fwrite(src_y, width, 1, fout) == 1);
    524       if (width & 1) fputc(0, fout);    // padding byte
    525       src_y += yuv->y_stride;
    526     }
    527     for (y = 0; ok && y < uv_height; ++y) {
    528       ok &= (fwrite(src_u, uv_width, 1, fout) == 1);
    529       ok &= (fwrite(src_v, uv_width, 1, fout) == 1);
    530       src_u += yuv->u_stride;
    531       src_v += yuv->v_stride;
    532     }
    533     for (y = 0; ok && y < a_height; ++y) {
    534       ok &= (fwrite(src_a, width, 1, fout) == 1);
    535       if (width & 1) fputc(0, fout);    // padding byte
    536       src_a += yuv->a_stride;
    537     }
    538     return ok;
    539   }
    540 }
    541 
    542 //------------------------------------------------------------------------------
    543 // Raw YUV(A) planes
    544 
    545 int WebPWriteYUV(FILE* fout, const WebPDecBuffer* const buffer) {
    546   if (fout == NULL || buffer == NULL) {
    547     return 0;
    548   } else {
    549     const int width = buffer->width;
    550     const int height = buffer->height;
    551     const WebPYUVABuffer* const yuv = &buffer->u.YUVA;
    552     const uint8_t* src_y = yuv->y;
    553     const uint8_t* src_u = yuv->u;
    554     const uint8_t* src_v = yuv->v;
    555     const uint8_t* src_a = yuv->a;
    556     const int uv_width = (width + 1) / 2;
    557     const int uv_height = (height + 1) / 2;
    558     const int a_height = (src_a != NULL) ? height : 0;
    559     int ok = 1;
    560     int y;
    561 
    562     if (src_y == NULL || src_u == NULL || src_v == NULL) return 0;
    563 
    564     for (y = 0; ok && y < height; ++y) {
    565       ok &= (fwrite(src_y, width, 1, fout) == 1);
    566       src_y += yuv->y_stride;
    567     }
    568     for (y = 0; ok && y < uv_height; ++y) {
    569       ok &= (fwrite(src_u, uv_width, 1, fout) == 1);
    570       src_u += yuv->u_stride;
    571     }
    572     for (y = 0; ok && y < uv_height; ++y) {
    573       ok &= (fwrite(src_v, uv_width, 1, fout) == 1);
    574       src_v += yuv->v_stride;
    575     }
    576     for (y = 0; ok && y < a_height; ++y) {
    577       ok &= (fwrite(src_a, width, 1, fout) == 1);
    578       src_a += yuv->a_stride;
    579     }
    580     return ok;
    581   }
    582 }
    583 
    584 //------------------------------------------------------------------------------
    585 // Generic top-level call
    586 
    587 int WebPSaveImage(const WebPDecBuffer* const buffer,
    588                   WebPOutputFileFormat format,
    589                   const char* const out_file_name) {
    590   FILE* fout = NULL;
    591   int needs_open_file = 1;
    592   const int use_stdout =
    593       (out_file_name != NULL) && !WSTRCMP(out_file_name, "-");
    594   int ok = 1;
    595 
    596   if (buffer == NULL || out_file_name == NULL) return 0;
    597 
    598 #ifdef HAVE_WINCODEC_H
    599   needs_open_file = (format != PNG);
    600 #endif
    601 
    602   if (needs_open_file) {
    603     fout = use_stdout ? ImgIoUtilSetBinaryMode(stdout)
    604                       : WFOPEN(out_file_name, "wb");
    605     if (fout == NULL) {
    606       WFPRINTF(stderr, "Error opening output file %s\n",
    607                (const W_CHAR*)out_file_name);
    608       return 0;
    609     }
    610   }
    611 
    612   if (format == PNG ||
    613       format == RGBA || format == BGRA || format == ARGB ||
    614       format == rgbA || format == bgrA || format == Argb) {
    615 #ifdef HAVE_WINCODEC_H
    616     ok &= WebPWritePNG(out_file_name, use_stdout, buffer);
    617 #else
    618     ok &= WebPWritePNG(fout, buffer);
    619 #endif
    620   } else if (format == PAM) {
    621     ok &= WebPWritePAM(fout, buffer);
    622   } else if (format == PPM || format == RGB || format == BGR) {
    623     ok &= WebPWritePPM(fout, buffer);
    624   } else if (format == RGBA_4444 || format == RGB_565 || format == rgbA_4444) {
    625     ok &= WebPWrite16bAsPGM(fout, buffer);
    626   } else if (format == BMP) {
    627     ok &= WebPWriteBMP(fout, buffer);
    628   } else if (format == TIFF) {
    629     ok &= WebPWriteTIFF(fout, buffer);
    630   } else if (format == RAW_YUV) {
    631     ok &= WebPWriteYUV(fout, buffer);
    632   } else if (format == PGM || format == YUV || format == YUVA) {
    633     ok &= WebPWritePGM(fout, buffer);
    634   } else if (format == ALPHA_PLANE_ONLY) {
    635     ok &= WebPWriteAlphaPlane(fout, buffer);
    636   }
    637   if (fout != NULL && fout != stdout) {
    638     fclose(fout);
    639   }
    640   return ok;
    641 }