static int png_write(FILE *fp, struct ida_image *img) { png_structp png_ptr = NULL; png_infop info_ptr = NULL; png_bytep row; unsigned int y; /* Create and initialize the png_struct with the desired error handler * functions. If you want to use the default stderr and longjump method, * you can supply NULL for the last three parameters. We also check that * the library version is compatible with the one used at compile time, * in case we are using dynamically linked libraries. REQUIRED. */ png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,NULL,NULL,NULL); if (png_ptr == NULL) goto oops; /* Allocate/initialize the image information data. REQUIRED */ info_ptr = png_create_info_struct(png_ptr); if (info_ptr == NULL) goto oops; if (setjmp(png_jmpbuf(png_ptr))) goto oops; png_init_io(png_ptr, fp); png_set_IHDR(png_ptr, info_ptr, img->i.width, img->i.height, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); if (img->i.dpi) { png_set_pHYs(png_ptr, info_ptr, res_inch_to_m(img->i.dpi), res_inch_to_m(img->i.dpi), PNG_RESOLUTION_METER); } png_write_info(png_ptr, info_ptr); png_set_packing(png_ptr); for (y = 0; y < img->i.height; y++) { row = img->data + y * 3 * img->i.width; png_write_rows(png_ptr, &row, 1); } png_write_end(png_ptr, info_ptr); png_destroy_write_struct(&png_ptr, &info_ptr); return 0; oops: fprintf(stderr,"can't save image: libpng error\n"); if (png_ptr) png_destroy_write_struct(&png_ptr, (png_infopp)NULL); return -1; }
/* Store the format name. */ if (fmt_name != NULL) *fmt_name = gif_fmt_name; if (fmt_description != NULL) *fmt_description = gif_fmt_description; return 1; /* GIF */ } static void pngx_set_GIF_palette(png_structp png_ptr, png_infop info_ptr, unsigned char *color_table, unsigned int num_colors) { png_color palette[256]; unsigned int i; PNGX_ASSERT(color_table != NULL && num_colors <= 256); for (i = 0; i < num_colors; ++i) { palette[i].red = color_table[3 * i]; palette[i].green = color_table[3 * i + 1]; palette[i].blue = color_table[3 * i + 2]; } png_set_PLTE(png_ptr, info_ptr, palette, (int)num_colors); } static void pngx_set_GIF_transparent(png_structp png_ptr, png_infop info_ptr, unsigned int transparent) { png_byte trans[256]; unsigned int i; PNGX_ASSERT(transparent < 256); for (i = 0; i < transparent; ++i) trans[i] = 255; trans[transparent] = 0; png_set_tRNS(png_ptr, info_ptr, trans, (int)transparent + 1, NULL); } #if 0 /* ... need to implement ... */ static void pngx_set_GIF_meta(png_structp png_ptr, png_infop info_ptr, struct GIFImage *image, struct GIFExtension *ext) { /* If the GIF specifies an aspect ratio, turn it into a pHYs chunk. */ if (GifScreen.AspectRatio != 0 && GifScreen.AspectRatio != 49) png_set_pHYs(png_ptr, info_ptr, GifScreen.AspectRatio+15, 64, PNG_RESOLUTION_UNKNOWN); /* If the GIF specifies an image offset, turn it into a oFFs chunk. */ if (img->offset_x > 0 && img->offset_y > 0) png_set_oFFs(png_ptr, info_ptr, img->offset_x, img->offset_y, PNG_OFFSET_PIXEL); }
void open_png(void) { png_ptr=png_create_write_struct(PNG_LIBPNG_VER_STRING,NULL,NULL,NULL); info_ptr=png_create_info_struct(png_ptr); png_init_io(png_ptr,of); png_set_filter(png_ptr,0,PNG_FILTER_NONE|PNG_FILTER_SUB|PNG_FILTER_UP |PNG_FILTER_AVG|PNG_FILTER_PAETH); png_set_compression_level(png_ptr,Z_BEST_COMPRESSION); png_set_IHDR(png_ptr,info_ptr,ox,oy,8,PNG_COLOR_TYPE_GRAY,PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,PNG_FILTER_TYPE_DEFAULT); png_set_gAMA(png_ptr,info_ptr,1.0); png_set_pHYs(png_ptr,info_ptr,ppm,ppm,PNG_RESOLUTION_METER); png_write_info(png_ptr,info_ptr); }
//=================================================================== // save_png //=================================================================== int save_png(PKLImage pkl, FILE *image) { png_structp png_ptr; png_infop info_ptr; int i; if(pkl->color!=PKL_RGB && pkl->color!=PKL_GRAYSCALE) return(1); png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if(!png_ptr) return(1); info_ptr = png_create_info_struct(png_ptr); if(!info_ptr){ png_destroy_write_struct(&png_ptr, (png_infopp)NULL); return(1); } if( setjmp(png_jmpbuf(png_ptr)) ){ png_destroy_info_struct(png_ptr, &info_ptr); png_destroy_write_struct(&png_ptr, &info_ptr); return(1); } png_init_io(png_ptr, image); info_ptr->width = pkl->width; info_ptr->height = pkl->height; info_ptr->bit_depth = pkl_png_depth(pkl->color); info_ptr->color_type = color_pk2pn(pkl->color); png_set_pHYs(png_ptr, info_ptr, (int)(pkl->resh*10000.0/254.0), (int)(pkl->resv*10000.0/254.0), PNG_RESOLUTION_METER); png_set_compression_level(png_ptr, compress_pk2pn(pkl->compress)); png_write_info(png_ptr, info_ptr); for(i=0; i<pkl->height; i++) png_write_row(png_ptr, &pkl->image[i*pkl->width*pkl->channel]); png_write_end(png_ptr, info_ptr); png_destroy_info_struct(png_ptr, &info_ptr); png_destroy_write_struct(&png_ptr, &info_ptr); return(0); }
static bool sp_png_write_rgba_striped(SPDocument *doc, gchar const *filename, unsigned long int width, unsigned long int height, double xdpi, double ydpi, int (* get_rows)(guchar const **rows, void **to_free, int row, int num_rows, void *data), void *data) { struct SPEBP *ebp = (struct SPEBP *) data; FILE *fp; png_structp png_ptr; png_infop info_ptr; png_color_8 sig_bit; png_uint_32 r; g_return_val_if_fail(filename != NULL, false); g_return_val_if_fail(data != NULL, false); /* open the file */ Inkscape::IO::dump_fopen_call(filename, "M"); fp = Inkscape::IO::fopen_utf8name(filename, "wb"); g_return_val_if_fail(fp != NULL, false); /* Create and initialize the png_struct with the desired error handler * functions. If you want to use the default stderr and longjump method, * you can supply NULL for the last three parameters. We also check that * the library version is compatible with the one used at compile time, * in case we are using dynamically linked libraries. REQUIRED. */ png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (png_ptr == NULL) { fclose(fp); return false; } /* Allocate/initialize the image information data. REQUIRED */ info_ptr = png_create_info_struct(png_ptr); if (info_ptr == NULL) { fclose(fp); png_destroy_write_struct(&png_ptr, NULL); return false; } /* Set error handling. REQUIRED if you aren't supplying your own * error hadnling functions in the png_create_write_struct() call. */ if (setjmp(png_jmpbuf(png_ptr))) { // If we get here, we had a problem reading the file fclose(fp); png_destroy_write_struct(&png_ptr, &info_ptr); return false; } /* set up the output control if you are using standard C streams */ png_init_io(png_ptr, fp); /* 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(png_ptr, info_ptr, width, height, 8, /* bit_depth */ PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); /* otherwise, if we are dealing with a color image then */ sig_bit.red = 8; sig_bit.green = 8; sig_bit.blue = 8; /* if the image has an alpha channel then */ sig_bit.alpha = 8; png_set_sBIT(png_ptr, info_ptr, &sig_bit); PngTextList textList; textList.add("Software", "www.inkscape.org"); // Made by Inkscape comment { const gchar* pngToDc[] = {"Title", "title", "Author", "creator", "Description", "description", //"Copyright", "", "Creation Time", "date", //"Disclaimer", "", //"Warning", "", "Source", "source" //"Comment", "" }; for (size_t i = 0; i < G_N_ELEMENTS(pngToDc); i += 2) { struct rdf_work_entity_t * entity = rdf_find_entity ( pngToDc[i + 1] ); if (entity) { gchar const* data = rdf_get_work_entity(doc, entity); if (data && *data) { textList.add(pngToDc[i], data); } } else { g_warning("Unable to find entity [%s]", pngToDc[i + 1]); } } struct rdf_license_t *license = rdf_get_license(doc); if (license) { if (license->name && license->uri) { gchar* tmp = g_strdup_printf("%s %s", license->name, license->uri); textList.add("Copyright", tmp); g_free(tmp); } else if (license->name) { textList.add("Copyright", license->name); } else if (license->uri) { textList.add("Copyright", license->uri); } } } if (textList.getCount() > 0) { png_set_text(png_ptr, info_ptr, textList.getPtext(), textList.getCount()); } /* other optional chunks like cHRM, bKGD, tRNS, tIME, oFFs, pHYs, */ /* note that if sRGB is present the cHRM chunk must be ignored * on read and must be written in accordance with the sRGB profile */ png_set_pHYs(png_ptr, info_ptr, unsigned(xdpi / 0.0254 + 0.5), unsigned(ydpi / 0.0254 + 0.5), PNG_RESOLUTION_METER); /* Write the file header information. REQUIRED */ png_write_info(png_ptr, info_ptr); /* Once we write out the header, the compression type on the text * chunks gets changed to PNG_TEXT_COMPRESSION_NONE_WR or * PNG_TEXT_COMPRESSION_zTXt_WR, so it doesn't get written out again * at the end. */ /* set up the transformations you want. Note that these are * all optional. Only call them if you want them. */ /* --- CUT --- */ /* The easiest way to write the image (you may have a different memory * layout, however, so choose what fits your needs best). You need to * use the first method if you aren't handling interlacing yourself. */ png_bytep* row_pointers = new png_bytep[ebp->sheight]; r = 0; while (r < static_cast<png_uint_32>(height)) { void *to_free; int n = get_rows((unsigned char const **) row_pointers, &to_free, r, height-r, data); if (!n) break; png_write_rows(png_ptr, row_pointers, n); g_free(to_free); r += n; } delete[] row_pointers; /* You can write optional chunks like tEXt, zTXt, and tIME at the end * as well. */ /* It is REQUIRED to call this to finish writing the rest of the file */ png_write_end(png_ptr, info_ptr); /* if you allocated any text comments, free them here */ /* clean up after the write, and free any memory allocated */ png_destroy_write_struct(&png_ptr, &info_ptr); /* close the file */ fclose(fp); /* that's it */ 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 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; }
bool PNGImageFileType::write(const Image *OSG_PNG_ARG(pImage ), std::ostream &OSG_PNG_ARG(os ), const std::string &OSG_PNG_ARG(mimetype)) { #ifdef OSG_WITH_PNG png_structp png_ptr; png_infop info_ptr; if(pImage->getDimension() < 1 || pImage->getDimension() > 2) { FWARNING(("PNGImageFileType::write: invalid dimension %d!\n", pImage->getDimension())); return false; } /* Create and initialize the png_struct with the desired error handler * functions. If you want to use the default stderr and longjump method, * you can supply NULL for the last three parameters. We also check that * the library version is compatible with the one used at compile time, * in case we are using dynamically linked libraries. REQUIRED. */ png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, &errorOutput, &warningOutput); if(png_ptr == NULL) return false; /* Allocate/initialize the image information data. REQUIRED */ info_ptr = png_create_info_struct(png_ptr); if(info_ptr == NULL) { png_destroy_write_struct(&png_ptr, NULL); return false; } /* set up the output handlers */ png_set_write_fn(png_ptr, &os, &osWriteFunc, &osFlushFunc); /* This is the hard way */ /* 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 */ Int32 ctype; switch(pImage->getPixelFormat()) { case Image::OSG_L_PF: ctype = PNG_COLOR_TYPE_GRAY; break; case Image::OSG_LA_PF: ctype = PNG_COLOR_TYPE_GRAY_ALPHA; break; #if defined(GL_BGR) || defined(GL_BGR_EXT) case Image::OSG_BGR_PF: #endif case Image::OSG_RGB_PF: ctype = PNG_COLOR_TYPE_RGB; break; #if defined(GL_BGRA) || defined(GL_BGRA_EXT) case Image::OSG_BGRA_PF: #endif case Image::OSG_RGBA_PF: ctype = PNG_COLOR_TYPE_RGB_ALPHA; break; default: FWARNING(("PNGImageFileType::write: unknown pixel format %d!\n", pImage->getPixelFormat())); png_destroy_write_struct(&png_ptr, NULL); return false; } Int32 bit_depth; switch(pImage->getDataType()) { case Image::OSG_UINT8_IMAGEDATA: bit_depth = 8; break; case Image::OSG_UINT16_IMAGEDATA: bit_depth = 16; break; default: FWARNING (("Invalid pixeldepth, cannot store data\n")); return false; }; png_set_IHDR(png_ptr, info_ptr, pImage->getWidth(), pImage->getHeight(), bit_depth,ctype, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); // set resolution png supports only meter per pixel, // so we do a conversion from dpi with some rounding. png_uint_32 res_x = png_uint_32(pImage->getResX()); png_uint_32 res_y = png_uint_32(pImage->getResY()); if(pImage->getResUnit() == Image::OSG_RESUNIT_INCH) { res_x = png_uint_32((pImage->getResX() * 39.37007874f) < 0.0f ? (pImage->getResX() * 39.37007874f) - 0.5f : (pImage->getResX() * 39.37007874f) + 0.5f); res_y = png_uint_32((pImage->getResY() * 39.37007874f) < 0.0f ? (pImage->getResY() * 39.37007874f) - 0.5f : (pImage->getResY() * 39.37007874f) + 0.5f); } png_set_pHYs(png_ptr, info_ptr, res_x, res_y, PNG_RESOLUTION_METER); #if 0 /* optional significant bit chunk */ /* if we are dealing with a grayscale image then */ sig_bit.gray = true_bit_depth; /* otherwise, if we are dealing with a color image then */ sig_bit.red = true_red_bit_depth; sig_bit.green = true_green_bit_depth; sig_bit.blue = true_blue_bit_depth; /* if the image has an alpha channel then */ sig_bit.alpha = true_alpha_bit_depth; png_set_sBIT(png_ptr, info_ptr, sig_bit); /* Optional gamma chunk is strongly suggested if you have any guess * as to the correct gamma of the image. */ png_set_gAMA(png_ptr, info_ptr, gamma); /* Optionally write comments into the image */ text_ptr[0].key = "Title"; text_ptr[0].text = "Mona Lisa"; text_ptr[0].compression = PNG_TEXT_COMPRESSION_NONE; text_ptr[1].key = "Author"; text_ptr[1].text = "Leonardo DaVinci"; text_ptr[1].compression = PNG_TEXT_COMPRESSION_NONE; text_ptr[2].key = "Description"; text_ptr[2].text = "<long text>"; text_ptr[2].compression = PNG_TEXT_COMPRESSION_zTXt; #ifdef PNG_iTXt_SUPPORTED text_ptr[0].lang = NULL; text_ptr[1].lang = NULL; text_ptr[2].lang = NULL; #endif png_set_text(png_ptr, info_ptr, text_ptr, 3); #endif /* other optional chunks like cHRM, bKGD, tRNS, tIME, oFFs, pHYs, */ /* note that if sRGB is present the gAMA and cHRM chunks must be ignored * on read and must be written in accordance with the sRGB profile */ /* Write the file header information. REQUIRED */ png_write_info(png_ptr, info_ptr); #if BYTE_ORDER == LITTLE_ENDIAN if (bit_depth == 16) png_set_swap(png_ptr); #endif #if 0 /* invert monochrome pixels */ png_set_invert_mono(png_ptr); /* Shift the pixels up to a legal bit depth and fill in * as appropriate to correctly scale the image. */ png_set_shift(png_ptr, &sig_bit); /* pack pixels into bytes */ png_set_packing(png_ptr); /* Get rid of filler (OR ALPHA) bytes, pack XRGB/RGBX/ARGB/RGBA into * RGB (4 channels -> 3 channels). The second parameter is not used. */ png_set_filler(png_ptr, 0, PNG_FILLER_BEFORE); /* swap bytes of 16-bit files to most significant byte first */ png_set_swap(png_ptr); /* swap bits of 1, 2, 4 bit packed pixel formats */ png_set_packswap(png_ptr); #endif if(pImage->getPixelFormat() == Image::OSG_BGR_PF || pImage->getPixelFormat() == Image::OSG_BGRA_PF ) { /* flip BGR pixels to RGB */ png_set_bgr(png_ptr); /* swap location of alpha bytes from ARGB to RGBA */ png_set_swap_alpha(png_ptr); } /* The easiest way to write the image (you may have a different memory * layout, however, so choose what fits your needs best). You need to * use the first method if you aren't handling interlacing yourself. */ png_bytep *row_pointers = new png_bytep [pImage->getHeight()]; for(Int32 k = 0; k < pImage->getHeight(); k++) { row_pointers[k] = (const_cast<UInt8 *>(pImage->getData())) + (pImage->getHeight() - 1 - k) * pImage->getWidth() * pImage->getBpp(); } /* write out the entire image data in one call */ png_write_image(png_ptr, row_pointers); /* It is REQUIRED to call this to finish writing the rest of the file */ png_write_end(png_ptr, info_ptr); /* clean up after the write, and free any memory allocated */ png_destroy_write_struct(&png_ptr, &info_ptr); delete [] row_pointers; /* that's it */ return true; #else SWARNING << getMimeType() << " write is not compiled into the current binary " << endLog; return false; #endif }
/* Write a VIPS image to PNG. */ static int write_vips( Write *write, int compress, int interlace, const char *profile, VipsForeignPngFilter filter, gboolean strip, gboolean palette, int colours, int Q, double dither ) { VipsImage *in = write->in; int bit_depth; int color_type; int interlace_type; int i, nb_passes; g_assert( in->BandFmt == VIPS_FORMAT_UCHAR || in->BandFmt == VIPS_FORMAT_USHORT ); g_assert( in->Coding == VIPS_CODING_NONE ); g_assert( in->Bands > 0 && in->Bands < 5 ); /* Catch PNG errors. */ if( setjmp( png_jmpbuf( write->pPng ) ) ) return( -1 ); /* Check input image. If we are writing interlaced, we need to make 7 * passes over the image. We advertise ourselves as seq, so to ensure * we only suck once from upstream, switch to WIO. */ if( interlace ) { if( !(write->memory = vips_image_copy_memory( in )) ) return( -1 ); in = write->memory; } else { if( vips_image_pio_input( in ) ) return( -1 ); } if( compress < 0 || compress > 9 ) { vips_error( "vips2png", "%s", _( "compress should be in [0,9]" ) ); return( -1 ); } /* Set compression parameters. */ png_set_compression_level( write->pPng, compress ); /* Set row filter. */ png_set_filter( write->pPng, 0, filter ); bit_depth = in->BandFmt == VIPS_FORMAT_UCHAR ? 8 : 16; switch( in->Bands ) { case 1: color_type = PNG_COLOR_TYPE_GRAY; break; case 2: color_type = PNG_COLOR_TYPE_GRAY_ALPHA; break; case 3: color_type = PNG_COLOR_TYPE_RGB; break; case 4: color_type = PNG_COLOR_TYPE_RGB_ALPHA; break; default: vips_error( "vips2png", _( "can't save %d band image as png" ), in->Bands ); return( -1 ); } #ifdef HAVE_IMAGEQUANT /* Enable image quantisation to paletted 8bpp PNG if colours is set. */ if( palette ) { g_assert( colours >= 2 && colours <= 256 ); bit_depth = 8; color_type = PNG_COLOR_TYPE_PALETTE; } #else if( palette ) g_warning( "%s", _( "ignoring palette (no quantisation support)" ) ); #endif /*HAVE_IMAGEQUANT*/ interlace_type = interlace ? PNG_INTERLACE_ADAM7 : PNG_INTERLACE_NONE; png_set_IHDR( write->pPng, write->pInfo, in->Xsize, in->Ysize, bit_depth, color_type, interlace_type, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT ); /* Set resolution. libpng uses pixels per meter. */ png_set_pHYs( write->pPng, write->pInfo, VIPS_RINT( in->Xres * 1000 ), VIPS_RINT( in->Yres * 1000 ), PNG_RESOLUTION_METER ); /* Set ICC Profile. */ if( profile && !strip ) { if( strcmp( profile, "none" ) != 0 ) { void *data; size_t length; if( !(data = vips__file_read_name( profile, vips__icc_dir(), &length )) ) return( -1 ); #ifdef DEBUG printf( "write_vips: " "attaching %zd bytes of ICC profile\n", length ); #endif /*DEBUG*/ png_set_iCCP( write->pPng, write->pInfo, "icc", PNG_COMPRESSION_TYPE_BASE, data, length ); } } else if( vips_image_get_typeof( in, VIPS_META_ICC_NAME ) && !strip ) { void *data; size_t length; if( vips_image_get_blob( in, VIPS_META_ICC_NAME, &data, &length ) ) return( -1 ); #ifdef DEBUG printf( "write_vips: attaching %zd bytes of ICC profile\n", length ); #endif /*DEBUG*/ png_set_iCCP( write->pPng, write->pInfo, "icc", PNG_COMPRESSION_TYPE_BASE, data, length ); } if( vips_image_get_typeof( in, VIPS_META_XMP_NAME ) ) { const char *str; if( vips_image_get_string( in, VIPS_META_XMP_NAME, &str ) ) return( -1 ); vips__png_set_text( write->pPng, write->pInfo, "XML:com.adobe.xmp", str ); } /* Set any "png-comment-xx-yyy" metadata items. */ if( vips_image_map( in, write_png_comment, write ) ) return( -1 ); #ifdef HAVE_IMAGEQUANT if( palette ) { VipsImage *im_index; VipsImage *im_palette; int palette_count; png_color *png_palette; png_byte *png_trans; int trans_count; if( vips__quantise_image( in, &im_index, &im_palette, colours, Q, dither ) ) return( -1 ); palette_count = im_palette->Xsize; g_assert( palette_count <= PNG_MAX_PALETTE_LENGTH ); png_palette = (png_color *) png_malloc( write->pPng, palette_count * sizeof( png_color ) ); png_trans = (png_byte *) png_malloc( write->pPng, palette_count * sizeof( png_byte ) ); trans_count = 0; for( i = 0; i < palette_count; i++ ) { VipsPel *p = (VipsPel *) VIPS_IMAGE_ADDR( im_palette, i, 0 ); png_color *col = &png_palette[i]; col->red = p[0]; col->green = p[1]; col->blue = p[2]; png_trans[i] = p[3]; if( p[3] != 255 ) trans_count = i + 1; #ifdef DEBUG printf( "write_vips: palette[%d] %d %d %d %d\n", i + 1, p[0], p[1], p[2], p[3] ); #endif /*DEBUG*/ } #ifdef DEBUG printf( "write_vips: attaching %d color palette\n", palette_count ); #endif /*DEBUG*/ png_set_PLTE( write->pPng, write->pInfo, png_palette, palette_count ); if( trans_count ) { #ifdef DEBUG printf( "write_vips: attaching %d alpha values\n", trans_count ); #endif /*DEBUG*/ png_set_tRNS( write->pPng, write->pInfo, png_trans, trans_count, NULL ); } png_free( write->pPng, (void *) png_palette ); png_free( write->pPng, (void *) png_trans ); VIPS_UNREF( im_palette ); VIPS_UNREF( write->memory ); write->memory = im_index; in = write->memory; } #endif /*HAVE_IMAGEQUANT*/ png_write_info( write->pPng, write->pInfo ); /* If we're an intel byte order CPU and this is a 16bit image, we need * to swap bytes. */ if( bit_depth > 8 && !vips_amiMSBfirst() ) png_set_swap( write->pPng ); if( interlace ) nb_passes = png_set_interlace_handling( write->pPng ); else nb_passes = 1; /* Write data. */ for( i = 0; i < nb_passes; i++ ) if( vips_sink_disc( in, write_png_block, write ) ) return( -1 ); /* The setjmp() was held by our background writer: reset it. */ if( setjmp( png_jmpbuf( write->pPng ) ) ) return( -1 ); png_write_end( write->pPng, write->pInfo ); return( 0 ); }
int imb_savepng(struct ImBuf *ibuf, const char *name, int flags) { png_structp png_ptr; png_infop info_ptr; unsigned char *pixels = NULL; unsigned char *from, *to; unsigned short *pixels16 = NULL, *to16; float *from_float, from_straight[4]; png_bytepp row_pointers = NULL; int i, bytesperpixel, color_type = PNG_COLOR_TYPE_GRAY; FILE *fp = NULL; bool is_16bit = (ibuf->foptions.flag & PNG_16BIT) != 0; bool has_float = (ibuf->rect_float != NULL); int channels_in_float = ibuf->channels ? ibuf->channels : 4; float (*chanel_colormanage_cb)(float); size_t num_bytes; /* use the jpeg quality setting for compression */ int compression; compression = (int)(((float)(ibuf->foptions.quality) / 11.1111f)); compression = compression < 0 ? 0 : (compression > 9 ? 9 : compression); if (ibuf->float_colorspace || (ibuf->colormanage_flag & IMB_COLORMANAGE_IS_DATA)) { /* float buffer was managed already, no need in color space conversion */ chanel_colormanage_cb = channel_colormanage_noop; } else { /* standard linear-to-srgb conversion if float buffer wasn't managed */ chanel_colormanage_cb = linearrgb_to_srgb; } /* for prints */ if (flags & IB_mem) name = "<memory>"; bytesperpixel = (ibuf->planes + 7) >> 3; if ((bytesperpixel > 4) || (bytesperpixel == 2)) { printf("imb_savepng: Unsupported bytes per pixel: %d for file: '%s'\n", bytesperpixel, name); return (0); } png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (png_ptr == NULL) { printf("imb_savepng: Cannot png_create_write_struct for file: '%s'\n", name); return 0; } info_ptr = png_create_info_struct(png_ptr); if (info_ptr == NULL) { png_destroy_write_struct(&png_ptr, (png_infopp)NULL); printf("imb_savepng: Cannot png_create_info_struct for file: '%s'\n", name); return 0; } if (setjmp(png_jmpbuf(png_ptr))) { png_destroy_write_struct(&png_ptr, &info_ptr); printf("imb_savepng: Cannot setjmp for file: '%s'\n", name); return 0; } /* copy image data */ num_bytes = ((size_t)ibuf->x) * ibuf->y * bytesperpixel; if (is_16bit) pixels16 = MEM_mallocN(num_bytes * sizeof(unsigned short), "png 16bit pixels"); else pixels = MEM_mallocN(num_bytes * sizeof(unsigned char), "png 8bit pixels"); if (pixels == NULL && pixels16 == NULL) { png_destroy_write_struct(&png_ptr, &info_ptr); printf("imb_savepng: Cannot allocate pixels array of %dx%d, %d bytes per pixel for file: '%s'\n", ibuf->x, ibuf->y, bytesperpixel, name); return 0; } from = (unsigned char *) ibuf->rect; to = pixels; from_float = ibuf->rect_float; to16 = pixels16; switch (bytesperpixel) { case 4: color_type = PNG_COLOR_TYPE_RGBA; if (is_16bit) { if (has_float) { if (channels_in_float == 4) { for (i = ibuf->x * ibuf->y; i > 0; i--) { premul_to_straight_v4_v4(from_straight, from_float); to16[0] = ftoshort(chanel_colormanage_cb(from_straight[0])); to16[1] = ftoshort(chanel_colormanage_cb(from_straight[1])); to16[2] = ftoshort(chanel_colormanage_cb(from_straight[2])); to16[3] = ftoshort(chanel_colormanage_cb(from_straight[3])); to16 += 4; from_float += 4; } } else if (channels_in_float == 3) { for (i = ibuf->x * ibuf->y; i > 0; i--) { to16[0] = ftoshort(chanel_colormanage_cb(from_float[0])); to16[1] = ftoshort(chanel_colormanage_cb(from_float[1])); to16[2] = ftoshort(chanel_colormanage_cb(from_float[2])); to16[3] = 65535; to16 += 4; from_float += 3; } } else { for (i = ibuf->x * ibuf->y; i > 0; i--) { to16[0] = ftoshort(chanel_colormanage_cb(from_float[0])); to16[2] = to16[1] = to16[0]; to16[3] = 65535; to16 += 4; from_float++; } } } else { for (i = ibuf->x * ibuf->y; i > 0; i--) { to16[0] = UPSAMPLE_8_TO_16(from[0]); to16[1] = UPSAMPLE_8_TO_16(from[1]); to16[2] = UPSAMPLE_8_TO_16(from[2]); to16[3] = UPSAMPLE_8_TO_16(from[3]); to16 += 4; from += 4; } } } else { for (i = ibuf->x * ibuf->y; i > 0; i--) { to[0] = from[0]; to[1] = from[1]; to[2] = from[2]; to[3] = from[3]; to += 4; from += 4; } } break; case 3: color_type = PNG_COLOR_TYPE_RGB; if (is_16bit) { if (has_float) { if (channels_in_float == 4) { for (i = ibuf->x * ibuf->y; i > 0; i--) { premul_to_straight_v4_v4(from_straight, from_float); to16[0] = ftoshort(chanel_colormanage_cb(from_straight[0])); to16[1] = ftoshort(chanel_colormanage_cb(from_straight[1])); to16[2] = ftoshort(chanel_colormanage_cb(from_straight[2])); to16 += 3; from_float += 4; } } else if (channels_in_float == 3) { for (i = ibuf->x * ibuf->y; i > 0; i--) { to16[0] = ftoshort(chanel_colormanage_cb(from_float[0])); to16[1] = ftoshort(chanel_colormanage_cb(from_float[1])); to16[2] = ftoshort(chanel_colormanage_cb(from_float[2])); to16 += 3; from_float += 3; } } else { for (i = ibuf->x * ibuf->y; i > 0; i--) { to16[0] = ftoshort(chanel_colormanage_cb(from_float[0])); to16[2] = to16[1] = to16[0]; to16 += 3; from_float++; } } } else { for (i = ibuf->x * ibuf->y; i > 0; i--) { to16[0] = UPSAMPLE_8_TO_16(from[0]); to16[1] = UPSAMPLE_8_TO_16(from[1]); to16[2] = UPSAMPLE_8_TO_16(from[2]); to16 += 3; from += 4; } } } else { for (i = ibuf->x * ibuf->y; i > 0; i--) { to[0] = from[0]; to[1] = from[1]; to[2] = from[2]; to += 3; from += 4; } } break; case 1: color_type = PNG_COLOR_TYPE_GRAY; if (is_16bit) { if (has_float) { float rgb[3]; if (channels_in_float == 4) { for (i = ibuf->x * ibuf->y; i > 0; i--) { premul_to_straight_v4_v4(from_straight, from_float); rgb[0] = chanel_colormanage_cb(from_straight[0]); rgb[1] = chanel_colormanage_cb(from_straight[1]); rgb[2] = chanel_colormanage_cb(from_straight[2]); to16[0] = ftoshort(IMB_colormanagement_get_luminance(rgb)); to16++; from_float += 4; } } else if (channels_in_float == 3) { for (i = ibuf->x * ibuf->y; i > 0; i--) { rgb[0] = chanel_colormanage_cb(from_float[0]); rgb[1] = chanel_colormanage_cb(from_float[1]); rgb[2] = chanel_colormanage_cb(from_float[2]); to16[0] = ftoshort(IMB_colormanagement_get_luminance(rgb)); to16++; from_float += 3; } } else { for (i = ibuf->x * ibuf->y; i > 0; i--) { to16[0] = ftoshort(chanel_colormanage_cb(from_float[0])); to16++; from_float++; } } } else { for (i = ibuf->x * ibuf->y; i > 0; i--) { to16[0] = UPSAMPLE_8_TO_16(from[0]); to16++; from += 4; } } } else { for (i = ibuf->x * ibuf->y; i > 0; i--) { to[0] = from[0]; to++; from += 4; } } break; } if (flags & IB_mem) { /* create image in memory */ imb_addencodedbufferImBuf(ibuf); ibuf->encodedsize = 0; png_set_write_fn(png_ptr, (png_voidp) ibuf, WriteData, Flush); } else { fp = BLI_fopen(name, "wb"); if (!fp) { png_destroy_write_struct(&png_ptr, &info_ptr); if (pixels) MEM_freeN(pixels); if (pixels16) MEM_freeN(pixels16); printf("imb_savepng: Cannot open file for writing: '%s'\n", name); return 0; } png_init_io(png_ptr, fp); } #if 0 png_set_filter(png_ptr, 0, PNG_FILTER_NONE | PNG_FILTER_VALUE_NONE | PNG_FILTER_SUB | PNG_FILTER_VALUE_SUB | PNG_FILTER_UP | PNG_FILTER_VALUE_UP | PNG_FILTER_AVG | PNG_FILTER_VALUE_AVG | PNG_FILTER_PAETH | PNG_FILTER_VALUE_PAETH | PNG_ALL_FILTERS); #endif png_set_compression_level(png_ptr, compression); /* png image settings */ png_set_IHDR(png_ptr, info_ptr, ibuf->x, ibuf->y, is_16bit ? 16 : 8, color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); /* image text info */ if (ibuf->metadata) { png_text *metadata; IDProperty *prop; int num_text = 0; for (prop = ibuf->metadata->data.group.first; prop; prop = prop->next) { if (prop->type == IDP_STRING) { num_text++; } } metadata = MEM_callocN(num_text * sizeof(png_text), "png_metadata"); num_text = 0; for (prop = ibuf->metadata->data.group.first; prop; prop = prop->next) { if (prop->type == IDP_STRING) { metadata[num_text].compression = PNG_TEXT_COMPRESSION_NONE; metadata[num_text].key = prop->name; metadata[num_text].text = IDP_String(prop); num_text++; } } png_set_text(png_ptr, info_ptr, metadata, num_text); MEM_freeN(metadata); } if (ibuf->ppm[0] > 0.0 && ibuf->ppm[1] > 0.0) { png_set_pHYs(png_ptr, info_ptr, (unsigned int)(ibuf->ppm[0] + 0.5), (unsigned int)(ibuf->ppm[1] + 0.5), PNG_RESOLUTION_METER); } /* write the file header information */ png_write_info(png_ptr, info_ptr); #ifdef __LITTLE_ENDIAN__ png_set_swap(png_ptr); #endif /* allocate memory for an array of row-pointers */ row_pointers = (png_bytepp) MEM_mallocN(ibuf->y * sizeof(png_bytep), "row_pointers"); if (row_pointers == NULL) { printf("imb_savepng: Cannot allocate row-pointers array for file '%s'\n", name); png_destroy_write_struct(&png_ptr, &info_ptr); if (pixels) MEM_freeN(pixels); if (pixels16) MEM_freeN(pixels16); if (fp) { fclose(fp); } return 0; } /* set the individual row-pointers to point at the correct offsets */ if (is_16bit) { for (i = 0; i < ibuf->y; i++) { row_pointers[ibuf->y - 1 - i] = (png_bytep) ((unsigned short *)pixels16 + (((size_t)i) * ibuf->x) * bytesperpixel); } } else { for (i = 0; i < ibuf->y; i++) { row_pointers[ibuf->y - 1 - i] = (png_bytep) ((unsigned char *)pixels + (((size_t)i) * ibuf->x) * bytesperpixel * sizeof(unsigned char)); } } /* write out the entire image data in one call */ png_write_image(png_ptr, row_pointers); /* write the additional chunks to the PNG file (not really needed) */ png_write_end(png_ptr, info_ptr); /* clean up */ if (pixels) MEM_freeN(pixels); if (pixels16) MEM_freeN(pixels16); MEM_freeN(row_pointers); png_destroy_write_struct(&png_ptr, &info_ptr); if (fp) { fflush(fp); fclose(fp); } return(1); }
/* Write a VIPS image to PNG. */ static int write_vips( Write *write, int compress, int interlace, const char *profile, VipsForeignPngFilter filter, gboolean strip ) { VipsImage *in = write->in; int bit_depth; int color_type; int interlace_type; int i, nb_passes; g_assert( in->BandFmt == VIPS_FORMAT_UCHAR || in->BandFmt == VIPS_FORMAT_USHORT ); g_assert( in->Coding == VIPS_CODING_NONE ); g_assert( in->Bands > 0 && in->Bands < 5 ); /* Catch PNG errors. */ if( setjmp( png_jmpbuf( write->pPng ) ) ) return( -1 ); /* Check input image. If we are writing interlaced, we need to make 7 * passes over the image. We advertise ourselves as seq, so to ensure * we only suck once from upstream, switch to WIO. */ if( interlace ) { if( !(write->memory = vips_image_copy_memory( in )) ) return( -1 ); in = write->memory; } else { if( vips_image_pio_input( in ) ) return( -1 ); } if( compress < 0 || compress > 9 ) { vips_error( "vips2png", "%s", _( "compress should be in [0,9]" ) ); return( -1 ); } /* Set compression parameters. */ png_set_compression_level( write->pPng, compress ); /* Set row filter. */ png_set_filter( write->pPng, 0, filter ); bit_depth = in->BandFmt == VIPS_FORMAT_UCHAR ? 8 : 16; switch( in->Bands ) { case 1: color_type = PNG_COLOR_TYPE_GRAY; break; case 2: color_type = PNG_COLOR_TYPE_GRAY_ALPHA; break; case 3: color_type = PNG_COLOR_TYPE_RGB; break; case 4: color_type = PNG_COLOR_TYPE_RGB_ALPHA; break; default: vips_error( "vips2png", _( "can't save %d band image as png" ), in->Bands ); return( -1 ); } interlace_type = interlace ? PNG_INTERLACE_ADAM7 : PNG_INTERLACE_NONE; png_set_IHDR( write->pPng, write->pInfo, in->Xsize, in->Ysize, bit_depth, color_type, interlace_type, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT ); /* Set resolution. libpng uses pixels per meter. */ png_set_pHYs( write->pPng, write->pInfo, VIPS_RINT( in->Xres * 1000 ), VIPS_RINT( in->Yres * 1000 ), PNG_RESOLUTION_METER ); /* Set ICC Profile. */ if( profile && !strip ) { if( strcmp( profile, "none" ) != 0 ) { void *data; size_t length; if( !(data = vips__file_read_name( profile, VIPS_ICC_DIR, &length )) ) return( -1 ); #ifdef DEBUG printf( "write_vips: " "attaching %zd bytes of ICC profile\n", length ); #endif /*DEBUG*/ png_set_iCCP( write->pPng, write->pInfo, "icc", PNG_COMPRESSION_TYPE_BASE, data, length ); } } else if( vips_image_get_typeof( in, VIPS_META_ICC_NAME ) && !strip ) { void *data; size_t length; if( vips_image_get_blob( in, VIPS_META_ICC_NAME, &data, &length ) ) return( -1 ); #ifdef DEBUG printf( "write_vips: attaching %zd bytes of ICC profile\n", length ); #endif /*DEBUG*/ png_set_iCCP( write->pPng, write->pInfo, "icc", PNG_COMPRESSION_TYPE_BASE, data, length ); } png_write_info( write->pPng, write->pInfo ); /* If we're an intel byte order CPU and this is a 16bit image, we need * to swap bytes. */ if( bit_depth > 8 && !vips_amiMSBfirst() ) png_set_swap( write->pPng ); if( interlace ) nb_passes = png_set_interlace_handling( write->pPng ); else nb_passes = 1; /* Write data. */ for( i = 0; i < nb_passes; i++ ) if( vips_sink_disc( in, write_png_block, write ) ) return( -1 ); /* The setjmp() was held by our background writer: reset it. */ if( setjmp( png_jmpbuf( write->pPng ) ) ) return( -1 ); png_write_end( write->pPng, write->pInfo ); return( 0 ); }
// this code is heavily adapted from the paint license, which is in // the file paint.license (BSD compatible) included in this // distribution. TODO, add license file to MANIFEST.in and CVS Py::Object _png_module::write_png(const Py::Tuple& args) { args.verify_length(4, 5); FILE *fp = NULL; bool close_file = false; bool close_dup_file = false; Py::Object buffer_obj = Py::Object(args[0]); PyObject* buffer = buffer_obj.ptr(); if (!PyObject_CheckReadBuffer(buffer)) { throw Py::TypeError("First argument must be an rgba buffer."); } const void* pixBufferPtr = NULL; Py_ssize_t pixBufferLength = 0; if (PyObject_AsReadBuffer(buffer, &pixBufferPtr, &pixBufferLength)) { throw Py::ValueError("Couldn't get data from read buffer."); } png_byte* pixBuffer = (png_byte*)pixBufferPtr; int width = (int)Py::Int(args[1]); int height = (int)Py::Int(args[2]); if (pixBufferLength < width * height * 4) { throw Py::ValueError("Buffer and width, height don't seem to match."); } Py::Object py_fileobj = Py::Object(args[3]); PyObject* py_file = NULL; if (py_fileobj.isString()) { if ((py_file = npy_PyFile_OpenFile(py_fileobj.ptr(), (char *)"wb")) == NULL) { throw Py::Exception(); } close_file = true; } else { py_file = py_fileobj.ptr(); } if ((fp = npy_PyFile_Dup(py_file, (char *)"wb"))) { close_dup_file = true; } else { PyErr_Clear(); PyObject* write_method = PyObject_GetAttrString( py_file, "write"); if (!(write_method && PyCallable_Check(write_method))) { Py_XDECREF(write_method); throw Py::TypeError( "Object does not appear to be a 8-bit string path or " "a Python file-like object"); } Py_XDECREF(write_method); } png_bytep *row_pointers = NULL; png_structp png_ptr = NULL; png_infop info_ptr = NULL; try { struct png_color_8_struct sig_bit; png_uint_32 row; row_pointers = new png_bytep[height]; for (row = 0; row < (png_uint_32)height; ++row) { row_pointers[row] = pixBuffer + row * width * 4; } png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (png_ptr == NULL) { throw Py::RuntimeError("Could not create write struct"); } info_ptr = png_create_info_struct(png_ptr); if (info_ptr == NULL) { throw Py::RuntimeError("Could not create info struct"); } if (setjmp(png_jmpbuf(png_ptr))) { throw Py::RuntimeError("Error building image"); } if (fp) { png_init_io(png_ptr, fp); } else { png_set_write_fn(png_ptr, (void*)py_file, &write_png_data, &flush_png_data); } png_set_IHDR(png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); // Save the dpi of the image in the file if (args.size() == 5) { double dpi = Py::Float(args[4]); size_t dots_per_meter = (size_t)(dpi / (2.54 / 100.0)); png_set_pHYs(png_ptr, info_ptr, dots_per_meter, dots_per_meter, PNG_RESOLUTION_METER); } // this a a color image! sig_bit.gray = 0; sig_bit.red = 8; sig_bit.green = 8; sig_bit.blue = 8; /* if the image has an alpha channel then */ sig_bit.alpha = 8; png_set_sBIT(png_ptr, info_ptr, &sig_bit); png_write_info(png_ptr, info_ptr); png_write_image(png_ptr, row_pointers); png_write_end(png_ptr, info_ptr); } catch (...) { if (png_ptr && info_ptr) { png_destroy_write_struct(&png_ptr, &info_ptr); } delete [] row_pointers; if (close_dup_file) { if (npy_PyFile_DupClose(py_file, fp)) { throw Py::RuntimeError("Error closing dupe file handle"); } } if (close_file) { npy_PyFile_CloseFile(py_file); Py_DECREF(py_file); } /* Changed calls to png_destroy_write_struct to follow http://www.libpng.org/pub/png/libpng-manual.txt. This ensures the info_ptr memory is released. */ throw; } png_destroy_write_struct(&png_ptr, &info_ptr); delete [] row_pointers; if (close_dup_file) { if (npy_PyFile_DupClose(py_file, fp)) { throw Py::RuntimeError("Error closing dupe file handle"); } } if (close_file) { npy_PyFile_CloseFile(py_file); Py_DECREF(py_file); } if (PyErr_Occurred()) { throw Py::Exception(); } else { return Py::Object(); } }
bool writePng(const Bitmap &image, FILE *fp) { png_structp png_ptr; png_infop info_ptr; png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (png_ptr == NULL) { fclose(fp); return false; } /* Allocate/initialize the image information data. REQUIRED */ info_ptr = png_create_info_struct(png_ptr); if (info_ptr == NULL) { fclose(fp); png_destroy_write_struct(&png_ptr, NULL); return false; } /* Set error handling. REQUIRED if you aren't supplying your own * error handling functions in the png_create_write_struct() call. */ if (setjmp(png_jmpbuf(png_ptr))) { /* If we get here, we had a problem writing the file */ fclose(fp); png_destroy_write_struct(&png_ptr, &info_ptr); return false; } /* Set up the output control if you are using standard C streams */ png_init_io(png_ptr, fp); png_set_IHDR(png_ptr, info_ptr, image.width(), image.height(), 1, PNG_COLOR_TYPE_GRAY, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); /* Optional gamma chunk is strongly suggested if you have any guess * as to the correct gamma of the image. */ png_set_gAMA(png_ptr, info_ptr, 1.0); png_set_pHYs(png_ptr, info_ptr, image.xRes(), image.yRes(), image.resUnit()); /* Write the file header information. REQUIRED */ png_write_info(png_ptr, info_ptr); int width_bytes = (image.width() + 7) >> 3; png_bytep row = new png_byte[width_bytes]; for (int y = 0; y < image.height(); y++) { for (int x = 0; x < image.width(); x += 8) { uint8_t b = 0; uint8_t mask = 0x80; for (int xx = x; xx < x+8 && xx < image.width(); xx++) { if (image.get(xx, y)) { b |= mask; } mask >>= 1; } row[x >> 3] = ~b; } png_write_rows(png_ptr, &row, 1); } delete [] row; /* It is REQUIRED to call this to finish writing the rest of the file */ png_write_end(png_ptr, info_ptr); /* Clean up after the write, and free any memory allocated */ png_destroy_write_struct(&png_ptr, &info_ptr); /* Close the file */ fclose(fp); return true; }
int R_SaveAsPng(void *d, int width, int height, unsigned int (*gp)(void *, int, int), int bgr, FILE *fp, unsigned int transparent, int res) { png_structp png_ptr; png_infop info_ptr; unsigned int col, palette[256]; png_color pngpalette[256]; png_bytep pscanline; png_bytep scanline = (png_bytep) calloc((size_t)(4*width),sizeof(png_byte)); png_byte trans[256]; png_color_16 trans_values[1]; int i, j, r, ncols, mid, high, low, withpalette, have_alpha; volatile DECLARESHIFTS; /* Have we enough memory?*/ if (scanline == NULL) return 0; if (fp == NULL) { free(scanline); return 0; } /* Create and initialize the png_struct with the desired error handler * functions. If you want to use the default stderr and longjump method, * you can supply NULL for the last three parameters. We also check that * the library version is compatible with the one used at compile time, * in case we are using dynamically linked libraries. REQUIRED. */ png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (png_ptr == NULL) { free(scanline); return 0; } /* Allocate/initialize the image information data. REQUIRED */ info_ptr = png_create_info_struct(png_ptr); if (info_ptr == NULL) { free(scanline); png_destroy_write_struct(&png_ptr, (png_infopp)NULL); return 0; } /* Set error handling. REQUIRED if you aren't supplying your own * error handling functions in the png_create_write_struct() call. */ #if PNG_LIBPNG_VER < 10400 if (setjmp(png_ptr->jmpbuf)) #else if (setjmp(png_jmpbuf(png_ptr))) #endif { /* If we get here, we had a problem writing the file */ free(scanline); png_destroy_write_struct(&png_ptr, &info_ptr); return 0; } png_set_error_fn(png_ptr, NULL, my_png_error, my_png_warning); /* I/O initialization functions is REQUIRED */ png_init_io(png_ptr, fp); /* Have we less than 256 different colors? */ ncols = 0; if(transparent) palette[ncols++] = transparent & 0xFFFFFF; mid = ncols; withpalette = 1; have_alpha = 0; for (i = 0; (i < height) && withpalette ; i++) { for (j = 0; (j < width) && withpalette ; j++) { col = gp(d,i,j); if (GETALPHA(col) < 255) have_alpha = 1; /* binary search the palette: */ low = 0; high = ncols - 1; while (low <= high) { mid = (low + high)/2; if ( col < palette[mid] ) high = mid - 1; else if ( col > palette[mid] ) low = mid + 1; else break; } if (high < low) { /* didn't find colour in palette, insert it: */ if (ncols >= 256) { withpalette = 0; } else { for (r = ncols; r > low; r--) palette[r] = palette[r-1] ; palette[low] = col; ncols ++; } } } } col = gp(d,0,0); //have_alpha &= (transparent == 0); /* 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(png_ptr, info_ptr, width, height, 8, withpalette ? PNG_COLOR_TYPE_PALETTE : (have_alpha ? PNG_COLOR_TYPE_RGB_ALPHA : PNG_COLOR_TYPE_RGB), PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); if (withpalette) { for (i = 0; i < ncols ; i++) { col = palette[i]; if(transparent) { trans[i] = (col == transparent) ? 0:255; pngpalette[i].red = GETRED(col); pngpalette[i].green = GETGREEN(col); pngpalette[i].blue = GETBLUE(col); } else { /* PNG needs NON-premultiplied alpha */ int a = GETALPHA(col); trans[i] = a; if(a == 255 || a == 0) { pngpalette[i].red = GETRED(col); pngpalette[i].green = GETGREEN(col); pngpalette[i].blue = GETBLUE(col); } else { pngpalette[i].red = 0.49 + 255.0*GETRED(col)/a; pngpalette[i].green = 0.49 + 255.0*GETGREEN(col)/a; pngpalette[i].blue = 0.49 + 255.0*GETBLUE(col)/a; } } } png_set_PLTE(png_ptr, info_ptr, pngpalette, ncols); if (transparent || have_alpha) png_set_tRNS(png_ptr, info_ptr, trans, ncols, trans_values); } /* Deal with transparency */ if(transparent && !withpalette) { trans_values[0].red = GETRED(transparent); trans_values[0].blue = GETBLUE(transparent); trans_values[0].green = GETGREEN(transparent); png_set_tRNS(png_ptr, info_ptr, trans, ncols, trans_values); } if(res > 0) png_set_pHYs(png_ptr, info_ptr, res/0.0254, res/0.0254, PNG_RESOLUTION_METER); /* Write the file header information. REQUIRED */ png_write_info(png_ptr, info_ptr); /* * Now, write the pixels */ for (i = 0 ; i < height ; i++) { /* Build the scanline */ pscanline = scanline; for (j = 0 ; j < width ; j++) { col = gp(d, i, j); if (withpalette) { /* binary search the palette (the colour must be there): */ low = 0; high = ncols - 1; while (low <= high) { mid = (low + high)/2; if (col < palette[mid]) high = mid - 1; else if (col > palette[mid]) low = mid + 1; else break; } *pscanline++ = mid; } else { if(have_alpha) { /* PNG needs NON-premultiplied alpha */ int a = GETALPHA(col); if(a == 255 || a == 0) { *pscanline++ = GETRED(col) ; *pscanline++ = GETGREEN(col) ; *pscanline++ = GETBLUE(col) ; *pscanline++ = a; } else { *pscanline++ = 0.49 + 255.0*GETRED(col)/a ; *pscanline++ = 0.49 + 255.0*GETGREEN(col)/a ; *pscanline++ = 0.49 + 255.0*GETBLUE(col)/a ; *pscanline++ = a; } } else { *pscanline++ = GETRED(col) ; *pscanline++ = GETGREEN(col) ; *pscanline++ = GETBLUE(col) ; } } } png_write_row(png_ptr, scanline); } /* It is REQUIRED to call this to finish writing the rest of the file */ png_write_end(png_ptr, info_ptr); /* clean up after the write, and free any memory allocated */ free(scanline); png_destroy_write_struct(&png_ptr, &info_ptr); /* that's it */ return 1; }
bool Q_INTERNAL_WIN_NO_THROW QPNGImageWriter::writeImage(const QImage& image, volatile int quality_in, const QString &description, int off_x_in, int off_y_in) { QPoint offset = image.offset(); int off_x = off_x_in + offset.x(); int off_y = off_y_in + offset.y(); png_structp png_ptr; png_infop info_ptr; png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,0,0,0); if (!png_ptr) { return false; } png_set_error_fn(png_ptr, 0, 0, qt_png_warning); info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_write_struct(&png_ptr, 0); return false; } if (setjmp(png_jmpbuf(png_ptr))) { png_destroy_write_struct(&png_ptr, &info_ptr); return false; } int quality = quality_in; if (quality >= 0) { if (quality > 9) { qWarning("PNG: Quality %d out of range", quality); quality = 9; } png_set_compression_level(png_ptr, quality); } png_set_write_fn(png_ptr, (void*)this, qpiw_write_fn, qpiw_flush_fn); int color_type = 0; if (image.colorCount()) { if (image.isGrayscale()) color_type = PNG_COLOR_TYPE_GRAY; else color_type = PNG_COLOR_TYPE_PALETTE; } else if (image.format() == QImage::Format_Grayscale8) color_type = PNG_COLOR_TYPE_GRAY; else if (image.hasAlphaChannel()) color_type = PNG_COLOR_TYPE_RGB_ALPHA; else color_type = PNG_COLOR_TYPE_RGB; png_set_IHDR(png_ptr, info_ptr, image.width(), image.height(), image.depth() == 1 ? 1 : 8, // per channel color_type, 0, 0, 0); // sets #channels if (gamma != 0.0) { png_set_gAMA(png_ptr, info_ptr, 1.0/gamma); } if (image.format() == QImage::Format_MonoLSB) png_set_packswap(png_ptr); if (color_type == PNG_COLOR_TYPE_PALETTE) { // Paletted int num_palette = qMin(256, image.colorCount()); png_color palette[256]; png_byte trans[256]; int num_trans = 0; for (int i=0; i<num_palette; i++) { QRgb rgba=image.color(i); palette[i].red = qRed(rgba); palette[i].green = qGreen(rgba); palette[i].blue = qBlue(rgba); trans[i] = qAlpha(rgba); if (trans[i] < 255) { num_trans = i+1; } } png_set_PLTE(png_ptr, info_ptr, palette, num_palette); if (num_trans) { png_set_tRNS(png_ptr, info_ptr, trans, num_trans, 0); } } // Swap ARGB to RGBA (normal PNG format) before saving on // BigEndian machines if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { png_set_swap_alpha(png_ptr); } // Qt==ARGB==Big(ARGB)==Little(BGRA). But RGB888 is RGB regardless if (QSysInfo::ByteOrder == QSysInfo::LittleEndian && image.format() != QImage::Format_RGB888) { png_set_bgr(png_ptr); } if (off_x || off_y) { png_set_oFFs(png_ptr, info_ptr, off_x, off_y, PNG_OFFSET_PIXEL); } if (frames_written > 0) png_set_sig_bytes(png_ptr, 8); if (image.dotsPerMeterX() > 0 || image.dotsPerMeterY() > 0) { png_set_pHYs(png_ptr, info_ptr, image.dotsPerMeterX(), image.dotsPerMeterY(), PNG_RESOLUTION_METER); } set_text(image, png_ptr, info_ptr, description); png_write_info(png_ptr, info_ptr); if (image.depth() != 1) png_set_packing(png_ptr); if (color_type == PNG_COLOR_TYPE_RGB && image.format() != QImage::Format_RGB888) png_set_filler(png_ptr, 0, QSysInfo::ByteOrder == QSysInfo::BigEndian ? PNG_FILLER_BEFORE : PNG_FILLER_AFTER); if (looping >= 0 && frames_written == 0) { uchar data[13] = "NETSCAPE2.0"; // 0123456789aBC data[0xB] = looping%0x100; data[0xC] = looping/0x100; png_write_chunk(png_ptr, const_cast<png_bytep>((const png_byte *)"gIFx"), data, 13); } if (ms_delay >= 0 || disposal!=Unspecified) { uchar data[4]; data[0] = disposal; data[1] = 0; data[2] = (ms_delay/10)/0x100; // hundredths data[3] = (ms_delay/10)%0x100; png_write_chunk(png_ptr, const_cast<png_bytep>((const png_byte *)"gIFg"), data, 4); } int height = image.height(); int width = image.width(); switch (image.format()) { case QImage::Format_Mono: case QImage::Format_MonoLSB: case QImage::Format_Indexed8: case QImage::Format_Grayscale8: case QImage::Format_RGB32: case QImage::Format_ARGB32: case QImage::Format_RGB888: { png_bytep* row_pointers = new png_bytep[height]; for (int y=0; y<height; y++) row_pointers[y] = const_cast<png_bytep>(image.constScanLine(y)); png_write_image(png_ptr, row_pointers); delete [] row_pointers; } break; default: { QImage::Format fmt = image.hasAlphaChannel() ? QImage::Format_ARGB32 : QImage::Format_RGB32; QImage row; png_bytep row_pointers[1]; for (int y=0; y<height; y++) { row = image.copy(0, y, width, 1).convertToFormat(fmt); row_pointers[0] = const_cast<png_bytep>(row.constScanLine(0)); png_write_rows(png_ptr, row_pointers, 1); } } break; } png_write_end(png_ptr, info_ptr); frames_written++; png_destroy_write_struct(&png_ptr, &info_ptr); return true; }
/* Write a VIPS image to PNG. */ static int write_vips( Write *write, int compress, int interlace ) { VipsImage *in = write->in; int bit_depth; int color_type; int interlace_type; int i, nb_passes; g_assert( in->BandFmt == VIPS_FORMAT_UCHAR || in->BandFmt == VIPS_FORMAT_USHORT ); g_assert( in->Coding == VIPS_CODING_NONE ); g_assert( in->Bands > 0 && in->Bands < 5 ); /* Catch PNG errors. */ if( setjmp( png_jmpbuf( write->pPng ) ) ) return( -1 ); /* Check input image. */ if( vips_image_pio_input( in ) ) return( -1 ); if( compress < 0 || compress > 9 ) { vips_error( "vips2png", "%s", _( "compress should be in [0,9]" ) ); return( -1 ); } /* Set compression parameters. */ png_set_compression_level( write->pPng, compress ); bit_depth = in->BandFmt == VIPS_FORMAT_UCHAR ? 8 : 16; switch( in->Bands ) { case 1: color_type = PNG_COLOR_TYPE_GRAY; break; case 2: color_type = PNG_COLOR_TYPE_GRAY_ALPHA; break; case 3: color_type = PNG_COLOR_TYPE_RGB; break; case 4: color_type = PNG_COLOR_TYPE_RGB_ALPHA; break; default: g_assert( 0 ); /* Keep -Wall happy. */ return( 0 ); } interlace_type = interlace ? PNG_INTERLACE_ADAM7 : PNG_INTERLACE_NONE; png_set_IHDR( write->pPng, write->pInfo, in->Xsize, in->Ysize, bit_depth, color_type, interlace_type, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT ); /* Set resolution. libpnbg uses pixels per meter. */ png_set_pHYs( write->pPng, write->pInfo, VIPS_RINT( in->Xres * 1000 ), VIPS_RINT( in->Yres * 1000 ), PNG_RESOLUTION_METER ); /* Set ICC Profile. */ if( vips_image_get_typeof( in, VIPS_META_ICC_NAME ) ) { void *profile; size_t profile_length; if( vips_image_get_blob( in, VIPS_META_ICC_NAME, &profile, &profile_length ) ) return( -1 ); #ifdef DEBUG printf( "write_vips: attaching %zd bytes of ICC profile\n", profile_length ); #endif /*DEBUG*/ png_set_iCCP( write->pPng, write->pInfo, "icc", PNG_COMPRESSION_TYPE_BASE, profile, profile_length ); } png_write_info( write->pPng, write->pInfo ); /* If we're an intel byte order CPU and this is a 16bit image, we need * to swap bytes. */ if( bit_depth > 8 && !vips_amiMSBfirst() ) png_set_swap( write->pPng ); if( interlace ) nb_passes = png_set_interlace_handling( write->pPng ); else nb_passes = 1; /* Write data. */ for( i = 0; i < nb_passes; i++ ) if( vips_sink_disc( write->in, write_png_block, write ) ) return( -1 ); /* The setjmp() was held by our background writer: reset it. */ if( setjmp( png_jmpbuf( write->pPng ) ) ) return( -1 ); png_write_end( write->pPng, write->pInfo ); return( 0 ); }
static PyObject *Py_write_png(PyObject *self, PyObject *args, PyObject *kwds) { numpy::array_view<unsigned char, 3> buffer; PyObject *filein; double dpi = 0; const char *names[] = { "buffer", "file", "dpi", NULL }; // We don't need strict contiguity, just for each row to be // contiguous, and libpng has special handling for getting RGB out // of RGBA, ARGB or BGR. But the simplest thing to do is to // enforce contiguity using array_view::converter_contiguous. if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&O|d:write_png", (char **)names, &buffer.converter_contiguous, &buffer, &filein, &dpi)) { return NULL; } png_uint_32 width = (png_uint_32)buffer.dim(1); png_uint_32 height = (png_uint_32)buffer.dim(0); int channels = buffer.dim(2); std::vector<png_bytep> row_pointers(height); for (png_uint_32 row = 0; row < (png_uint_32)height; ++row) { row_pointers[row] = (png_bytep)buffer[row].data(); } FILE *fp = NULL; mpl_off_t offset = 0; bool close_file = false; bool close_dup_file = false; PyObject *py_file = NULL; png_structp png_ptr = NULL; png_infop info_ptr = NULL; struct png_color_8_struct sig_bit; int png_color_type; switch (channels) { case 1: png_color_type = PNG_COLOR_TYPE_GRAY; break; case 3: png_color_type = PNG_COLOR_TYPE_RGB; break; case 4: png_color_type = PNG_COLOR_TYPE_RGB_ALPHA; break; default: PyErr_SetString(PyExc_ValueError, "Buffer must be an NxMxD array with D in 1, 3, 4 " "(grayscale, RGB, RGBA)"); goto exit; } if (PyBytes_Check(filein) || PyUnicode_Check(filein)) { if ((py_file = mpl_PyFile_OpenFile(filein, (char *)"wb")) == NULL) { goto exit; } close_file = true; } else { py_file = filein; } if ((fp = mpl_PyFile_Dup(py_file, (char *)"wb", &offset))) { close_dup_file = true; } else { PyErr_Clear(); PyObject *write_method = PyObject_GetAttrString(py_file, "write"); if (!(write_method && PyCallable_Check(write_method))) { Py_XDECREF(write_method); PyErr_SetString(PyExc_TypeError, "Object does not appear to be a 8-bit string path or " "a Python file-like object"); goto exit; } Py_XDECREF(write_method); } png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (png_ptr == NULL) { PyErr_SetString(PyExc_RuntimeError, "Could not create write struct"); goto exit; } info_ptr = png_create_info_struct(png_ptr); if (info_ptr == NULL) { PyErr_SetString(PyExc_RuntimeError, "Could not create info struct"); goto exit; } if (setjmp(png_jmpbuf(png_ptr))) { PyErr_SetString(PyExc_RuntimeError, "libpng signaled error"); goto exit; } if (fp) { png_init_io(png_ptr, fp); } else { png_set_write_fn(png_ptr, (void *)py_file, &write_png_data, &flush_png_data); } png_set_IHDR(png_ptr, info_ptr, width, height, 8, png_color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); // Save the dpi of the image in the file if (dpi > 0.0) { png_uint_32 dots_per_meter = (png_uint_32)(dpi / (2.54 / 100.0)); png_set_pHYs(png_ptr, info_ptr, dots_per_meter, dots_per_meter, PNG_RESOLUTION_METER); } sig_bit.alpha = 0; switch (png_color_type) { case PNG_COLOR_TYPE_GRAY: sig_bit.gray = 8; sig_bit.red = 0; sig_bit.green = 0; sig_bit.blue = 0; break; case PNG_COLOR_TYPE_RGB_ALPHA: sig_bit.alpha = 8; // fall through case PNG_COLOR_TYPE_RGB: sig_bit.gray = 0; sig_bit.red = 8; sig_bit.green = 8; sig_bit.blue = 8; break; default: PyErr_SetString(PyExc_RuntimeError, "internal error, bad png_color_type"); goto exit; } png_set_sBIT(png_ptr, info_ptr, &sig_bit); png_write_info(png_ptr, info_ptr); png_write_image(png_ptr, &row_pointers[0]); png_write_end(png_ptr, info_ptr); exit: if (png_ptr && info_ptr) { png_destroy_write_struct(&png_ptr, &info_ptr); } if (close_dup_file) { mpl_PyFile_DupClose(py_file, fp, offset); } if (close_file) { mpl_PyFile_CloseFile(py_file); Py_DECREF(py_file); } if (PyErr_Occurred()) { return NULL; } else { Py_RETURN_NONE; } }
BOOL OutputPNG::OutputPNGHeader(CCLexFile *File, LPBITMAPINFOHEADER pInfo, BOOL InterlaceState, INT32 TransparentColour, LPLOGPALETTE pPalette, LPRGBQUAD pQuadPalette) { ERROR2IF(File==NULL,FALSE,"OutputPNG::OutputPNGHeader File pointer is null"); if (pInfo == NULL) pInfo = &(DestBitmapInfo->bmiHeader); ERROR2IF(pInfo==NULL,FALSE,"OutputPNG::OutputPNGHeader BitmapInfo pointer is null"); //ERROR2IF(pPalette==NULL && pQuadPalette==NULL,FALSE,"OutputPNG::OutputPNGHeader Bitmap palette pointer is null"); TRACEUSER( "Jonathan", _T("PNG write: Interlace: %s\n"), InterlaceState ? _T("Yes") : _T("No")); // Note file in our class variable as used by all the low level routines OutputFile = File; // Note the specified transparency and interlace states in our class variables Interlace = InterlaceState; if (TransparentColour != -1) Transparent = TRUE; else Transparent = FALSE; // We are just about to start so set the PNG exception handling up with our CCFile pointer PNGUtil::SetCCFilePointer(File); // Must set the exception throwing flag to True and force reporting of errors to False. // This means that the caller must report an error if the function returns False. // Any calls to CCFile::GotError will now throw a file exception and should fall into // the catch handler at the end of the function. // Replaces the goto's that handled this before. BOOL OldThrowingState = File->SetThrowExceptions( TRUE ); BOOL OldReportingState = File->SetReportErrors( FALSE ); // PNG related items (NOTE: p at end means pointer and hence implied *) png_ptr = NULL; info_ptr = NULL; palette = NULL; try { // Work out the palette size INT32 PalSize = pInfo->biClrUsed; // How many entries in palette TRACEUSER( "Jonathan", _T("PNG write: PalSize = %d\n"),PalSize); // Set up the class variables // First the width/height of the bitmap Width = pInfo->biWidth; Height = pInfo->biHeight; TRACEUSER( "Jonathan", _T("PNG write: Width = %d Height = %d\n"),Width,Height); BitsPerPixel = pInfo->biBitCount; // Start up the PNG writing code // allocate the necessary structures // Use the default handlers png_ptr = png_create_write_struct_2( PNG_LIBPNG_VER_STRING, // libpng version 0, // Optional pointer to be sent with errors camelot_png_error, // Function called in case of error camelot_png_warning, // Function called for warnings 0, // Optional pointer to be sent with mem ops camelot_png_malloc, // Function called to alloc memory camelot_png_free // Function called to free memory ); if (!png_ptr) File->GotError( _R(IDS_OUT_OF_MEMORY) ); info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_write_struct(&png_ptr, (png_infopp)NULL); File->GotError( _R(IDS_OUT_OF_MEMORY) ); } // set up the input control to the fstream class // If not a disk file then panic for the present moment // Could use the memfile functions for reading and writing as they give us what // we want. Use the io_ptr and iostream* pFStream = File->GetIOFile(); if (pFStream == NULL) { TRACEUSER( "Jonathan", _T("PNG write: OutputPNG::OutputPNGHeader No access to IOStream!")); File->GotError( _R(IDS_UNKNOWN_PNG_ERROR) ); } // Should use our own function png_set_write_fn(png_ptr, pFStream, camelot_png_write_data, camelot_png_flush_data); // png_init_io(png_ptr, pFStream); // You now have the option of modifying how the compression library // will run. The following functions are mainly for testing, but // may be useful in certain special cases, like if you need to // write png files extremely fast and are willing to give up some // compression, or if you want to get the maximum possible compression // at the expense of slower writing. If you have no special needs // in this area, let the library do what it wants, as it has been // carefully tuned to deliver the best speed/compression ratio. // See the compression library for more details. // turn on or off filtering (1 or 0) //png_set_filtering(png_ptr, 1); // compression level (0 - none, 6 - default, 9 - maximum) //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); // - this describes which optional chunks to write to the // file. Note that if you are writing a // PNG_COLOR_TYPE_PALETTE file, the PLTE chunk is not // optional, but must still be marked for writing. To // mark chunks for writing, OR valid with the // appropriate PNG_INFO_<chunk name> define. png_get_valid(png_ptr, info_ptr, 0); // resolution of image png_set_invalid(png_ptr, info_ptr, PNG_INFO_pHYs); png_set_pHYs(png_ptr, info_ptr, pInfo->biXPelsPerMeter, pInfo->biYPelsPerMeter, 1); //meter TRACEUSER( "Jonathan", _T("PNG write: x,y px per cm = %d %d\n"), png_get_x_pixels_per_meter(png_ptr, info_ptr) / 1000, png_get_y_pixels_per_meter(png_ptr, info_ptr) / 1000); BitsPerPixel = pInfo->biBitCount; TRACEUSER( "Jonathan", _T("PNG write: Bitdepth = %d\n"), BitsPerPixel); palette = NULL; num_palette = 0; trans = NULL; // - array of transparent entries for paletted images num_trans = 0; // - number of transparent entries TRACEUSER( "Jonathan", _T("PNG write: TransColour = %d\n"), TransparentColour); if ( BitsPerPixel <= 8 ) { png_set_IHDR(png_ptr, info_ptr, Width, Height, BitsPerPixel, PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); // set the palette if there is one png_set_invalid(png_ptr, info_ptr, PNG_INFO_PLTE); INT32 PaletteEntries = pInfo->biClrUsed; palette = (png_colorp)png_malloc(png_ptr, PNG_MAX_PALETTE_LENGTH * sizeof (png_color)); if (palette == NULL) File->GotError( _R(IDS_OUT_OF_MEMORY) ); png_set_PLTE(png_ptr, info_ptr, palette, num_palette); png_color_struct * pPNGPalette = palette; // ... set palette colors ... if (pQuadPalette && PaletteEntries > 0) { // Palette supplied in RGBQUAD form for (INT32 i = 0; i < PaletteEntries; i++) { pPNGPalette->red = pQuadPalette->rgbRed; pPNGPalette->green = pQuadPalette->rgbGreen; pPNGPalette->blue = pQuadPalette->rgbBlue; // skip to the next palette entry pQuadPalette++; pPNGPalette++; } } else if (pPalette && PaletteEntries > 0) { // Palette supplied in LOGPALETTE form for (INT32 i = 0; i < PaletteEntries; i++) { pPNGPalette->red = pPalette->palPalEntry[i].peRed; pPNGPalette->green = pPalette->palPalEntry[i].peGreen; pPNGPalette->blue = pPalette->palPalEntry[i].peBlue; pPNGPalette++; } } else File->GotError(_R(IDS_PNG_ERR_WRITE_PALETTE)); // Now check to see if transparency is present or not if (TransparentColour >= 0 && TransparentColour <= PaletteEntries ) { // Create the array of transparent entries for this palette // 0 is fully transparent, 255 is fully opaque, regardless of image bit depth // We will only create as many as we require, i.e. up to the transparent colour entry // rather a full palettes worth INT32 NumEntries = TransparentColour + 1; trans = (png_byte*)png_malloc(png_ptr, NumEntries * sizeof (png_byte)); if (trans) { // Set the number of transparent entries num_trans = NumEntries; png_byte * pTransEntry = trans; png_set_invalid(png_ptr, info_ptr, PNG_INFO_tRNS); for (INT32 i = 0; i < TransparentColour; i++) { *pTransEntry = 255; // set it fully opaque pTransEntry++; } // We should now be at the transparent entry so set it fully transparent *pTransEntry = 0; } } } else if (BitsPerPixel == 24) { png_set_IHDR(png_ptr, info_ptr, Width, Height, 8, /* bit_depth */ PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); } else if (BitsPerPixel == 32) { png_set_IHDR(png_ptr, info_ptr, Width, Height, 8, /* bit_depth */ PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); } else ERROR2(FALSE,"OutputPNG::OutputPNGHeader Unknown bit depth"); TRACEUSER( "Jonathan", _T("PNG write: bit_depth = %d color_type = %d\n"), png_get_bit_depth(png_ptr, info_ptr), png_get_color_type(png_ptr, info_ptr)); // Could use:- // if we are dealing with a grayscale image then //info_ptr->sig_bit.gray = true_bit_depth; png_set_hIST(png_ptr, info_ptr, NULL); png_set_text(png_ptr, info_ptr, NULL, 0); // write the file information png_write_info(png_ptr, info_ptr); TRACEUSER( "Jonathan", _T("PNG write: pixel_depth %d channels %d\n"), png_get_bit_depth(png_ptr, info_ptr), png_get_channels(png_ptr, info_ptr)); TRACEUSER( "Jonathan", _T("PNG write: rowbytes %d color_type %d\n"), png_get_rowbytes(png_ptr, info_ptr), png_get_color_type(png_ptr, info_ptr)); // Set up the transformations you want. // Note: that these are all optional. Only call them if you want them // invert monocrome pixels //png_set_invert(png_ptr); // shift the pixels up to a legal bit depth and fill in as appropriate // to correctly scale the image //png_set_shift(png_ptr, &(info_ptr->sig_bit)); // pack pixels into bytes //png_set_packing(png_ptr); png_set_bgr(png_ptr); // swap bytes of 16 bit files to most significant bit first png_set_swap(png_ptr); // Must set the exception throwing and reporting flags back to their entry states File->SetThrowExceptions( OldThrowingState ); File->SetReportErrors( OldReportingState ); // er, we seem to have finished OK so say so return TRUE; } catch (...) { // catch our form of a file exception TRACE( _T("OutputPNG::OutputPNGHeader CC catch handler\n")); // Call up function to clean up the png structures CleanUpPngStructures(); // Must set the exception throwing and reporting flags back to their entry states File->SetThrowExceptions( OldThrowingState ); File->SetReportErrors( OldReportingState ); // We have finished so reset the PNG exception handling PNGUtil::SetCCFilePointer(NULL); return FALSE; } ERROR2( FALSE, "Escaped exception clause somehow" ); }
/*! * pixWriteStreamPng() * * Input: stream * pix * gamma (use 0.0 if gamma is not defined) * Return: 0 if OK; 1 on error * * Notes: * (1) If called from pixWriteStream(), the stream is positioned * at the beginning of the file. * (2) To do sequential writes of png format images to a stream, * use pixWriteStreamPng() directly. * (3) gamma is an optional png chunk. If no gamma value is to be * placed into the file, use gamma = 0.0. Otherwise, if * gamma > 0.0, its value is written into the header. * (4) The use of gamma in png is highly problematic. For an illuminating * discussion, see: http://hsivonen.iki.fi/png-gamma/ * (5) What is the effect/meaning of gamma in the png file? This * gamma, which we can call the 'source' gamma, is the * inverse of the gamma that was used in enhance.c to brighten * or darken images. The 'source' gamma is supposed to indicate * the intensity mapping that was done at the time the * image was captured. Display programs typically apply a * 'display' gamma of 2.2 to the output, which is intended * to linearize the intensity based on the response of * thermionic tubes (CRTs). Flat panel LCDs have typically * been designed to give a similar response as CRTs (call it * "backward compatibility"). The 'display' gamma is * in some sense the inverse of the 'source' gamma. * jpeg encoders attached to scanners and cameras will lighten * the pixels, applying a gamma corresponding to approximately * a square-root relation of output vs input: * output = input^(gamma) * where gamma is often set near 0.4545 (1/gamma is 2.2). * This is stored in the image file. Then if the display * program reads the gamma, it will apply a display gamma, * typically about 2.2; the product is 1.0, and the * display program produces a linear output. This works because * the dark colors were appropriately boosted by the scanner, * as described by the 'source' gamma, so they should not * be further boosted by the display program. * (6) As an example, with xv and display, if no gamma is stored, * the program acts as if gamma were 0.4545, multiplies this by 2.2, * and does a linear rendering. Taking this as a baseline * brightness, if the stored gamma is: * > 0.4545, the image is rendered lighter than baseline * < 0.4545, the image is rendered darker than baseline * In contrast, gqview seems to ignore the gamma chunk in png. * (7) The only valid pixel depths in leptonica are 1, 2, 4, 8, 16 * and 32. However, it is possible, and in some cases desirable, * to write out a png file using an rgb pix that has 24 bpp. * For example, the open source xpdf SplashBitmap class generates * 24 bpp rgb images. Consequently, we anble writing 24 bpp pix. * To generate such a pix, you can make a 24 bpp pix without data * and assign the data array to the pix; e.g., * pix = pixCreateHeader(w, h, 24); * pixSetData(pix, rgbdata); * See pixConvert32To24() for an example, where we get rgbdata * from the 32 bpp pix. Caution: do not call pixSetPadBits(), * because the alignment is wrong and you may erase part of the * last pixel on each line. */ l_int32 pixWriteStreamPng(FILE *fp, PIX *pix, l_float32 gamma) { char commentstring[] = "Comment"; l_int32 i, j, k; l_int32 wpl, d, cmflag; l_int32 ncolors; l_int32 *rmap, *gmap, *bmap; l_uint32 *data, *ppixel; png_byte bit_depth, color_type; png_uint_32 w, h; png_uint_32 xres, yres; png_bytep *row_pointers; png_bytep rowbuffer; png_structp png_ptr; png_infop info_ptr; png_colorp palette; PIX *pixt; PIXCMAP *cmap; char *text; PROCNAME("pixWriteStreamPng"); if (!fp) return ERROR_INT("stream not open", procName, 1); if (!pix) return ERROR_INT("pix not defined", procName, 1); /* Allocate the 2 data structures */ if ((png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, NULL, NULL)) == NULL) return ERROR_INT("png_ptr not made", procName, 1); if ((info_ptr = png_create_info_struct(png_ptr)) == NULL) { png_destroy_write_struct(&png_ptr, (png_infopp)NULL); return ERROR_INT("info_ptr not made", procName, 1); } /* Set up png setjmp error handling */ if (setjmp(png_jmpbuf(png_ptr))) { png_destroy_write_struct(&png_ptr, &info_ptr); return ERROR_INT("internal png error", procName, 1); } png_init_io(png_ptr, fp); /* With best zlib compression (9), get between 1 and 10% improvement * over default (5), but the compression is 3 to 10 times slower. * Our default compression is the zlib default (5). */ png_set_compression_level(png_ptr, var_ZLIB_COMPRESSION); w = pixGetWidth(pix); h = pixGetHeight(pix); d = pixGetDepth(pix); if ((cmap = pixGetColormap(pix))) cmflag = 1; else cmflag = 0; /* Set the color type and bit depth. */ if (d == 32 && var_PNG_WRITE_ALPHA == 1) { bit_depth = 8; color_type = PNG_COLOR_TYPE_RGBA; /* 6 */ cmflag = 0; /* ignore if it exists */ } else if (d == 24 || d == 32) { bit_depth = 8; color_type = PNG_COLOR_TYPE_RGB; /* 2 */ cmflag = 0; /* ignore if it exists */ } else { bit_depth = d; color_type = PNG_COLOR_TYPE_GRAY; /* 0 */ } if (cmflag) color_type = PNG_COLOR_TYPE_PALETTE; /* 3 */ #if DEBUG fprintf(stderr, "cmflag = %d, bit_depth = %d, color_type = %d\n", cmflag, bit_depth, color_type); #endif /* DEBUG */ png_set_IHDR(png_ptr, info_ptr, w, h, bit_depth, color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); /* Store resolution in ppm, if known */ xres = (png_uint_32)(39.37 * (l_float32)pixGetXRes(pix) + 0.5); yres = (png_uint_32)(39.37 * (l_float32)pixGetYRes(pix) + 0.5); if ((xres == 0) || (yres == 0)) png_set_pHYs(png_ptr, info_ptr, 0, 0, PNG_RESOLUTION_UNKNOWN); else png_set_pHYs(png_ptr, info_ptr, xres, yres, PNG_RESOLUTION_METER); if (cmflag) { pixcmapToArrays(cmap, &rmap, &gmap, &bmap); ncolors = pixcmapGetCount(cmap); /* Make and save the palette */ if ((palette = (png_colorp)(CALLOC(ncolors, sizeof(png_color)))) == NULL) return ERROR_INT("palette not made", procName, 1); for (i = 0; i < ncolors; i++) { palette[i].red = (png_byte)rmap[i]; palette[i].green = (png_byte)gmap[i]; palette[i].blue = (png_byte)bmap[i]; } png_set_PLTE(png_ptr, info_ptr, palette, (int)ncolors); FREE(rmap); FREE(gmap); FREE(bmap); } /* 0.4545 is treated as the default by some image * display programs (not gqview). A value > 0.4545 will * lighten an image as displayed by xv, display, etc. */ if (gamma > 0.0) png_set_gAMA(png_ptr, info_ptr, (l_float64)gamma); if ((text = pixGetText(pix))) { png_text text_chunk; text_chunk.compression = PNG_TEXT_COMPRESSION_NONE; text_chunk.key = commentstring; text_chunk.text = text; text_chunk.text_length = strlen(text); #ifdef PNG_ITXT_SUPPORTED text_chunk.itxt_length = 0; text_chunk.lang = NULL; text_chunk.lang_key = NULL; #endif png_set_text(png_ptr, info_ptr, &text_chunk, 1); } /* Write header and palette info */ png_write_info(png_ptr, info_ptr); if ((d != 32) && (d != 24)) { /* not rgb color */ /* Generate a temporary pix with bytes swapped. * For a binary image, there are two conditions in * which you must first invert the data for writing png: * (a) no colormap * (b) colormap with BLACK set to 0 * png writes binary with BLACK = 0, unless contradicted * by a colormap. If the colormap has BLACK = "1" * (typ. about 255), do not invert the data. If there * is no colormap, you must invert the data to store * in default BLACK = 0 state. */ if (d == 1 && (!cmap || (cmap && ((l_uint8 *)(cmap->array))[0] == 0x0))) { pixt = pixInvert(NULL, pix); pixEndianByteSwap(pixt); } else pixt = pixEndianByteSwapNew(pix); if (!pixt) { png_destroy_write_struct(&png_ptr, &info_ptr); return ERROR_INT("pixt not made", procName, 1); } /* Make and assign array of image row pointers */ if ((row_pointers = (png_bytep *)CALLOC(h, sizeof(png_bytep))) == NULL) return ERROR_INT("row-pointers not made", procName, 1); wpl = pixGetWpl(pixt); data = pixGetData(pixt); for (i = 0; i < h; i++) row_pointers[i] = (png_bytep)(data + i * wpl); png_set_rows(png_ptr, info_ptr, row_pointers); /* Transfer the data */ png_write_image(png_ptr, row_pointers); png_write_end(png_ptr, info_ptr); if (cmflag) FREE(palette); FREE(row_pointers); pixDestroy(&pixt); png_destroy_write_struct(&png_ptr, &info_ptr); return 0; } /* For rgb, compose and write a row at a time */ data = pixGetData(pix); wpl = pixGetWpl(pix); if (d == 24) { /* See note 7 above: special case of 24 bpp rgb */ for (i = 0; i < h; i++) { ppixel = data + i * wpl; png_write_rows(png_ptr, (png_bytepp)&ppixel, 1); } } else { /* 32 bpp rgb and rgba */ if ((rowbuffer = (png_bytep)CALLOC(w, 4)) == NULL) return ERROR_INT("rowbuffer not made", procName, 1); for (i = 0; i < h; i++) { ppixel = data + i * wpl; for (j = k = 0; j < w; j++) { rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_RED); rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_GREEN); rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_BLUE); if (var_PNG_WRITE_ALPHA == 1) rowbuffer[k++] = GET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL); ppixel++; } png_write_rows(png_ptr, &rowbuffer, 1); } FREE(rowbuffer); } png_write_end(png_ptr, info_ptr); if (cmflag) FREE(palette); png_destroy_write_struct(&png_ptr, &info_ptr); return 0; }
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; }
static int writePNG(QRcode *qrcode, const char *outfile) { static FILE *fp; // avoid clobbering by setjmp. png_structp png_ptr; png_infop info_ptr; png_colorp palette; png_byte alpha_values[2]; unsigned char *row, *p, *q; int x, y, xx, yy, bit; int realwidth; realwidth = (qrcode->width + margin * 2) * size; row = (unsigned char *)malloc((realwidth + 7) / 8); if(row == NULL) { fprintf(stderr, "Failed to allocate memory.\n"); exit(EXIT_FAILURE); } if(outfile[0] == '-' && outfile[1] == '\0') { fp = stdout; } else { fp = fopen(outfile, "wb"); if(fp == NULL) { fprintf(stderr, "Failed to create file: %s\n", outfile); perror(NULL); exit(EXIT_FAILURE); } } png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if(png_ptr == NULL) { fprintf(stderr, "Failed to initialize PNG writer.\n"); exit(EXIT_FAILURE); } info_ptr = png_create_info_struct(png_ptr); if(info_ptr == NULL) { fprintf(stderr, "Failed to initialize PNG write.\n"); exit(EXIT_FAILURE); } if(setjmp(png_jmpbuf(png_ptr))) { png_destroy_write_struct(&png_ptr, &info_ptr); fprintf(stderr, "Failed to write PNG image.\n"); exit(EXIT_FAILURE); } palette = (png_colorp) malloc(sizeof(png_color) * 2); if(palette == NULL) { fprintf(stderr, "Failed to allocate memory.\n"); exit(EXIT_FAILURE); } palette[0].red = fg_color[0]; palette[0].green = fg_color[1]; palette[0].blue = fg_color[2]; palette[1].red = bg_color[0]; palette[1].green = bg_color[1]; palette[1].blue = bg_color[2]; alpha_values[0] = fg_color[3]; alpha_values[1] = bg_color[3]; png_set_PLTE(png_ptr, info_ptr, palette, 2); png_set_tRNS(png_ptr, info_ptr, alpha_values, 2, NULL); png_init_io(png_ptr, fp); png_set_IHDR(png_ptr, info_ptr, realwidth, realwidth, 1, PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); png_set_pHYs(png_ptr, info_ptr, dpi * INCHES_PER_METER, dpi * INCHES_PER_METER, PNG_RESOLUTION_METER); png_write_info(png_ptr, info_ptr); /* top margin */ memset(row, 0xff, (realwidth + 7) / 8); for(y=0; y<margin * size; y++) { png_write_row(png_ptr, row); } /* data */ p = qrcode->data; for(y=0; y<qrcode->width; y++) { bit = 7; memset(row, 0xff, (realwidth + 7) / 8); q = row; q += margin * size / 8; bit = 7 - (margin * size % 8); for(x=0; x<qrcode->width; x++) { for(xx=0; xx<size; xx++) { *q ^= (*p & 1) << bit; bit--; if(bit < 0) { q++; bit = 7; } } p++; } for(yy=0; yy<size; yy++) { png_write_row(png_ptr, row); } } /* bottom margin */ memset(row, 0xff, (realwidth + 7) / 8); for(y=0; y<margin * size; y++) { png_write_row(png_ptr, row); } png_write_end(png_ptr, info_ptr); png_destroy_write_struct(&png_ptr, &info_ptr); fclose(fp); free(row); free(palette); return 0; }
int imb_savepng(struct ImBuf *ibuf, const char *name, int flags) { png_structp png_ptr; png_infop info_ptr; unsigned char *pixels = NULL; unsigned char *from, *to; unsigned short *pixels16 = NULL, *to16; float *from_float, from_straight[4]; png_bytepp row_pointers = NULL; int i, bytesperpixel, color_type = PNG_COLOR_TYPE_GRAY; FILE *fp = NULL; int is_16bit = (ibuf->ftype & PNG_16BIT) && ibuf->rect_float; /* use the jpeg quality setting for compression */ int compression; compression = (int)(((float)(ibuf->ftype & 0xff) / 11.1111f)); compression = compression < 0 ? 0 : (compression > 9 ? 9 : compression); /* for prints */ if (flags & IB_mem) name = "<memory>"; bytesperpixel = (ibuf->planes + 7) >> 3; if ((bytesperpixel > 4) || (bytesperpixel == 2)) { printf("imb_savepng: Cunsupported bytes per pixel: %d for file: '%s'\n", bytesperpixel, name); return (0); } png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (png_ptr == NULL) { printf("imb_savepng: Cannot png_create_write_struct for file: '%s'\n", name); return 0; } info_ptr = png_create_info_struct(png_ptr); if (info_ptr == NULL) { png_destroy_write_struct(&png_ptr, (png_infopp)NULL); printf("imb_savepng: Cannot png_create_info_struct for file: '%s'\n", name); return 0; } if (setjmp(png_jmpbuf(png_ptr))) { png_destroy_write_struct(&png_ptr, &info_ptr); printf("imb_savepng: Cannot setjmp for file: '%s'\n", name); return 0; } /* copy image data */ if (is_16bit) pixels16 = MEM_mallocN(ibuf->x * ibuf->y * bytesperpixel * sizeof(unsigned short), "png 16bit pixels"); else pixels = MEM_mallocN(ibuf->x * ibuf->y * bytesperpixel * sizeof(unsigned char), "png 8bit pixels"); if (pixels == NULL && pixels16 == NULL) { png_destroy_write_struct(&png_ptr, &info_ptr); printf("imb_savepng: Cannot allocate pixels array of %dx%d, %d bytes per pixel for file: '%s'\n", ibuf->x, ibuf->y, bytesperpixel, name); return 0; } from = (unsigned char *) ibuf->rect; to = pixels; from_float = ibuf->rect_float; to16 = pixels16; switch (bytesperpixel) { case 4: color_type = PNG_COLOR_TYPE_RGBA; if (is_16bit) { for (i = ibuf->x * ibuf->y; i > 0; i--) { premul_to_straight_v4(from_straight, from_float); to16[0] = FTOUSHORT(from_straight[0]); to16[1] = FTOUSHORT(from_straight[1]); to16[2] = FTOUSHORT(from_straight[2]); to16[3] = FTOUSHORT(from_straight[3]); to16 += 4; from_float += 4; } } else { for (i = ibuf->x * ibuf->y; i > 0; i--) { to[0] = from[0]; to[1] = from[1]; to[2] = from[2]; to[3] = from[3]; to += 4; from += 4; } } break; case 3: color_type = PNG_COLOR_TYPE_RGB; if (is_16bit) { for (i = ibuf->x * ibuf->y; i > 0; i--) { premul_to_straight_v4(from_straight, from_float); to16[0] = FTOUSHORT(from_straight[0]); to16[1] = FTOUSHORT(from_straight[1]); to16[2] = FTOUSHORT(from_straight[2]); to16 += 3; from_float += 4; } } else { for (i = ibuf->x * ibuf->y; i > 0; i--) { to[0] = from[0]; to[1] = from[1]; to[2] = from[2]; to += 3; from += 4; } } break; case 1: color_type = PNG_COLOR_TYPE_GRAY; if (is_16bit) { for (i = ibuf->x * ibuf->y; i > 0; i--) { premul_to_straight_v4(from_straight, from_float); to16[0] = FTOUSHORT(from_straight[0]); to16++; from_float += 4; } } else { for (i = ibuf->x * ibuf->y; i > 0; i--) { to[0] = from[0]; to++; from += 4; } } break; } if (flags & IB_mem) { /* create image in memory */ imb_addencodedbufferImBuf(ibuf); ibuf->encodedsize = 0; png_set_write_fn(png_ptr, (png_voidp) ibuf, WriteData, Flush); } else { fp = BLI_fopen(name, "wb"); if (!fp) { png_destroy_write_struct(&png_ptr, &info_ptr); if (pixels) MEM_freeN(pixels); if (pixels16) MEM_freeN(pixels16); printf("imb_savepng: Cannot open file for writing: '%s'\n", name); return 0; } png_init_io(png_ptr, fp); } #if 0 png_set_filter(png_ptr, 0, PNG_FILTER_NONE | PNG_FILTER_VALUE_NONE | PNG_FILTER_SUB | PNG_FILTER_VALUE_SUB | PNG_FILTER_UP | PNG_FILTER_VALUE_UP | PNG_FILTER_AVG | PNG_FILTER_VALUE_AVG | PNG_FILTER_PAETH | PNG_FILTER_VALUE_PAETH | PNG_ALL_FILTERS); #endif png_set_compression_level(png_ptr, compression); /* png image settings */ png_set_IHDR(png_ptr, info_ptr, ibuf->x, ibuf->y, is_16bit ? 16 : 8, color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); /* image text info */ if (ibuf->metadata) { png_text *metadata; ImMetaData *iptr; int num_text = 0; iptr = ibuf->metadata; while (iptr) { num_text++; iptr = iptr->next; } metadata = MEM_callocN(num_text * sizeof(png_text), "png_metadata"); iptr = ibuf->metadata; num_text = 0; while (iptr) { metadata[num_text].compression = PNG_TEXT_COMPRESSION_NONE; metadata[num_text].key = iptr->key; metadata[num_text].text = iptr->value; num_text++; iptr = iptr->next; } png_set_text(png_ptr, info_ptr, metadata, num_text); MEM_freeN(metadata); } if (ibuf->ppm[0] > 0.0 && ibuf->ppm[1] > 0.0) { png_set_pHYs(png_ptr, info_ptr, (unsigned int)(ibuf->ppm[0] + 0.5), (unsigned int)(ibuf->ppm[1] + 0.5), PNG_RESOLUTION_METER); } /* write the file header information */ png_write_info(png_ptr, info_ptr); #ifdef __LITTLE_ENDIAN__ png_set_swap(png_ptr); #endif /* allocate memory for an array of row-pointers */ row_pointers = (png_bytepp) MEM_mallocN(ibuf->y * sizeof(png_bytep), "row_pointers"); if (row_pointers == NULL) { printf("imb_savepng: Cannot allocate row-pointers array for file '%s'\n", name); png_destroy_write_struct(&png_ptr, &info_ptr); if (pixels) MEM_freeN(pixels); if (pixels16) MEM_freeN(pixels16); if (fp) { fclose(fp); } return 0; } /* set the individual row-pointers to point at the correct offsets */ if (is_16bit) { for (i = 0; i < ibuf->y; i++) { row_pointers[ibuf->y - 1 - i] = (png_bytep) ((unsigned short *)pixels16 + (i * ibuf->x) * bytesperpixel); } } else { for (i = 0; i < ibuf->y; i++) { row_pointers[ibuf->y - 1 - i] = (png_bytep) ((unsigned char *)pixels + (i * ibuf->x) * bytesperpixel * sizeof(unsigned char)); } } /* write out the entire image data in one call */ png_write_image(png_ptr, row_pointers); /* write the additional chunks to the PNG file (not really needed) */ png_write_end(png_ptr, info_ptr); /* clean up */ if (pixels) MEM_freeN(pixels); if (pixels16) MEM_freeN(pixels16); MEM_freeN(row_pointers); png_destroy_write_struct(&png_ptr, &info_ptr); if (fp) { fflush(fp); fclose(fp); } return(1); }
static QRimageResultType writePNG(QRcode *qrcode, const char *outfile) { static FILE *fp; // avoid clobbering by setjmp. png_structp png_ptr; png_infop info_ptr; png_colorp palette; png_byte alpha_values[2]; unsigned char *row, *p, *q; int x, y, xx, yy, bit; int realwidth; realwidth = (qrcode->width + margin * 2) * size; row = (unsigned char *)malloc((realwidth + 7) / 8); if(row == NULL) { return QR_IMG_FAILED_ALLOCATE_MEMORY; } fp = fopen(outfile, "wb"); if(fp == NULL) { free(row); return QR_IMG_FAILED_CREATE_FILE; } png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if(png_ptr == NULL) { free(row); fclose(fp); return QR_IMG_FAILED_INIT_PNG_WRITER; } info_ptr = png_create_info_struct(png_ptr); if(info_ptr == NULL) { free(row); fclose(fp); return QR_IMG_FAILED_INIT_PNG_WRITER; } if(setjmp(png_jmpbuf(png_ptr))) { png_destroy_write_struct(&png_ptr, &info_ptr); free(row); fclose(fp); return QR_IMG_FAILED_WRITE_PNG_IMAGE; } palette = (png_colorp) malloc(sizeof(png_color) * 2); if(palette == NULL) { png_destroy_write_struct(&png_ptr, &info_ptr); free(row); fclose(fp); return QR_IMG_FAILED_ALLOCATE_MEMORY; } palette[0].red = fg_color[0]; palette[0].green = fg_color[1]; palette[0].blue = fg_color[2]; palette[1].red = bg_color[0]; palette[1].green = bg_color[1]; palette[1].blue = bg_color[2]; alpha_values[0] = fg_color[3]; alpha_values[1] = bg_color[3]; png_set_PLTE(png_ptr, info_ptr, palette, 2); png_set_tRNS(png_ptr, info_ptr, alpha_values, 2, NULL); png_init_io(png_ptr, fp); png_set_IHDR(png_ptr, info_ptr, realwidth, realwidth, 1, PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); png_set_pHYs(png_ptr, info_ptr, dpi * INCHES_PER_METER, dpi * INCHES_PER_METER, PNG_RESOLUTION_METER); png_write_info(png_ptr, info_ptr); /* top margin */ memset(row, 0xff, (realwidth + 7) / 8); for(y=0; y<margin * size; y++) { png_write_row(png_ptr, row); } /* data */ p = qrcode->data; for(y=0; y<qrcode->width; y++) { bit = 7; memset(row, 0xff, (realwidth + 7) / 8); q = row; q += margin * size / 8; bit = 7 - (margin * size % 8); for(x=0; x<qrcode->width; x++) { for(xx=0; xx<size; xx++) { *q ^= (*p & 1) << bit; bit--; if(bit < 0) { q++; bit = 7; } } p++; } for(yy=0; yy<size; yy++) { png_write_row(png_ptr, row); } } /* bottom margin */ memset(row, 0xff, (realwidth + 7) / 8); for(y=0; y<margin * size; y++) { png_write_row(png_ptr, row); } png_write_end(png_ptr, info_ptr); png_destroy_write_struct(&png_ptr, &info_ptr); fclose(fp); free(row); free(palette); return QR_IMG_SUCCESS; }
static BOOL DLL_CALLCONV Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) { png_structp png_ptr; png_infop info_ptr; png_colorp palette = NULL; png_uint_32 width, height; BOOL has_alpha_channel = FALSE; RGBQUAD *pal; // pointer to dib palette int bit_depth, pixel_depth; // pixel_depth = bit_depth * channels int palette_entries; int interlace_type; fi_ioStructure fio; fio.s_handle = handle; fio.s_io = io; if ((dib) && (handle)) { try { // create the chunk manage structure png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, error_handler, warning_handler); if (!png_ptr) { return FALSE; } // allocate/initialize the image information data. info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_write_struct(&png_ptr, (png_infopp)NULL); return FALSE; } // Set error handling. REQUIRED if you aren't supplying your own // error handling functions in the png_create_write_struct() call. if (setjmp(png_jmpbuf(png_ptr))) { // if we get here, we had a problem reading the file png_destroy_write_struct(&png_ptr, &info_ptr); return FALSE; } // init the IO png_set_write_fn(png_ptr, &fio, _WriteProc, _FlushProc); // set physical resolution png_uint_32 res_x = (png_uint_32)FreeImage_GetDotsPerMeterX(dib); png_uint_32 res_y = (png_uint_32)FreeImage_GetDotsPerMeterY(dib); if ((res_x > 0) && (res_y > 0)) { png_set_pHYs(png_ptr, info_ptr, res_x, res_y, PNG_RESOLUTION_METER); } // 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 width = FreeImage_GetWidth(dib); height = FreeImage_GetHeight(dib); pixel_depth = FreeImage_GetBPP(dib); BOOL bInterlaced = FALSE; if( (flags & PNG_INTERLACED) == PNG_INTERLACED) { interlace_type = PNG_INTERLACE_ADAM7; bInterlaced = TRUE; } else { interlace_type = PNG_INTERLACE_NONE; } // set the ZLIB compression level or default to PNG default compression level (ZLIB level = 6) int zlib_level = flags & 0x0F; if((zlib_level >= 1) && (zlib_level <= 9)) { png_set_compression_level(png_ptr, zlib_level); } else if((flags & PNG_Z_NO_COMPRESSION) == PNG_Z_NO_COMPRESSION) { png_set_compression_level(png_ptr, Z_NO_COMPRESSION); } // filtered strategy works better for high color images if(pixel_depth >= 16){ png_set_compression_strategy(png_ptr, Z_FILTERED); png_set_filter(png_ptr, 0, PNG_FILTER_NONE|PNG_FILTER_SUB|PNG_FILTER_PAETH); } else { png_set_compression_strategy(png_ptr, Z_DEFAULT_STRATEGY); } FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib); if(image_type == FIT_BITMAP) { // standard image type bit_depth = (pixel_depth > 8) ? 8 : pixel_depth; } else { // 16-bit greyscale or 16-bit RGB(A) bit_depth = 16; } switch (FreeImage_GetColorType(dib)) { case FIC_MINISWHITE: // Invert monochrome files to have 0 as black and 1 as white (no break here) png_set_invert_mono(png_ptr); case FIC_MINISBLACK: png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, PNG_COLOR_TYPE_GRAY, interlace_type, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); break; case FIC_PALETTE: { png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, PNG_COLOR_TYPE_PALETTE, interlace_type, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); // set the palette palette_entries = 1 << bit_depth; palette = (png_colorp)png_malloc(png_ptr, palette_entries * sizeof (png_color)); pal = FreeImage_GetPalette(dib); for (int i = 0; i < palette_entries; i++) { palette[i].red = pal[i].rgbRed; palette[i].green = pal[i].rgbGreen; palette[i].blue = pal[i].rgbBlue; } png_set_PLTE(png_ptr, info_ptr, palette, palette_entries); // You must not free palette here, because png_set_PLTE only makes a link to // the palette that you malloced. Wait until you are about to destroy // the png structure. break; } case FIC_RGBALPHA : has_alpha_channel = TRUE; png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, PNG_COLOR_TYPE_RGBA, interlace_type, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); #if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR // flip BGR pixels to RGB if(image_type == FIT_BITMAP) { png_set_bgr(png_ptr); } #endif break; case FIC_RGB: png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, PNG_COLOR_TYPE_RGB, interlace_type, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); #if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR // flip BGR pixels to RGB if(image_type == FIT_BITMAP) { png_set_bgr(png_ptr); } #endif break; case FIC_CMYK: break; } // write possible ICC profile FIICCPROFILE *iccProfile = FreeImage_GetICCProfile(dib); if (iccProfile->size && iccProfile->data) { png_set_iCCP(png_ptr, info_ptr, "Embedded Profile", 0, (png_const_bytep)iccProfile->data, iccProfile->size); } // write metadata WriteMetadata(png_ptr, info_ptr, dib); // Optional gamma chunk is strongly suggested if you have any guess // as to the correct gamma of the image. // png_set_gAMA(png_ptr, info_ptr, gamma); // set the transparency table if (FreeImage_IsTransparent(dib) && (FreeImage_GetTransparencyCount(dib) > 0)) { png_set_tRNS(png_ptr, info_ptr, FreeImage_GetTransparencyTable(dib), FreeImage_GetTransparencyCount(dib), NULL); } // set the background color if(FreeImage_HasBackgroundColor(dib)) { png_color_16 image_background; RGBQUAD rgbBkColor; FreeImage_GetBackgroundColor(dib, &rgbBkColor); memset(&image_background, 0, sizeof(png_color_16)); image_background.blue = rgbBkColor.rgbBlue; image_background.green = rgbBkColor.rgbGreen; image_background.red = rgbBkColor.rgbRed; image_background.index = rgbBkColor.rgbReserved; png_set_bKGD(png_ptr, info_ptr, &image_background); } // Write the file header information. png_write_info(png_ptr, info_ptr); // write out the image data #ifndef FREEIMAGE_BIGENDIAN if (bit_depth == 16) { // turn on 16 bit byte swapping png_set_swap(png_ptr); } #endif int number_passes = 1; if (bInterlaced) { number_passes = png_set_interlace_handling(png_ptr); } if ((pixel_depth == 32) && (!has_alpha_channel)) { BYTE *buffer = (BYTE *)malloc(width * 3); // transparent conversion to 24-bit // the number of passes is either 1 for non-interlaced images, or 7 for interlaced images for (int pass = 0; pass < number_passes; pass++) { for (png_uint_32 k = 0; k < height; k++) { FreeImage_ConvertLine32To24(buffer, FreeImage_GetScanLine(dib, height - k - 1), width); png_write_row(png_ptr, buffer); } } free(buffer); } else { // the number of passes is either 1 for non-interlaced images, or 7 for interlaced images for (int pass = 0; pass < number_passes; pass++) { for (png_uint_32 k = 0; k < height; k++) { png_write_row(png_ptr, FreeImage_GetScanLine(dib, height - k - 1)); } } } // It is REQUIRED to call this to finish writing the rest of the file // Bug with png_flush png_write_end(png_ptr, info_ptr); // clean up after the write, and free any memory allocated if (palette) { png_free(png_ptr, palette); } png_destroy_write_struct(&png_ptr, &info_ptr); return TRUE; } catch (const char *text) { FreeImage_OutputMessageProc(s_format_id, text); } } return FALSE; }
int main( int argc, char *argv[] ) { int f, rowbytes; char buf[256]; static FILE *fpout; /* "static" prevents setjmp corruption */ png_structp write_ptr; png_infop write_info_ptr, end_info_ptr; png_bytep row_buf, here; png_uint_32 y; png_textp text_ptr, new_text_ptr; int num_text; int interlace_type, compression_type, filter_type, bit_depth, color_type; int it, ct, ft, bd, clrt; png_uint_32 width, height, w, h; int duration; if( argc < 4 ) { printf( "makeanim v0.2\nusage: makeanim <duration in milliseconds> <input files ...> <output file>\n" ); printf( "example: makeanim 1500 a00.png a01.png a02.png a03.png a04.png a.anim\n" ); return 1; } duration = atoi( argv[1] ); if( duration < 1 ) { printf( "duration is incorrect\n" ); return 1; } numfiles = argc - 3; input = (struct inputstruct *)malloc( sizeof( struct inputstruct ) * numfiles ); if( !input ) return 1; for( f = 0; f < numfiles; f++ ) { input[f].name = argv[f + 2]; printf( "opening file %d, \"%s\"\n", f, input[f].name ); /* open the file handle */ input[f].file = fopen( input[f].name, "rb" ); if( input[f].file == NULL ) { printf( "fopen() failed\n" ); return 1; } /* check if it's PNG */ if( fread( buf, 1, 8, input[f].file ) != 8 ) { printf( "fread() failed for file \"%s\"\n", input[f].name ); return 1; } if( png_sig_cmp( buf, (png_size_t)0, 8 ) ) { printf( "not a PNG file\n" ); return 1; } fseek( input[f].file, 0, SEEK_SET ); /* allocate read structure */ input[f].read_ptr = png_create_read_struct( PNG_LIBPNG_VER_STRING, (png_voidp)NULL, (png_error_ptr)NULL, (png_error_ptr)NULL ); if( input[f].read_ptr == NULL ) { printf( "png_create_read_struct() failed\n" ); return 1; } /* allocate read info structure */ input[f].read_info_ptr = png_create_info_struct( input[f].read_ptr ); if( input[f].read_info_ptr == NULL ) { printf( "png_create_info_struct() failed\n" ); return 1; } /* set error handler code */ if( setjmp( input[f].read_ptr->jmpbuf ) ) { printf( "libpng read error\n" ); return 1; } /* initialize stream */ png_init_io( input[f].read_ptr, input[f].file ); png_set_read_status_fn( input[f].read_ptr, NULL ); /* read png info struct */ png_read_info( input[f].read_ptr, input[f].read_info_ptr ); /* get the info */ if( !png_get_IHDR( input[f].read_ptr, input[f].read_info_ptr, &w, &h, &bd, &clrt, &it, &ct, &ft ) ) { printf( "png_get_IHDR() failed\n" ); return 1; } /* save the info of the first frame */ if( f == 0 ) { width = w; height = h; bit_depth = bd; color_type = clrt; interlace_type = it; compression_type = ct; filter_type = ft; } /* compare all other frames to first frame */ else if( (w != width) || (h != height) || (bd != bit_depth) || (clrt != color_type) || (it != interlace_type) || (ct != compression_type) || (ft != filter_type) ) { if( w != width ) printf( "width is different\n" ); if( h != height ) printf( "height is different\n" ); if( bd != bit_depth ) printf( "bit depth is different\n" ); if( clrt != color_type ) printf( "color type is different\n" ); if( it != interlace_type ) printf( "interlace type is different\n" ); if( ct != compression_type ) printf( "compression type is different\n" ); if( ft != filter_type ) printf( "filter type is different\n" ); return 1; } } row_buf = (png_bytep)NULL; /* open output file */ printf( "opening file \"%s\"\n", argv[numfiles + 2] ); fpout = fopen( argv[numfiles + 2], "wb" ); if( fpout == NULL ) { printf( "fopen() failed\n" ); return 1; } /* allocate write structure */ write_ptr = png_create_write_struct( PNG_LIBPNG_VER_STRING, (png_voidp)NULL, (png_error_ptr)NULL, (png_error_ptr)NULL ); /* allocate info structures */ write_info_ptr = png_create_info_struct( write_ptr ); end_info_ptr = png_create_info_struct( write_ptr ); /* error handling */ if( setjmp( write_ptr->jmpbuf ) ) { printf( "libpng write error\n" ); return 1; } /* initialize output stream */ png_init_io( write_ptr, fpout ); png_set_write_status_fn( write_ptr, NULL ); /* set info */ png_set_IHDR( write_ptr, write_info_ptr, width * numfiles, height, bit_depth, color_type, PNG_INTERLACE_NONE, compression_type, filter_type); /* image characteristics */ { png_color_16p background; double white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y; double gamma; int intent; png_uint_16p hist; png_uint_32 offset_x, offset_y; int unit_type; png_charp purpose, units; png_charpp params; png_int_32 X0, X1; int type, nparams; png_uint_32 res_x, res_y; png_colorp palette; int num_palette; png_color_8p sig_bit; png_bytep trans; int num_trans; png_color_16p trans_values; /* background color */ if( png_get_bKGD( input[0].read_ptr, input[0].read_info_ptr, &background ) ) { png_set_bKGD( write_ptr, write_info_ptr, background ); } if( png_get_cHRM( input[0].read_ptr, input[0].read_info_ptr, &white_x, &white_y, &red_x, &red_y, &green_x, &green_y, &blue_x, &blue_y ) ) { png_set_cHRM( write_ptr, write_info_ptr, white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y ); } /* gamma */ if( png_get_gAMA( input[0].read_ptr, input[0].read_info_ptr, &gamma ) ) { png_set_gAMA( write_ptr, write_info_ptr, gamma ); } /* rendering intent */ if( png_get_sRGB( input[0].read_ptr, input[0].read_info_ptr, &intent ) ) { png_set_sRGB( write_ptr, write_info_ptr, intent ); } /* Histogram */ if( png_get_hIST( input[0].read_ptr, input[0].read_info_ptr, &hist ) ) { png_set_hIST( write_ptr, write_info_ptr, hist ); } /* offsets */ if( png_get_oFFs( input[0].read_ptr, input[0].read_info_ptr, &offset_x, &offset_y, &unit_type ) ) { png_set_oFFs( write_ptr, write_info_ptr, offset_x, offset_y, unit_type ); } if( png_get_pCAL( input[0].read_ptr, input[0].read_info_ptr, &purpose, &X0, &X1, &type, &nparams, &units, ¶ms ) ) { png_set_pCAL( write_ptr, write_info_ptr, purpose, X0, X1, type, nparams, units, params ); } /* pixel density */ if( png_get_pHYs( input[0].read_ptr, input[0].read_info_ptr, &res_x, &res_y, &unit_type ) ) { png_set_pHYs( write_ptr, write_info_ptr, res_x, res_y, unit_type ); } /* text chunks */ /* if( png_get_text( input[0].read_ptr, input[0].read_info_ptr, &text_ptr, &num_text ) > 0 ) { printf( "Handling %d tEXt/zTXt chunks\n", num_text ); png_set_text( write_ptr, write_info_ptr, text_ptr, num_text ); } */ /* palette */ if( png_get_PLTE( input[0].read_ptr, input[0].read_info_ptr, &palette, &num_palette ) ) { png_set_PLTE( write_ptr, write_info_ptr, palette, num_palette ); } /* significant bits */ if( png_get_sBIT( input[0].read_ptr, input[0].read_info_ptr, &sig_bit ) ) { png_set_sBIT( write_ptr, write_info_ptr, sig_bit ); } /* transparency */ if( png_get_tRNS( input[0].read_ptr, input[0].read_info_ptr, &trans, &num_trans, &trans_values ) ) { png_set_tRNS( write_ptr, write_info_ptr, trans, num_trans, trans_values ); } } /* text chunks */ num_text = 0; if( !png_get_text( input[0].read_ptr, input[0].read_info_ptr, &text_ptr, &num_text ) ) num_text = 0; new_text_ptr = (struct png_text_struct *)malloc( sizeof( struct png_text_struct ) * num_text + 1 ); if( !new_text_ptr ) { printf( "malloc() failed\n" ); return 1; } memcpy( new_text_ptr, text_ptr, sizeof( struct png_text_struct ) * num_text ); snprintf( buf, 255, "SDL_anim %d %d %d", duration, width, numfiles ); buf[255] = 0; new_text_ptr[num_text].compression = PNG_TEXT_COMPRESSION_NONE; new_text_ptr[num_text].key = "format"; new_text_ptr[num_text].text = buf; new_text_ptr[num_text].text_length = strlen( buf ); num_text++; png_set_text( write_ptr, write_info_ptr, new_text_ptr, num_text ); /* write info */ png_write_info( write_ptr, write_info_ptr ); /* allocate buffer */ rowbytes = png_get_rowbytes( input[0].read_ptr, input[0].read_info_ptr ); row_buf = (png_bytep)png_malloc( write_ptr, rowbytes * numfiles ); if( row_buf == NULL ) { printf( "png_malloc() failed\n" ); return 1; } /* copy raw data */ for( y = 0; y < height; y++ ) { /* grab a scanline from each file */ here = row_buf; for( f = 0; f < numfiles; f++ ) { png_read_rows( input[f].read_ptr, (png_bytepp)&here, (png_bytepp)NULL, 1 ); here += rowbytes; } /* write the long scanline */ png_write_rows( write_ptr, (png_bytepp)&row_buf, 1 ); } /* end io */ for( f = 0; f < numfiles; f++ ) png_read_end( input[f].read_ptr, end_info_ptr ); png_write_end( write_ptr, end_info_ptr ); /* cleanup */ png_free( write_ptr, row_buf ); for( f = 0; f < numfiles; f++ ) { png_destroy_read_struct( &input[f].read_ptr, &input[f].read_info_ptr, &end_info_ptr); fclose( input[f].file ); } png_destroy_write_struct( &write_ptr, &write_info_ptr ); fclose( fpout ); 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() ); }
static PyObject *Py_write_png(PyObject *self, PyObject *args, PyObject *kwds) { numpy::array_view<unsigned char, 3> buffer; PyObject *filein; double dpi = 0; const char *names[] = { "buffer", "file", "dpi", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&O|d:write_png", (char **)names, &buffer.converter, &buffer, &filein, &dpi)) { return NULL; } png_uint_32 width = (png_uint_32)buffer.dim(1); png_uint_32 height = (png_uint_32)buffer.dim(0); std::vector<png_bytep> row_pointers(height); for (png_uint_32 row = 0; row < (png_uint_32)height; ++row) { row_pointers[row] = (png_bytep)buffer[row].data(); } FILE *fp = NULL; mpl_off_t offset = 0; bool close_file = false; bool close_dup_file = false; PyObject *py_file = NULL; png_structp png_ptr = NULL; png_infop info_ptr = NULL; struct png_color_8_struct sig_bit; if (buffer.dim(2) != 4) { PyErr_SetString(PyExc_ValueError, "Buffer must be RGBA NxMx4 array"); goto exit; } if (PyBytes_Check(filein) || PyUnicode_Check(filein)) { if ((py_file = mpl_PyFile_OpenFile(filein, (char *)"wb")) == NULL) { goto exit; } close_file = true; } else { py_file = filein; } if ((fp = mpl_PyFile_Dup(py_file, (char *)"wb", &offset))) { close_dup_file = true; } else { PyErr_Clear(); PyObject *write_method = PyObject_GetAttrString(py_file, "write"); if (!(write_method && PyCallable_Check(write_method))) { Py_XDECREF(write_method); PyErr_SetString(PyExc_TypeError, "Object does not appear to be a 8-bit string path or " "a Python file-like object"); goto exit; } Py_XDECREF(write_method); } png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (png_ptr == NULL) { PyErr_SetString(PyExc_RuntimeError, "Could not create write struct"); goto exit; } info_ptr = png_create_info_struct(png_ptr); if (info_ptr == NULL) { PyErr_SetString(PyExc_RuntimeError, "Could not create info struct"); goto exit; } if (setjmp(png_jmpbuf(png_ptr))) { PyErr_SetString(PyExc_RuntimeError, "Error setting jumps"); goto exit; } if (fp) { png_init_io(png_ptr, fp); } else { png_set_write_fn(png_ptr, (void *)py_file, &write_png_data, &flush_png_data); } png_set_IHDR(png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); // Save the dpi of the image in the file if (dpi > 0.0) { png_uint_32 dots_per_meter = (png_uint_32)(dpi / (2.54 / 100.0)); png_set_pHYs(png_ptr, info_ptr, dots_per_meter, dots_per_meter, PNG_RESOLUTION_METER); } // this a a color image! sig_bit.gray = 0; sig_bit.red = 8; sig_bit.green = 8; sig_bit.blue = 8; /* if the image has an alpha channel then */ sig_bit.alpha = 8; png_set_sBIT(png_ptr, info_ptr, &sig_bit); png_write_info(png_ptr, info_ptr); png_write_image(png_ptr, &row_pointers[0]); png_write_end(png_ptr, info_ptr); exit: if (png_ptr && info_ptr) { png_destroy_write_struct(&png_ptr, &info_ptr); } if (close_dup_file) { mpl_PyFile_DupClose(py_file, fp, offset); } if (close_file) { mpl_PyFile_CloseFile(py_file); Py_DECREF(py_file); } if (PyErr_Occurred()) { return NULL; } else { Py_RETURN_NONE; } }