/** \brief Constructor. * * @param fd File handle to adapt. * @param skip How many bytes of file have already been read */ PngWriter(FILE *fd) : m_fd(fd), m_png(NULL), m_info(NULL) { m_png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if(!m_png) { std::stringstream sstr; sstr << "could not create a PNG read struct"; BOOST_THROW_EXCEPTION(std::runtime_error(sstr.str())); } m_info = png_create_info_struct(m_png); if(!m_info) { std::stringstream sstr; sstr << "could not create a PNG info struct"; BOOST_THROW_EXCEPTION(std::runtime_error(sstr.str())); } png_init_io(m_png, m_fd); // read header png_set_compression_buffer_size(m_png, 8192); png_set_compression_level(m_png, Z_BEST_COMPRESSION); png_set_compression_mem_level(m_png, Z_BEST_COMPRESSION); png_set_compression_window_bits(m_png, 15); }
std::unique_ptr<PNGImageEncoderState> PNGImageEncoderState::create(const IntSize& imageSize, Vector<unsigned char>* output) { if (imageSize.width() <= 0 || imageSize.height() <= 0) return nullptr; png_struct* png = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0); png_info* info = png_create_info_struct(png); if (!png || !info || setjmp(png_jmpbuf(png))) { png_destroy_write_struct(png ? &png : 0, info ? &info : 0); return nullptr; } // Optimize compression for speed. // The parameters are the same as what libpng uses by default for RGB and RGBA images, except: // The zlib compression level is set to 3 instead of 6, to avoid the lazy Ziv-Lempel match searching. png_set_compression_level(png, 3); // The zlib memory level is set to 8. This actually matches the default, we are just future-proofing. png_set_compression_mem_level(png, 8); // The zlib strategy is set to Z_FILTERED, which does not match the default. // Avoid the zlib strategies Z_HUFFMAN_ONLY or Z_RLE. // Although they are the fastest for poorly-compressible images (e.g. photographs), // they are very slow for highly-compressible images (e.g. text, drawings or business graphics) png_set_compression_strategy(png, Z_FILTERED); // The delta filter is PNG_FILTER_SUB instead of PNG_ALL_FILTERS, to reduce the filter computations. png_set_filter(png, PNG_FILTER_TYPE_BASE, PNG_FILTER_SUB); png_set_write_fn(png, output, writeOutput, 0); png_set_IHDR(png, info, imageSize.width(), imageSize.height(), 8, PNG_COLOR_TYPE_RGB_ALPHA, 0, 0, 0); png_write_info(png, info); return wrapUnique(new PNGImageEncoderState(png, info)); }
errort WritePNG( FILE * fp , unsigned char * data, unsigned int sizeX, unsigned int sizeY, int img_depth, int img_alpha) { png_structp png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, (png_voidp)NULL,NULL,NULL); if (!png_ptr) return BadFormat; png_infop info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_write_struct(&png_ptr, (png_infopp)NULL); return BadFormat; } if (setjmp(png_ptr->jmpbuf)) { png_destroy_write_struct(&png_ptr, &info_ptr); return BadFormat; } png_init_io(png_ptr, fp); png_set_filter(png_ptr, 0, PNG_FILTER_NONE); png_set_compression_level(png_ptr, Z_BEST_COMPRESSION); /* set other zlib parameters */ png_set_compression_mem_level(png_ptr, 8); png_set_compression_strategy(png_ptr, Z_DEFAULT_STRATEGY); png_set_compression_window_bits(png_ptr, 15); png_set_compression_method(png_ptr, 8); png_set_IHDR(png_ptr, info_ptr, sizeX, sizeY, img_depth, img_alpha?PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); png_write_info(png_ptr, info_ptr); # if __BYTE_ORDER != __BIG_ENDIAN if (img_depth==16) { png_set_swap(png_ptr); } #endif int stride = (img_depth/8)*(img_alpha?4:3); png_byte **row_pointers = new png_byte*[sizeY]; for (unsigned int i=0;i<sizeY;i++) { row_pointers[i]= (png_byte *)&data[stride*i*sizeX]; } png_write_image (png_ptr,row_pointers); png_write_end(png_ptr, info_ptr); png_write_flush(png_ptr); png_destroy_write_struct(&png_ptr, &info_ptr); //free (data); delete [] row_pointers; return Ok; }
void png_write( const char *myfile, unsigned char *data, unsigned int width, unsigned int height, bool alpha, char bpp ) { FILE *fp = VSFileSystem::vs_open( myfile, "wb" ); png_structp png_ptr = png_create_write_struct ( PNG_LIBPNG_VER_STRING, (png_voidp) NULL, NULL, NULL ); if (!png_ptr) return; png_infop info_ptr = png_create_info_struct( png_ptr ); if (!info_ptr) { png_destroy_write_struct( &png_ptr, (png_infopp) NULL ); return; } if ( setjmp( png_ptr->jmpbuf ) ) { png_destroy_write_struct( &png_ptr, &info_ptr ); VSFileSystem::vs_close( fp ); return; } png_init_io( png_ptr, fp ); png_set_filter( png_ptr, 0, PNG_FILTER_NONE ); png_set_compression_level( png_ptr, Z_BEST_COMPRESSION ); /* set other zlib parameters */ png_set_compression_mem_level( png_ptr, 8 ); png_set_compression_strategy( png_ptr, Z_DEFAULT_STRATEGY ); png_set_compression_window_bits( png_ptr, 15 ); png_set_compression_method( png_ptr, 8 ); png_set_IHDR( png_ptr, info_ptr, width, height, bpp, alpha ? PNG_COLOR_TYPE_RGB_ALPHA : PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT ); png_write_info( png_ptr, info_ptr ); # if __BYTE_ORDER != __BIG_ENDIAN if (bpp == 16) png_set_swap( png_ptr ); #endif int stride = (bpp/8)*(alpha ? 4 : 3); png_byte **row_pointers = new png_byte*[height]; for (unsigned int i = 0; i < height; i++) row_pointers[i] = (png_byte*) &data[stride*i*width]; png_write_image( png_ptr, row_pointers ); png_write_end( png_ptr, info_ptr ); png_write_flush( png_ptr ); png_destroy_write_struct( &png_ptr, &info_ptr ); VSFileSystem::vs_close( fp ); free( data ); delete[] row_pointers; }
bool CPNGFile::Save(const char *szFilename) { // regular file saving - first, there has to be a buffer if (!pImageData) return false; // open the file fp = fopen(szFilename, "wb"); if (!fp) return false; // clear any previously initialized png-structs (e.g. by reading) ClearPngStructs(); // reinit them for writing fWriteMode=true; png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!png_ptr) { Clear(); return false; } info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { Clear(); return false; } // error handling if (setjmp(png_jmpbuf(png_ptr))) { Clear(); return false; } // io initialization png_init_io(png_ptr, fp); // compression stuff png_set_filter(png_ptr, 0, PNG_FILTER_NONE | PNG_FILTER_SUB | PNG_FILTER_PAETH); png_set_compression_level(png_ptr, Z_BEST_COMPRESSION); png_set_compression_mem_level(png_ptr, 8); png_set_compression_strategy(png_ptr, Z_DEFAULT_STRATEGY); png_set_compression_window_bits(png_ptr, 15); png_set_compression_method(png_ptr, 8); // set header png_set_IHDR(png_ptr, info_ptr, iWdt, iHgt, iBPC, iClrType, iIntrlcType, iCmprType, iFltrType); // double-check our calculated row size int iRealRowSize=png_get_rowbytes(png_ptr, info_ptr); if (iRealRowSize != iRowSize) { // this won't go well, so better abort Clear(); return false; } // write png header png_write_info(png_ptr, info_ptr); // image data is given as bgr... png_set_bgr(png_ptr); // create row array unsigned char **ppRowBuf = new unsigned char *[iHgt]; unsigned char **ppRows=ppRowBuf; unsigned char *pRow=pImageData; for (unsigned int i=0; i<iHgt; ++i,pRow+=iRowSize) *ppRows++=pRow; // write image png_write_image(png_ptr, ppRowBuf); // free row buffer delete [] ppRowBuf; // write end struct png_write_end(png_ptr, info_ptr); // finally, close the file fclose(fp); fp = NULL; // clear png structs ClearPngStructs(); // success! return true; }
/* * Encodes an image to a PNG file stream. */ int opng_encode_image(struct opng_codec_context *context, int filtered, FILE *stream, const char *fname, int level) { const char * volatile err_msg; /* volatile is required by cexcept */ context->libpng_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, opng_write_error, opng_write_warning); context->info_ptr = png_create_info_struct(context->libpng_ptr); if (!context->libpng_ptr || !context->info_ptr) { opng_error(0, "Out of memory"); png_destroy_write_struct(&context->libpng_ptr, &context->info_ptr); exit(1); } struct opng_encoding_stats * stats = context->stats; opng_init_stats(stats); context->stream = stream; context->fname = fname; Try { png_set_filter(context->libpng_ptr, PNG_FILTER_TYPE_BASE, filtered ? PNG_ALL_FILTERS : PNG_FILTER_NONE); if (level != 6) { png_set_compression_level(context->libpng_ptr, level); } png_set_compression_mem_level(context->libpng_ptr, 8); png_set_compression_window_bits(context->libpng_ptr, 15); png_set_compression_strategy(context->libpng_ptr, 0); png_set_keep_unknown_chunks(context->libpng_ptr, PNG_HANDLE_CHUNK_ALWAYS, 0, 0); opng_store_image(context->image, context->libpng_ptr, context->info_ptr); /* Write the PNG stream. */ png_set_write_fn(context->libpng_ptr, context, opng_write_data, 0); png_write_png(context->libpng_ptr, context->info_ptr, 0, 0); } Catch (err_msg) { stats->idat_size = OPTK_INT64_MAX; opng_error(fname, err_msg); return -1; } png_data_freer(context->libpng_ptr, context->info_ptr, PNG_USER_WILL_FREE_DATA, PNG_FREE_ALL); png_destroy_write_struct(&context->libpng_ptr, &context->info_ptr); return 0; }
void write_header( const View& view ) { using png_rw_info_t = detail::png_write_support < typename channel_type<typename get_pixel_type<View>::type>::type, typename color_space_type<View>::type >; // Set the image information here. Width and height are up to 2^31, // bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on // the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY, // PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB, // or PNG_COLOR_TYPE_RGB_ALPHA. interlace is either PNG_INTERLACE_NONE or // PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST // currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED png_set_IHDR( get_struct() , get_info() , static_cast< png_image_width::type >( view.width() ) , static_cast< png_image_height::type >( view.height() ) , static_cast< png_bitdepth::type >( png_rw_info_t::_bit_depth ) , static_cast< png_color_type::type >( png_rw_info_t::_color_type ) , _info._interlace_method , _info._compression_type , _info._filter_method ); #ifdef BOOST_GIL_IO_PNG_FLOATING_POINT_SUPPORTED if( _info._valid_cie_colors ) { png_set_cHRM( get_struct() , get_info() , _info._white_x , _info._white_y , _info._red_x , _info._red_y , _info._green_x , _info._green_y , _info._blue_x , _info._blue_y ); } if( _info._valid_file_gamma ) { png_set_gAMA( get_struct() , get_info() , _info._file_gamma ); } #else if( _info._valid_cie_colors ) { png_set_cHRM_fixed( get_struct() , get_info() , _info._white_x , _info._white_y , _info._red_x , _info._red_y , _info._green_x , _info._green_y , _info._blue_x , _info._blue_y ); } if( _info._valid_file_gamma ) { png_set_gAMA_fixed( get_struct() , get_info() , _info._file_gamma ); } #endif // BOOST_GIL_IO_PNG_FLOATING_POINT_SUPPORTED if( _info._valid_icc_profile ) { #if PNG_LIBPNG_VER_MINOR >= 5 png_set_iCCP( get_struct() , get_info() , const_cast< png_charp >( _info._icc_name.c_str() ) , _info._iccp_compression_type , reinterpret_cast< png_const_bytep >( & (_info._profile.front ()) ) , _info._profile_length ); #else png_set_iCCP( get_struct() , get_info() , const_cast< png_charp >( _info._icc_name.c_str() ) , _info._iccp_compression_type , const_cast< png_charp >( & (_info._profile.front()) ) , _info._profile_length ); #endif } if( _info._valid_intent ) { png_set_sRGB( get_struct() , get_info() , _info._intent ); } if( _info._valid_palette ) { png_set_PLTE( get_struct() , get_info() , const_cast< png_colorp >( &_info._palette.front() ) , _info._num_palette ); } if( _info._valid_background ) { png_set_bKGD( get_struct() , get_info() , const_cast< png_color_16p >( &_info._background ) ); } if( _info._valid_histogram ) { png_set_hIST( get_struct() , get_info() , const_cast< png_uint_16p >( &_info._histogram.front() ) ); } if( _info._valid_offset ) { png_set_oFFs( get_struct() , get_info() , _info._offset_x , _info._offset_y , _info._off_unit_type ); } if( _info._valid_pixel_calibration ) { std::vector< const char* > params( _info._num_params ); for( std::size_t i = 0; i < params.size(); ++i ) { params[i] = _info._params[ i ].c_str(); } png_set_pCAL( get_struct() , get_info() , const_cast< png_charp >( _info._purpose.c_str() ) , _info._X0 , _info._X1 , _info._cal_type , _info._num_params , const_cast< png_charp >( _info._units.c_str() ) , const_cast< png_charpp >( ¶ms.front() ) ); } if( _info._valid_resolution ) { png_set_pHYs( get_struct() , get_info() , _info._res_x , _info._res_y , _info._phy_unit_type ); } if( _info._valid_significant_bits ) { png_set_sBIT( get_struct() , get_info() , const_cast< png_color_8p >( &_info._sig_bits ) ); } #ifndef BOOST_GIL_IO_PNG_1_4_OR_LOWER #ifdef BOOST_GIL_IO_PNG_FLOATING_POINT_SUPPORTED if( _info._valid_scale_factors ) { png_set_sCAL( get_struct() , get_info() , this->_info._scale_unit , this->_info._scale_width , this->_info._scale_height ); } #else #ifdef BOOST_GIL_IO_PNG_FIXED_POINT_SUPPORTED if( _info._valid_scale_factors ) { png_set_sCAL_fixed( get_struct() , get_info() , this->_info._scale_unit , this->_info._scale_width , this->_info._scale_height ); } #else if( _info._valid_scale_factors ) { png_set_sCAL_s( get_struct() , get_info() , this->_info._scale_unit , const_cast< png_charp >( this->_info._scale_width.c_str() ) , const_cast< png_charp >( this->_info._scale_height.c_str() ) ); } #endif // BOOST_GIL_IO_PNG_FIXED_POINT_SUPPORTED #endif // BOOST_GIL_IO_PNG_FLOATING_POINT_SUPPORTED #endif // BOOST_GIL_IO_PNG_1_4_OR_LOWER if( _info._valid_text ) { std::vector< png_text > texts( _info._num_text ); for( std::size_t i = 0; i < texts.size(); ++i ) { png_text pt; pt.compression = _info._text[i]._compression; pt.key = const_cast< png_charp >( this->_info._text[i]._key.c_str() ); pt.text = const_cast< png_charp >( this->_info._text[i]._text.c_str() ); pt.text_length = _info._text[i]._text.length(); texts[i] = pt; } png_set_text( get_struct() , get_info() , &texts.front() , _info._num_text ); } if( _info._valid_modification_time ) { png_set_tIME( get_struct() , get_info() , const_cast< png_timep >( &_info._mod_time ) ); } if( _info._valid_transparency_factors ) { int sample_max = ( 1u << _info._bit_depth ); /* libpng doesn't reject a tRNS chunk with out-of-range samples */ if( !( ( _info._color_type == PNG_COLOR_TYPE_GRAY && (int) _info._trans_values[0].gray > sample_max ) || ( _info._color_type == PNG_COLOR_TYPE_RGB &&( (int) _info._trans_values[0].red > sample_max || (int) _info._trans_values[0].green > sample_max || (int) _info._trans_values[0].blue > sample_max ) ) ) ) { //@todo Fix that once reading transparency values works /* png_set_tRNS( get_struct() , get_info() , trans , num_trans , trans_values ); */ } } // Compression Levels - valid values are [0,9] png_set_compression_level( get_struct() , _info._compression_level ); png_set_compression_mem_level( get_struct() , _info._compression_mem_level ); png_set_compression_strategy( get_struct() , _info._compression_strategy ); png_set_compression_window_bits( get_struct() , _info._compression_window_bits ); png_set_compression_method( get_struct() , _info._compression_method ); png_set_compression_buffer_size( get_struct() , _info._compression_buffer_size ); #ifdef BOOST_GIL_IO_PNG_DITHERING_SUPPORTED // Dithering if( _info._set_dithering ) { png_set_dither( get_struct() , &_info._dithering_palette.front() , _info._dithering_num_palette , _info._dithering_maximum_colors , &_info._dithering_histogram.front() , _info._full_dither ); } #endif // BOOST_GIL_IO_PNG_DITHERING_SUPPORTED // Filter if( _info._set_filter ) { png_set_filter( get_struct() , 0 , _info._filter ); } // Invert Mono if( _info._invert_mono ) { png_set_invert_mono( get_struct() ); } // True Bits if( _info._set_true_bits ) { png_set_sBIT( get_struct() , get_info() , &_info._true_bits.front() ); } // sRGB Intent if( _info._set_srgb_intent ) { png_set_sRGB( get_struct() , get_info() , _info._srgb_intent ); } // Strip Alpha if( _info._strip_alpha ) { png_set_strip_alpha( get_struct() ); } // Swap Alpha if( _info._swap_alpha ) { png_set_swap_alpha( get_struct() ); } png_write_info( get_struct() , get_info() ); }
BOOL CScreenCapture::PngInit(int nWidth, int nHeight, BOOL bGrayscale, CString sFileName) { m_fp = NULL; m_png_ptr = NULL; m_info_ptr = NULL; #if _MSC_VER>=1400 _tfopen_s(&m_fp, sFileName.GetBuffer(0), _T("wb")); #else m_fp = _tfopen(sFileName.GetBuffer(0), _T("wb")); #endif if (!m_fp) { return FALSE; } m_png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, NULL, NULL); if (!m_png_ptr) return FALSE; m_info_ptr = png_create_info_struct(m_png_ptr); if (!m_info_ptr) { png_destroy_write_struct(&m_png_ptr, (png_infopp)NULL); return FALSE; } /* Error handler*/ if (setjmp(png_jmpbuf(m_png_ptr))) { png_destroy_write_struct(&m_png_ptr, &m_info_ptr); fclose(m_fp); return FALSE; } png_init_io(m_png_ptr, m_fp); /* set the zlib compression level */ png_set_compression_level(m_png_ptr, Z_BEST_COMPRESSION); /* set other zlib parameters */ png_set_compression_mem_level(m_png_ptr, 8); png_set_compression_strategy(m_png_ptr, Z_DEFAULT_STRATEGY); png_set_compression_window_bits(m_png_ptr, 15); png_set_compression_method(m_png_ptr, 8); png_set_compression_buffer_size(m_png_ptr, 8192); png_set_IHDR( m_png_ptr, m_info_ptr, nWidth, //width, nHeight, //height, 8, // bit_depth bGrayscale?PNG_COLOR_TYPE_GRAY:PNG_COLOR_TYPE_RGB, // color_type PNG_INTERLACE_NONE, // interlace_type PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); png_set_bgr(m_png_ptr); /* write the file information */ png_write_info(m_png_ptr, m_info_ptr); return TRUE; }
bool wxPNGHandler::SaveFile( wxImage *image, wxOutputStream& stream, bool verbose ) { wxPNGInfoStruct wxinfo; wxinfo.verbose = verbose; wxinfo.stream.out = &stream; png_structp png_ptr = png_create_write_struct ( PNG_LIBPNG_VER_STRING, NULL, wx_PNG_error, wx_PNG_warning ); if (!png_ptr) { if (verbose) { wxLogError(_("Couldn't save PNG image.")); } return false; } png_infop info_ptr = png_create_info_struct(png_ptr); if (info_ptr == NULL) { png_destroy_write_struct( &png_ptr, (png_infopp)NULL ); if (verbose) { wxLogError(_("Couldn't save PNG image.")); } return false; } if (setjmp(wxinfo.jmpbuf)) { png_destroy_write_struct( &png_ptr, (png_infopp)NULL ); if (verbose) { wxLogError(_("Couldn't save PNG image.")); } return false; } // NB: please see the comment near wxPNGInfoStruct declaration for // explanation why this line is mandatory png_set_write_fn( png_ptr, &wxinfo, wx_PNG_stream_writer, NULL); const int iHeight = image->GetHeight(); const int iWidth = image->GetWidth(); const bool bHasPngFormatOption = image->HasOption(wxIMAGE_OPTION_PNG_FORMAT); int iColorType = bHasPngFormatOption ? image->GetOptionInt(wxIMAGE_OPTION_PNG_FORMAT) : wxPNG_TYPE_COLOUR; bool bHasAlpha = image->HasAlpha(); bool bHasMask = image->HasMask(); bool bUsePalette = iColorType == wxPNG_TYPE_PALETTE #if wxUSE_PALETTE || (!bHasPngFormatOption && image->HasPalette() ) #endif ; png_color_8 mask = { 0, 0, 0, 0, 0 }; if (bHasMask) { mask.red = image->GetMaskRed(); mask.green = image->GetMaskGreen(); mask.blue = image->GetMaskBlue(); } PaletteMap palette; if (bUsePalette) { png_color png_rgb [PNG_MAX_PALETTE_LENGTH]; png_byte png_trans[PNG_MAX_PALETTE_LENGTH]; const unsigned char *pColors = image->GetData(); const unsigned char* pAlpha = image->GetAlpha(); if (bHasMask && !pAlpha) { // Mask must be first PaletteAdd(&palette, mask); } for (int y = 0; y < iHeight; y++) { for (int x = 0; x < iWidth; x++) { png_color_8 rgba; rgba.red = *pColors++; rgba.green = *pColors++; rgba.blue = *pColors++; rgba.gray = 0; rgba.alpha = (pAlpha && !bHasMask) ? *pAlpha++ : 0; // save in our palette long index = PaletteAdd(&palette, rgba); if (index < PNG_MAX_PALETTE_LENGTH) { // save in libpng's palette png_rgb[index].red = rgba.red; png_rgb[index].green = rgba.green; png_rgb[index].blue = rgba.blue; png_trans[index] = rgba.alpha; } else { bUsePalette = false; break; } } } if (bUsePalette) { png_set_PLTE(png_ptr, info_ptr, png_rgb, palette.size()); if (bHasMask && !pAlpha) { wxASSERT(PaletteFind(palette, mask) == 0); png_trans[0] = 0; png_set_tRNS(png_ptr, info_ptr, png_trans, 1, NULL); } else if (pAlpha && !bHasMask) { png_set_tRNS(png_ptr, info_ptr, png_trans, palette.size(), NULL); } } } /* If saving palettised was requested but it was decided we can't use a palette then reset the colour type to RGB. */ if (!bUsePalette && iColorType == wxPNG_TYPE_PALETTE) { iColorType = wxPNG_TYPE_COLOUR; } bool bUseAlpha = !bUsePalette && (bHasAlpha || bHasMask); int iPngColorType; if (bUsePalette) { iPngColorType = PNG_COLOR_TYPE_PALETTE; iColorType = wxPNG_TYPE_PALETTE; } else if ( iColorType==wxPNG_TYPE_COLOUR ) { iPngColorType = bUseAlpha ? PNG_COLOR_TYPE_RGB_ALPHA : PNG_COLOR_TYPE_RGB; } else { iPngColorType = bUseAlpha ? PNG_COLOR_TYPE_GRAY_ALPHA : PNG_COLOR_TYPE_GRAY; } if (image->HasOption(wxIMAGE_OPTION_PNG_FILTER)) png_set_filter( png_ptr, PNG_FILTER_TYPE_BASE, image->GetOptionInt(wxIMAGE_OPTION_PNG_FILTER) ); if (image->HasOption(wxIMAGE_OPTION_PNG_COMPRESSION_LEVEL)) png_set_compression_level( png_ptr, image->GetOptionInt(wxIMAGE_OPTION_PNG_COMPRESSION_LEVEL) ); if (image->HasOption(wxIMAGE_OPTION_PNG_COMPRESSION_MEM_LEVEL)) png_set_compression_mem_level( png_ptr, image->GetOptionInt(wxIMAGE_OPTION_PNG_COMPRESSION_MEM_LEVEL) ); if (image->HasOption(wxIMAGE_OPTION_PNG_COMPRESSION_STRATEGY)) png_set_compression_strategy( png_ptr, image->GetOptionInt(wxIMAGE_OPTION_PNG_COMPRESSION_STRATEGY) ); if (image->HasOption(wxIMAGE_OPTION_PNG_COMPRESSION_BUFFER_SIZE)) png_set_compression_buffer_size( png_ptr, image->GetOptionInt(wxIMAGE_OPTION_PNG_COMPRESSION_BUFFER_SIZE) ); int iBitDepth = !bUsePalette && image->HasOption(wxIMAGE_OPTION_PNG_BITDEPTH) ? image->GetOptionInt(wxIMAGE_OPTION_PNG_BITDEPTH) : 8; png_set_IHDR( png_ptr, info_ptr, image->GetWidth(), image->GetHeight(), iBitDepth, iPngColorType, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); int iElements; png_color_8 sig_bit; if ( iPngColorType & PNG_COLOR_MASK_COLOR ) { sig_bit.red = sig_bit.green = sig_bit.blue = (png_byte)iBitDepth; iElements = 3; } else // grey { sig_bit.gray = (png_byte)iBitDepth; iElements = 1; } if ( bUseAlpha ) { sig_bit.alpha = (png_byte)iBitDepth; iElements++; } if ( iBitDepth == 16 ) iElements *= 2; // save the image resolution if we have it int resX, resY; switch ( GetResolutionFromOptions(*image, &resX, &resY) ) { case wxIMAGE_RESOLUTION_INCHES: { const double INCHES_IN_METER = 10000.0 / 254; resX = int(resX * INCHES_IN_METER); resY = int(resY * INCHES_IN_METER); } break; case wxIMAGE_RESOLUTION_CM: resX *= 100; resY *= 100; break; case wxIMAGE_RESOLUTION_NONE: break; default: wxFAIL_MSG( wxT("unsupported image resolution units") ); } if ( resX && resY ) png_set_pHYs( png_ptr, info_ptr, resX, resY, PNG_RESOLUTION_METER ); png_set_sBIT( png_ptr, info_ptr, &sig_bit ); png_write_info( png_ptr, info_ptr ); png_set_shift( png_ptr, &sig_bit ); png_set_packing( png_ptr ); unsigned char * data = (unsigned char *)malloc( image->GetWidth() * iElements ); if ( !data ) { png_destroy_write_struct( &png_ptr, (png_infopp)NULL ); return false; } const unsigned char * pAlpha = (const unsigned char *)(bHasAlpha ? image->GetAlpha() : NULL); const unsigned char *pColors = image->GetData(); for (int y = 0; y != iHeight; ++y) { unsigned char *pData = data; for (int x = 0; x != iWidth; x++) { png_color_8 clr; clr.red = *pColors++; clr.green = *pColors++; clr.blue = *pColors++; clr.gray = 0; clr.alpha = (bUsePalette && pAlpha) ? *pAlpha++ : 0; // use with wxPNG_TYPE_PALETTE only switch ( iColorType ) { default: wxFAIL_MSG( wxT("unknown wxPNG_TYPE_XXX") ); // fall through case wxPNG_TYPE_COLOUR: *pData++ = clr.red; if ( iBitDepth == 16 ) *pData++ = 0; *pData++ = clr.green; if ( iBitDepth == 16 ) *pData++ = 0; *pData++ = clr.blue; if ( iBitDepth == 16 ) *pData++ = 0; break; case wxPNG_TYPE_GREY: { // where do these coefficients come from? maybe we // should have image options for them as well? unsigned uiColor = (unsigned) (76.544*(unsigned)clr.red + 150.272*(unsigned)clr.green + 36.864*(unsigned)clr.blue); *pData++ = (unsigned char)((uiColor >> 8) & 0xFF); if ( iBitDepth == 16 ) *pData++ = (unsigned char)(uiColor & 0xFF); } break; case wxPNG_TYPE_GREY_RED: *pData++ = clr.red; if ( iBitDepth == 16 ) *pData++ = 0; break; case wxPNG_TYPE_PALETTE: *pData++ = (unsigned char) PaletteFind(palette, clr); break; } if ( bUseAlpha ) { unsigned char uchAlpha = 255; if ( bHasAlpha ) uchAlpha = *pAlpha++; if ( bHasMask ) { if ( (clr.red == mask.red) && (clr.green == mask.green) && (clr.blue == mask.blue) ) uchAlpha = 0; } *pData++ = uchAlpha; if ( iBitDepth == 16 ) *pData++ = 0; } } png_bytep row_ptr = data; png_write_rows( png_ptr, &row_ptr, 1 ); } free(data); png_write_end( png_ptr, info_ptr ); png_destroy_write_struct( &png_ptr, (png_infopp)&info_ptr ); return true; }
bool PngEncoder::write( const Mat& img, const vector<int>& params ) { int compression_level = 0; for( size_t i = 0; i < params.size(); i += 2 ) { if( params[i] == CV_IMWRITE_PNG_COMPRESSION ) { compression_level = params[i+1]; compression_level = MIN(MAX(compression_level, 0), MAX_MEM_LEVEL); } } png_structp png_ptr = png_create_write_struct( PNG_LIBPNG_VER_STRING, 0, 0, 0 ); png_infop info_ptr = 0; FILE* f = 0; int y, width = img.cols, height = img.rows; int depth = img.depth(), channels = img.channels(); bool result = false; AutoBuffer<uchar*> buffer; if( depth != CV_8U && depth != CV_16U ) return false; if( png_ptr ) { info_ptr = png_create_info_struct( png_ptr ); if( info_ptr ) { if( setjmp( png_ptr->jmpbuf ) == 0 ) { if( m_buf ) { png_set_write_fn(png_ptr, this, (png_rw_ptr)writeDataToBuf, (png_flush_ptr)flushBuf); } else { f = fopen( m_filename.c_str(), "wb" ); if( f ) png_init_io( png_ptr, f ); } if( m_buf || f ) { if( compression_level > 0 ) { png_set_compression_mem_level( png_ptr, compression_level ); } else { // tune parameters for speed // (see http://wiki.linuxquestions.org/wiki/Libpng) png_set_filter(png_ptr, PNG_FILTER_TYPE_BASE, PNG_FILTER_SUB); png_set_compression_level(png_ptr, Z_BEST_SPEED); } png_set_compression_strategy(png_ptr, Z_HUFFMAN_ONLY); png_set_IHDR( png_ptr, info_ptr, width, height, depth == CV_8U ? 8 : 16, channels == 1 ? PNG_COLOR_TYPE_GRAY : channels == 3 ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGBA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT ); png_write_info( png_ptr, info_ptr ); png_set_bgr( png_ptr ); if( !isBigEndian() ) png_set_swap( png_ptr ); buffer.allocate(height); for( y = 0; y < height; y++ ) buffer[y] = img.data + y*img.step; png_write_image( png_ptr, buffer ); png_write_end( png_ptr, info_ptr ); result = true; } } } } png_destroy_write_struct( &png_ptr, &info_ptr ); if(f) fclose( f ); return result; }
bool wxPNGHandler::SaveFile( wxImage *image, wxOutputStream& stream, bool verbose ) { wxPNGInfoStruct wxinfo; wxinfo.verbose = verbose; wxinfo.stream.out = &stream; png_structp png_ptr = png_create_write_struct ( PNG_LIBPNG_VER_STRING, NULL, wx_png_error, wx_png_warning ); if (!png_ptr) { if (verbose) { wxLogError(_("Couldn't save PNG image.")); } return false; } png_infop info_ptr = png_create_info_struct(png_ptr); if (info_ptr == NULL) { png_destroy_write_struct( &png_ptr, (png_infopp)NULL ); if (verbose) { wxLogError(_("Couldn't save PNG image.")); } return false; } if (setjmp(wxinfo.jmpbuf)) { png_destroy_write_struct( &png_ptr, (png_infopp)NULL ); if (verbose) { wxLogError(_("Couldn't save PNG image.")); } return false; } // NB: please see the comment near wxPNGInfoStruct declaration for // explanation why this line is mandatory png_set_write_fn( png_ptr, &wxinfo, wx_PNG_stream_writer, NULL); const int iColorType = image->HasOption(wxIMAGE_OPTION_PNG_FORMAT) ? image->GetOptionInt(wxIMAGE_OPTION_PNG_FORMAT) : wxPNG_TYPE_COLOUR; const int iBitDepth = image->HasOption(wxIMAGE_OPTION_PNG_BITDEPTH) ? image->GetOptionInt(wxIMAGE_OPTION_PNG_BITDEPTH) : 8; bool bHasAlpha = image->HasAlpha(); bool bHasMask = image->HasMask(); bool bUseAlpha = bHasAlpha || bHasMask; int iPngColorType; if ( iColorType==wxPNG_TYPE_COLOUR ) { iPngColorType = bUseAlpha ? PNG_COLOR_TYPE_RGB_ALPHA : PNG_COLOR_TYPE_RGB; } else { iPngColorType = bUseAlpha ? PNG_COLOR_TYPE_GRAY_ALPHA : PNG_COLOR_TYPE_GRAY; } if (image->HasOption(wxIMAGE_OPTION_PNG_FILTER)) png_set_filter( png_ptr, PNG_FILTER_TYPE_BASE, image->GetOptionInt(wxIMAGE_OPTION_PNG_FILTER) ); if (image->HasOption(wxIMAGE_OPTION_PNG_COMPRESSION_LEVEL)) png_set_compression_level( png_ptr, image->GetOptionInt(wxIMAGE_OPTION_PNG_COMPRESSION_LEVEL) ); if (image->HasOption(wxIMAGE_OPTION_PNG_COMPRESSION_MEM_LEVEL)) png_set_compression_mem_level( png_ptr, image->GetOptionInt(wxIMAGE_OPTION_PNG_COMPRESSION_MEM_LEVEL) ); if (image->HasOption(wxIMAGE_OPTION_PNG_COMPRESSION_STRATEGY)) png_set_compression_strategy( png_ptr, image->GetOptionInt(wxIMAGE_OPTION_PNG_COMPRESSION_STRATEGY) ); if (image->HasOption(wxIMAGE_OPTION_PNG_COMPRESSION_BUFFER_SIZE)) png_set_compression_buffer_size( png_ptr, image->GetOptionInt(wxIMAGE_OPTION_PNG_COMPRESSION_BUFFER_SIZE) ); png_set_IHDR( png_ptr, info_ptr, image->GetWidth(), image->GetHeight(), iBitDepth, iPngColorType, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); int iElements; png_color_8 sig_bit; if ( iPngColorType & PNG_COLOR_MASK_COLOR ) { sig_bit.red = sig_bit.green = sig_bit.blue = (png_byte)iBitDepth; iElements = 3; } else // grey { sig_bit.gray = (png_byte)iBitDepth; iElements = 1; } if ( iPngColorType & PNG_COLOR_MASK_ALPHA ) { sig_bit.alpha = (png_byte)iBitDepth; iElements++; } if ( iBitDepth == 16 ) iElements *= 2; // save the image resolution if we have it int resX, resY; switch ( GetResolutionFromOptions(*image, &resX, &resY) ) { case wxIMAGE_RESOLUTION_INCHES: { const double INCHES_IN_METER = 10000.0 / 254; resX = int(resX * INCHES_IN_METER); resY = int(resY * INCHES_IN_METER); } break; case wxIMAGE_RESOLUTION_CM: resX *= 100; resY *= 100; break; case wxIMAGE_RESOLUTION_NONE: break; default: wxFAIL_MSG( wxT("unsupported image resolution units") ); } if ( resX && resY ) png_set_pHYs( png_ptr, info_ptr, resX, resY, PNG_RESOLUTION_METER ); png_set_sBIT( png_ptr, info_ptr, &sig_bit ); png_write_info( png_ptr, info_ptr ); png_set_shift( png_ptr, &sig_bit ); png_set_packing( png_ptr ); unsigned char * data = (unsigned char *)malloc( image->GetWidth() * iElements ); if ( !data ) { png_destroy_write_struct( &png_ptr, (png_infopp)NULL ); return false; } unsigned char * pAlpha = (unsigned char *)(bHasAlpha ? image->GetAlpha() : NULL); int iHeight = image->GetHeight(); int iWidth = image->GetWidth(); unsigned char uchMaskRed = 0, uchMaskGreen = 0, uchMaskBlue = 0; if ( bHasMask ) { uchMaskRed = image->GetMaskRed(); uchMaskGreen = image->GetMaskGreen(); uchMaskBlue = image->GetMaskBlue(); } unsigned char *pColors = image->GetData(); for (int y = 0; y != iHeight; ++y) { unsigned char *pData = data; for (int x = 0; x != iWidth; x++) { unsigned char uchRed = *pColors++; unsigned char uchGreen = *pColors++; unsigned char uchBlue = *pColors++; switch ( iColorType ) { default: wxFAIL_MSG( wxT("unknown wxPNG_TYPE_XXX") ); // fall through case wxPNG_TYPE_COLOUR: *pData++ = uchRed; if ( iBitDepth == 16 ) *pData++ = 0; *pData++ = uchGreen; if ( iBitDepth == 16 ) *pData++ = 0; *pData++ = uchBlue; if ( iBitDepth == 16 ) *pData++ = 0; break; case wxPNG_TYPE_GREY: { // where do these coefficients come from? maybe we // should have image options for them as well? unsigned uiColor = (unsigned) (76.544*(unsigned)uchRed + 150.272*(unsigned)uchGreen + 36.864*(unsigned)uchBlue); *pData++ = (unsigned char)((uiColor >> 8) & 0xFF); if ( iBitDepth == 16 ) *pData++ = (unsigned char)(uiColor & 0xFF); } break; case wxPNG_TYPE_GREY_RED: *pData++ = uchRed; if ( iBitDepth == 16 ) *pData++ = 0; break; } if ( bUseAlpha ) { unsigned char uchAlpha = 255; if ( bHasAlpha ) uchAlpha = *pAlpha++; if ( bHasMask ) { if ( (uchRed == uchMaskRed) && (uchGreen == uchMaskGreen) && (uchBlue == uchMaskBlue) ) uchAlpha = 0; } *pData++ = uchAlpha; if ( iBitDepth == 16 ) *pData++ = 0; } } png_bytep row_ptr = data; png_write_rows( png_ptr, &row_ptr, 1 ); } free(data); png_write_end( png_ptr, info_ptr ); png_destroy_write_struct( &png_ptr, (png_infopp)&info_ptr ); return true; }
void CAPTURE_AddImage(Bitu width, Bitu height, Bitu bpp, Bitu pitch, Bitu flags, float fps, Bit8u * data, Bit8u * pal) { #if (C_SSHOT) Bitu i; Bit8u doubleRow[SCALER_MAXWIDTH*4]; Bitu countWidth = width; if (flags & CAPTURE_FLAG_DBLH) height *= 2; if (flags & CAPTURE_FLAG_DBLW) width *= 2; if (height > SCALER_MAXHEIGHT) return; if (width > SCALER_MAXWIDTH) return; if (CaptureState & CAPTURE_IMAGE) { png_structp png_ptr; png_infop info_ptr; png_color palette[256]; CaptureState &= ~CAPTURE_IMAGE; /* Open the actual file */ FILE * fp=OpenCaptureFile("Screenshot",".png"); if (!fp) goto skip_shot; /* First try to allocate the png structures */ png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL,NULL, NULL); if (!png_ptr) goto skip_shot; info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_write_struct(&png_ptr,(png_infopp)NULL); goto skip_shot; } /* Finalize the initing of png library */ png_init_io(png_ptr, fp); png_set_compression_level(png_ptr,Z_BEST_COMPRESSION); /* set other zlib parameters */ png_set_compression_mem_level(png_ptr, 8); png_set_compression_strategy(png_ptr,Z_DEFAULT_STRATEGY); png_set_compression_window_bits(png_ptr, 15); png_set_compression_method(png_ptr, 8); png_set_compression_buffer_size(png_ptr, 8192); if (bpp==8) { png_set_IHDR(png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); for (i=0;i<256;i++) { palette[i].red=pal[i*4+0]; palette[i].green=pal[i*4+1]; palette[i].blue=pal[i*4+2]; } png_set_PLTE(png_ptr, info_ptr, palette,256); } else { png_set_bgr( png_ptr ); png_set_IHDR(png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); } #ifdef PNG_TEXT_SUPPORTED int fields = 1; png_text text[1]; const char* text_s = "DOSBox " VERSION; size_t strl = strlen(text_s); char* ptext_s = new char[strl + 1]; strcpy(ptext_s, text_s); char software[9] = { 'S','o','f','t','w','a','r','e',0}; text[0].compression = PNG_TEXT_COMPRESSION_NONE; text[0].key = software; text[0].text = ptext_s; png_set_text(png_ptr, info_ptr, text, fields); #endif png_write_info(png_ptr, info_ptr); #ifdef PNG_TEXT_SUPPORTED delete [] ptext_s; #endif for (i=0;i<height;i++) { void *rowPointer; void *srcLine; if (flags & CAPTURE_FLAG_DBLH) srcLine=(data+(i >> 1)*pitch); else srcLine=(data+(i >> 0)*pitch); rowPointer=srcLine; switch (bpp) { case 8: if (flags & CAPTURE_FLAG_DBLW) { for (Bitu x=0;x<countWidth;x++) doubleRow[x*2+0] = doubleRow[x*2+1] = ((Bit8u *)srcLine)[x]; rowPointer = doubleRow; } break; case 15: if (flags & CAPTURE_FLAG_DBLW) { for (Bitu x=0;x<countWidth;x++) { Bitu pixel = ((Bit16u *)srcLine)[x]; doubleRow[x*6+0] = doubleRow[x*6+3] = ((pixel& 0x001f) * 0x21) >> 2; doubleRow[x*6+1] = doubleRow[x*6+4] = ((pixel& 0x03e0) * 0x21) >> 7; doubleRow[x*6+2] = doubleRow[x*6+5] = ((pixel& 0x7c00) * 0x21) >> 12; } } else { for (Bitu x=0;x<countWidth;x++) { Bitu pixel = ((Bit16u *)srcLine)[x]; doubleRow[x*3+0] = ((pixel& 0x001f) * 0x21) >> 2; doubleRow[x*3+1] = ((pixel& 0x03e0) * 0x21) >> 7; doubleRow[x*3+2] = ((pixel& 0x7c00) * 0x21) >> 12; } } rowPointer = doubleRow; break; case 16: if (flags & CAPTURE_FLAG_DBLW) { for (Bitu x=0;x<countWidth;x++) { Bitu pixel = ((Bit16u *)srcLine)[x]; doubleRow[x*6+0] = doubleRow[x*6+3] = ((pixel& 0x001f) * 0x21) >> 2; doubleRow[x*6+1] = doubleRow[x*6+4] = ((pixel& 0x07e0) * 0x41) >> 9; doubleRow[x*6+2] = doubleRow[x*6+5] = ((pixel& 0xf800) * 0x21) >> 13; } } else { for (Bitu x=0;x<countWidth;x++) {
bool GrFmtPngWriter::WriteImage( const uchar* data, int step, int width, int height, int depth, int channels ) { png_structp png_ptr = png_create_write_struct( PNG_LIBPNG_VER_STRING, 0, 0, 0 ); png_infop info_ptr = 0; FILE* f = 0; uchar** buffer = 0; int y; bool result = false; if( depth != IPL_DEPTH_8U && depth != IPL_DEPTH_16U ) return false; if( png_ptr ) { info_ptr = png_create_info_struct( png_ptr ); if( info_ptr ) { if( setjmp( png_ptr->jmpbuf ) == 0 ) { f = fopen( m_filename, "wb" ); if( f ) { png_init_io( png_ptr, f ); png_set_compression_mem_level( png_ptr, MAX_MEM_LEVEL ); png_set_IHDR( png_ptr, info_ptr, width, height, depth, channels == 1 ? PNG_COLOR_TYPE_GRAY : channels == 3 ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGBA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT ); png_write_info( png_ptr, info_ptr ); png_set_bgr( png_ptr ); if( !isBigEndian() ) png_set_swap( png_ptr ); buffer = new uchar*[height]; for( y = 0; y < height; y++ ) buffer[y] = (uchar*)(data + y*step); png_write_image( png_ptr, buffer ); png_write_end( png_ptr, info_ptr ); delete[] buffer; result = true; } } } } png_destroy_write_struct( &png_ptr, &info_ptr ); if(f) fclose( f ); return result; }
unsigned int __stdcall png_write_thread (byte *buffer) { char picname[MAX_OSPATH]; char checkname[MAX_OSPATH]; int i; FILE *f; png_structp png_ptr; png_infop info_ptr; unsigned k; png_bytepp row_pointers; // create the scrnshots directory if it doesn't exist Com_sprintf (checkname, sizeof(checkname), "%s/scrnshot/", ri.FS_Gamedir()); FS_CreatePath (checkname); for (i = 0; i < 999; i++) { sprintf (picname, "%s/scrnshot/quake%.3d.png", ri.FS_Gamedir(), i); f = fopen (picname, "rb"); if (!f) break; fclose (f); } f = fopen (picname, "wb"); if (!f) { ri.Con_Printf (PRINT_ALL, "Couldn't open %s for writing.\n", picname); #ifdef USE_THREADS ExitThread (1); #else return 1; #endif } png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!png_ptr) { ri.Con_Printf (PRINT_ALL, "libpng error\n", picname); #ifdef USE_THREADS ExitThread (1); #else return 1; #endif } info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_write_struct(&png_ptr, (png_infopp)NULL); ri.Con_Printf (PRINT_ALL, "libpng error\n", picname); #ifdef USE_THREADS ExitThread (1); #else return 1; #endif } png_init_io(png_ptr, f); png_set_IHDR(png_ptr, info_ptr, vid.width, vid.height, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); png_set_compression_level(png_ptr, Z_DEFAULT_COMPRESSION); png_set_compression_mem_level(png_ptr, 9); png_set_compression_buffer_size(png_ptr, vid.width * vid.height * 3); png_write_info(png_ptr, info_ptr); row_pointers = malloc(vid.height * sizeof(png_bytep)); if (!row_pointers) ri.Sys_Error (ERR_FATAL, "png_write_thread: out of memory"); for (k = 0; k < vid.height; k++) row_pointers[k] = buffer + (vid.height-1-k)*3*vid.width; png_write_image(png_ptr, row_pointers); png_write_end(png_ptr, info_ptr); png_destroy_write_struct(&png_ptr, &info_ptr); fclose (f); free (buffer); free (row_pointers); ri.Con_Printf (PRINT_ALL, "Finished, wrote %s\n", picname); #ifdef USE_THREADS ExitThread (0); #endif return 0; }