static HRESULT WINAPI PngFrameEncode_WritePixels(IWICBitmapFrameEncode *iface, UINT lineCount, UINT cbStride, UINT cbBufferSize, BYTE *pbPixels) { PngEncoder *This = encoder_from_frame(iface); png_byte **row_pointers=NULL; UINT i; jmp_buf jmpbuf; TRACE("(%p,%u,%u,%u,%p)\n", iface, lineCount, cbStride, cbBufferSize, pbPixels); if (!This->frame_initialized || !This->width || !This->height || !This->format) return WINCODEC_ERR_WRONGSTATE; if (lineCount == 0 || lineCount + This->lines_written > This->height) return E_INVALIDARG; /* set up setjmp/longjmp error handling */ if (setjmp(jmpbuf)) { HeapFree(GetProcessHeap(), 0, row_pointers); return E_FAIL; } ppng_set_error_fn(This->png_ptr, &jmpbuf, user_error_fn, user_warning_fn); if (!This->info_written) { ppng_set_IHDR(This->png_ptr, This->info_ptr, This->width, This->height, This->format->bit_depth, This->format->color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); if (This->xres != 0.0 && This->yres != 0.0) { ppng_set_pHYs(This->png_ptr, This->info_ptr, (This->xres+0.0127) / 0.0254, (This->yres+0.0127) / 0.0254, PNG_RESOLUTION_METER); } ppng_write_info(This->png_ptr, This->info_ptr); if (This->format->remove_filler) ppng_set_filler(This->png_ptr, 0, PNG_FILLER_AFTER); if (This->format->swap_rgb) ppng_set_bgr(This->png_ptr); This->info_written = TRUE; } row_pointers = HeapAlloc(GetProcessHeap(), 0, lineCount * sizeof(png_byte*)); if (!row_pointers) return E_OUTOFMEMORY; for (i=0; i<lineCount; i++) row_pointers[i] = pbPixels + cbStride * i; ppng_write_rows(This->png_ptr, row_pointers, lineCount); This->lines_written += lineCount; HeapFree(GetProcessHeap(), 0, row_pointers); return S_OK; }
static HRESULT WINAPI PngFrameEncode_Commit(IWICBitmapFrameEncode *iface) { PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface); png_byte **row_pointers=NULL; jmp_buf jmpbuf; TRACE("(%p)\n", iface); EnterCriticalSection(&This->lock); if (!This->info_written || This->lines_written != This->height || This->frame_committed) { LeaveCriticalSection(&This->lock); return WINCODEC_ERR_WRONGSTATE; } /* set up setjmp/longjmp error handling */ if (setjmp(jmpbuf)) { LeaveCriticalSection(&This->lock); HeapFree(GetProcessHeap(), 0, row_pointers); return E_FAIL; } ppng_set_error_fn(This->png_ptr, jmpbuf, user_error_fn, user_warning_fn); if (This->interlace) { int i; row_pointers = HeapAlloc(GetProcessHeap(), 0, This->height * sizeof(png_byte*)); if (!row_pointers) { LeaveCriticalSection(&This->lock); return E_OUTOFMEMORY; } for (i=0; i<This->height; i++) row_pointers[i] = This->data + This->stride * i; for (i=0; i<This->passes; i++) ppng_write_rows(This->png_ptr, row_pointers, This->height); } ppng_write_end(This->png_ptr, This->info_ptr); This->frame_committed = TRUE; HeapFree(GetProcessHeap(), 0, row_pointers); LeaveCriticalSection(&This->lock); return S_OK; }
static HRESULT WINAPI PngFrameEncode_WritePixels(IWICBitmapFrameEncode *iface, UINT lineCount, UINT cbStride, UINT cbBufferSize, BYTE *pbPixels) { PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface); png_byte **row_pointers=NULL; UINT i; jmp_buf jmpbuf; TRACE("(%p,%u,%u,%u,%p)\n", iface, lineCount, cbStride, cbBufferSize, pbPixels); EnterCriticalSection(&This->lock); if (!This->frame_initialized || !This->width || !This->height || !This->format) { LeaveCriticalSection(&This->lock); return WINCODEC_ERR_WRONGSTATE; } if (lineCount == 0 || lineCount + This->lines_written > This->height) { LeaveCriticalSection(&This->lock); return E_INVALIDARG; } /* set up setjmp/longjmp error handling */ if (setjmp(jmpbuf)) { LeaveCriticalSection(&This->lock); HeapFree(GetProcessHeap(), 0, row_pointers); return E_FAIL; } ppng_set_error_fn(This->png_ptr, jmpbuf, user_error_fn, user_warning_fn); if (!This->info_written) { if (This->interlace) { /* libpng requires us to write all data multiple times in this case. */ This->stride = (This->format->bpp * This->width + 7)/8; This->data = HeapAlloc(GetProcessHeap(), 0, This->height * This->stride); if (!This->data) { LeaveCriticalSection(&This->lock); return E_OUTOFMEMORY; } } ppng_set_IHDR(This->png_ptr, This->info_ptr, This->width, This->height, This->format->bit_depth, This->format->color_type, This->interlace ? PNG_INTERLACE_ADAM7 : PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); if (This->xres != 0.0 && This->yres != 0.0) { ppng_set_pHYs(This->png_ptr, This->info_ptr, (This->xres+0.0127) / 0.0254, (This->yres+0.0127) / 0.0254, PNG_RESOLUTION_METER); } ppng_write_info(This->png_ptr, This->info_ptr); if (This->format->remove_filler) ppng_set_filler(This->png_ptr, 0, PNG_FILLER_AFTER); if (This->format->swap_rgb) ppng_set_bgr(This->png_ptr); if (This->interlace) This->passes = ppng_set_interlace_handling(This->png_ptr); This->info_written = TRUE; } if (This->interlace) { /* Just store the data so we can write it in multiple passes in Commit. */ for (i=0; i<lineCount; i++) memcpy(This->data + This->stride * (This->lines_written + i), pbPixels + cbStride * i, This->stride); This->lines_written += lineCount; LeaveCriticalSection(&This->lock); return S_OK; } row_pointers = HeapAlloc(GetProcessHeap(), 0, lineCount * sizeof(png_byte*)); if (!row_pointers) { LeaveCriticalSection(&This->lock); return E_OUTOFMEMORY; } for (i=0; i<lineCount; i++) row_pointers[i] = pbPixels + cbStride * i; ppng_write_rows(This->png_ptr, row_pointers, lineCount); This->lines_written += lineCount; LeaveCriticalSection(&This->lock); HeapFree(GetProcessHeap(), 0, row_pointers); return S_OK; }
/** * WritePNG(): Write a PNG image. * @param fImg File handle to save the image to. * @param w Width of the image. * @param h Height of the image. * @param pitch Pitch of the image. (measured in pixels) * @param screen Pointer to screen buffer. * @param bpp Bits per pixel. * @param alpha Alpha channel specification. (32-bit color only.) * @return 0 on success; non-zero on error. */ int ImageUtil::WritePNG(FILE *fImg, const int w, const int h, const int pitch, const void *screen, const int bpp, const AlphaChannel alpha) { if (!fImg || !screen || (w <= 0 || h <= 0 || pitch <= 0)) return 1; int rval = gsft_png_dll_init(); if (rval) return rval; png_structp png_ptr; png_infop info_ptr; // Initialize libpng. png_ptr = ppng_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!png_ptr) { LOG_MSG(gens, LOG_MSG_LEVEL_CRITICAL, "Error initializing the PNG pointer."); return 2; } info_ptr = ppng_create_info_struct(png_ptr); if (!info_ptr) { LOG_MSG(gens, LOG_MSG_LEVEL_CRITICAL, "Error initializing the PNG info pointer."); ppng_destroy_write_struct(&png_ptr, (png_infopp)NULL); return 3; } if (setjmp(png_jmpbuf(png_ptr))) { // TODO: Is setjmp() really necessary? LOG_MSG(gens, LOG_MSG_LEVEL_CRITICAL, "Error initializing the PNG setjmp pointer."); ppng_destroy_write_struct(&png_ptr, &info_ptr); return 4; } // Initialize libpng I/O. ppng_init_io(png_ptr, fImg); // Disable PNG filters. ppng_set_filter(png_ptr, 0, PNG_FILTER_NONE); // Set the compression level to 5. (Levels range from 1 through 9.) // TODO: Add a UI option to set compression level. ppng_set_compression_level(png_ptr, 5); // Set up the PNG header. if (!(bpp == 32 && alpha != ALPHACHANNEL_NONE)) { ppng_set_IHDR(png_ptr, info_ptr, w, h, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); } else { // 32-bit color, with alpha channel. ppng_set_IHDR(png_ptr, info_ptr, w, h, 8, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); } // Write the PNG information to the file. ppng_write_info(png_ptr, info_ptr); #if GSFT_BYTEORDER == GSFT_LIL_ENDIAN // PNG stores data in big-endian format. // On little-endian systems, byteswapping needs to be enabled. // TODO: Check if this really isn't needed on big-endian systems. ppng_set_swap(png_ptr); #endif // Write the image. if (bpp == 15) { // 15-bit color. (Mode 555) rval = T_writePNG_rows_16<uint16_t, MASK_RED_15, MASK_GREEN_15, MASK_BLUE_15, SHIFT_RED_15, SHIFT_GREEN_15, SHIFT_BLUE_15> (static_cast<const uint16_t*>(screen), png_ptr, info_ptr, w, h, pitch); if (rval != 0) return 5; } else if (bpp == 16) { // 16-bit color. (Mode 565) rval = T_writePNG_rows_16<uint16_t, MASK_RED_16, MASK_GREEN_16, MASK_BLUE_16, SHIFT_RED_16, SHIFT_GREEN_16, SHIFT_BLUE_16> (static_cast<const uint16_t*>(screen), png_ptr, info_ptr, w, h, pitch); if (rval != 0) return 6; } else // if (bpp == 32) { // 32-bit color. // Depending on the alpha channel settings, libpng expects either // 24-bit data (no alpha) or 32-bit data (with alpha); however, // libpng offers an option to automatically convert 32-bit data // without alpha channel to 24-bit. (png_set_filler()) // TODO: PNG_FILLER_AFTER, BGR mode - needed for little-endian. // Figure out what's needed on big-endian. png_byte **row_pointers = static_cast<png_byte**>(malloc(sizeof(png_byte*) * h)); uint32_t *screen32 = (uint32_t*)screen; for (int y = 0; y < h; y++) { row_pointers[y] = (uint8_t*)&screen32[y * pitch]; } if (!alpha) { // No alpha channel. Set filler byte. ppng_set_filler(png_ptr, 0, PNG_FILLER_AFTER); } else if (alpha == ALPHACHANNEL_TRANSPARENCY) { // Alpha channel indicates transparency. // 0x00 == opaque; 0xFF == transparent. ppng_set_invert_alpha(png_ptr); } ppng_set_bgr(png_ptr); ppng_write_rows(png_ptr, row_pointers, h); free(row_pointers); } // Finished writing. ppng_write_end(png_ptr, info_ptr); ppng_destroy_write_struct(&png_ptr, &info_ptr); return 0; }