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 }