static BOOL WriteMetadata(png_structp png_ptr, png_infop info_ptr, FIBITMAP *dib) { // XMP keyword char *g_png_xmp_keyword = "XML:com.adobe.xmp"; FITAG *tag = NULL; FIMETADATA *mdhandle = NULL; BOOL bResult = TRUE; png_text text_metadata; int num_text = 0; // set the 'Comments' metadata as iTXt chuncks mdhandle = FreeImage_FindFirstMetadata(FIMD_COMMENTS, dib, &tag); if(mdhandle) { do { memset(&text_metadata, 0, sizeof(png_text)); text_metadata.compression = 1; // iTXt, none text_metadata.key = (char*)FreeImage_GetTagKey(tag); // keyword, 1-79 character description of "text" text_metadata.text = (char*)FreeImage_GetTagValue(tag); // comment, may be an empty string (ie "") text_metadata.text_length = FreeImage_GetTagLength(tag);// length of the text string text_metadata.itxt_length = FreeImage_GetTagLength(tag);// length of the itxt string text_metadata.lang = 0; // language code, 0-79 characters or a NULL pointer text_metadata.lang_key = 0; // keyword translated UTF-8 string, 0 or more chars or a NULL pointer // set the tag png_set_text(png_ptr, info_ptr, &text_metadata, 1); } while(FreeImage_FindNextMetadata(mdhandle, &tag)); FreeImage_FindCloseMetadata(mdhandle); bResult &= TRUE; } // set the 'XMP' metadata as iTXt chuncks tag = NULL; FreeImage_GetMetadata(FIMD_XMP, dib, g_TagLib_XMPFieldName, &tag); if(tag && FreeImage_GetTagLength(tag)) { memset(&text_metadata, 0, sizeof(png_text)); text_metadata.compression = 1; // iTXt, none text_metadata.key = g_png_xmp_keyword; // keyword, 1-79 character description of "text" text_metadata.text = (char*)FreeImage_GetTagValue(tag); // comment, may be an empty string (ie "") text_metadata.text_length = FreeImage_GetTagLength(tag);// length of the text string text_metadata.itxt_length = FreeImage_GetTagLength(tag);// length of the itxt string text_metadata.lang = 0; // language code, 0-79 characters or a NULL pointer text_metadata.lang_key = 0; // keyword translated UTF-8 string, 0 or more chars or a NULL pointer // set the tag png_set_text(png_ptr, info_ptr, &text_metadata, 1); bResult &= TRUE; } return bResult; }
/* offsetを文字列化してPNGのテキストチャンクに埋め込む関数 ※この関数はrow_pointerに読み込む前に実行されないといけない @offset 埋め込むwoff_t @cspecs テキストチャンクにアクセスするために必要 */ static void setOffsetToChunk(const woff_t *offset, struct png_control_specs *cspecs) { png_text text; char buf[32]; /* メモリの確保 */ text.compression = PNG_TEXT_COMPRESSION_NONE; text.key = (char *)malloc(sizeof(char) * 16); text.text = (char *)malloc(sizeof(char) * 32); /* オフセットは書き込んであるビット数だけ */ sprintf(buf, "%d", offset->total_bit); strcpy(text.key, "WaterMark offset"); strcpy(text.text, buf); png_set_text(cspecs->png_ptr, cspecs->info_ptr, (png_textp)&text, 1); #ifdef DEBUG puts("The offset wrote"); printOffset(offset); #endif png_write_info(cspecs->png_ptr, cspecs->info_ptr); /* メモリを解放する */ free(text.key); free(text.text); }
/** Wrapper for png_set_text. * * @param png_ptr PNG pointer. * @param info_ptr PNG info pointer. * @param ptext Text map. */ static void png_set_text_map(png_structp png_ptr, png_infop info_ptr, const std::map<std::string, std::string> &ptext) { png_textp text_array = new png_text[ptext.size()]; png_textp iter = text_array; for(std::map<std::string, std::string>::const_iterator ii = ptext.begin(), ee = ptext.end(); (ii != ee); ++ii) { const std::string &key = (*ii).first; const std::string &value = (*ii).second; memset(iter, 0, sizeof(png_text)); iter->compression = PNG_TEXT_COMPRESSION_NONE; iter->key = const_cast<char*>(key.c_str()); iter->text = const_cast<char*>(value.c_str()); iter->text_length = value.length(); ++iter; } png_set_text(png_ptr, info_ptr, text_array, static_cast<int>(ptext.size())); delete[] text_array; }
int writeImage(const char* filename, int width, int height, BGR *buffer, uint8_t *alpha, char *pimg) { int code = 0; FILE *fp = nullptr; png_structp png_ptr; png_infop info_ptr; // Open file for writing (binary mode) fp = fopen(filename, "wb"); // Initialize write structure png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); // Initialize info structure info_ptr = png_create_info_struct(png_ptr); png_init_io(png_ptr, fp); // Write header (8 bit colour depth) png_set_IHDR(png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_RGBA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); png_text text; text.compression = PNG_TEXT_COMPRESSION_NONE; char buff[16]; strcpy(buff, "Comment"); text.key = buff; text.text = pimg; text.text_length = strlen(text.text); png_set_text(png_ptr, info_ptr, (png_const_textp)&text, 1); png_write_info(png_ptr, info_ptr); RGBA *data = new RGBA[width*height]; for (int i = 0; i < width*height; ++i) { data[i].a = alpha[i]; data[i].r = buffer[i].b; data[i].g = buffer[i].g; data[i].b = buffer[i].r; } // Write image data for (int y=0 ; y<height ; y++) { png_write_row(png_ptr, (uint8_t *)data + y*width*4); } png_write_end(png_ptr, NULL); if (fp != NULL) fclose(fp); if (info_ptr != NULL) png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1); if (png_ptr != NULL) png_destroy_write_struct(&png_ptr, (png_infopp)NULL); delete[] data; return 0; }
void PngWriter::setText(const string& key, const string& value) { png_text pngText; pngText.compression = -1; pngText.key = const_cast<char*>(key.c_str()); pngText.text = const_cast<char*>(value.c_str()); png_set_text(m_PngPtr, m_InfoPtr, &pngText, 1); }
static void vips__png_set_text( png_structp pPng, png_infop pInfo, const char *key, const char *value ) { png_text text; text.compression = 0; text.key = (char *) key; text.text = (char *) value; text.text_length = strlen( value ); /* Before 1.4, these fields were only there if explicitly enabled. */ #if PNG_LIBPNG_VER > 10400 text.itxt_length = 0; text.lang = NULL; #endif png_set_text( pPng, pInfo, &text, 1 ); }
/* write row_pointers data to png file */ int write_png(png_bytep *row_pointers, png_uint_32 width, png_uint_32 height, const char *filename) { png_structp png_ptr; png_infop info_ptr; FILE *fp; fp = fopen(filename, "wb"); if (fp == NULL) { return 0; } png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, NULL, NULL); info_ptr = png_create_info_struct(png_ptr); png_init_io(png_ptr, fp); png_set_IHDR(png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); png_write_info(png_ptr, info_ptr); png_write_image(png_ptr, row_pointers); /* add some comments */ if (config & CONFIG_COMMENT) { int num_text = 2; png_textp text_ptr = calloc(num_text, sizeof(png_text)); text_ptr[0].compression = PNG_TEXT_COMPRESSION_NONE; text_ptr[0].key = "Software"; text_ptr[0].text = VERSION_STRING; text_ptr[0].text_length = strlen(text_ptr[0].text); text_ptr[1].compression = PNG_TEXT_COMPRESSION_NONE; text_ptr[1].key = "Comment"; text_ptr[1].text = "created with "VERSION_STRING; text_ptr[1].text_length = strlen(text_ptr[1].text); png_set_text(png_ptr, info_ptr, text_ptr, num_text); free(text_ptr); } png_write_end(png_ptr, info_ptr); png_destroy_write_struct(&png_ptr, &info_ptr); fclose(fp); return 1; }
void write_png(char* filename, unsigned w, unsigned h, unsigned char** data, png_text* comments) { FILE* fp = fopen(filename, "wb"); if (!fp) { fprintf(stderr, "ERROR: Could not open output file %s\n", filename); exit(-1); } png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0); if (!png_ptr) { fprintf(stderr, "ERROR: Could not open output file %s\n", filename); fclose(fp); exit(-1); } png_infop info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_read_struct(&png_ptr, 0, 0); fprintf(stderr, "ERROR: Could not open output file %s\n", filename); fclose(fp); exit(-1); } if (setjmp(png_jmpbuf(png_ptr))) { png_destroy_write_struct(&png_ptr, &info_ptr); fprintf(stderr, "ERROR: Could not open output file %s\n", filename); fclose(fp); exit(-1); } png_init_io(png_ptr, fp); png_set_IHDR(png_ptr, info_ptr, w, h, 8, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); png_set_text(png_ptr, info_ptr, comments, 1); png_set_rows(png_ptr, info_ptr, data); png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL); fclose(fp); }
void set_text(struct PNGImage *png) { png_text *text; char buffer[3]; text = malloc(sizeof(png_text)); if (png->horizontal) { text[0].key = "h"; text[0].text = ""; text[0].compression = PNG_TEXT_COMPRESSION_NONE; png_set_text(png->png, png->info, text, 1); } if (png->trim) { text[0].key = "x"; snprintf(buffer, 3, "%d", png->trim); text[0].text = buffer; text[0].compression = PNG_TEXT_COMPRESSION_NONE; png_set_text(png->png, png->info, text, 1); } if (*png->mapfile) { text[0].key = "t"; text[0].text = ""; text[0].compression = PNG_TEXT_COMPRESSION_NONE; png_set_text(png->png, png->info, text, 1); } if (png->mapout) { text[0].key = "T"; text[0].text = ""; text[0].compression = PNG_TEXT_COMPRESSION_NONE; png_set_text(png->png, png->info, text, 1); } if (*png->palfile) { text[0].key = "p"; text[0].text = ""; text[0].compression = PNG_TEXT_COMPRESSION_NONE; png_set_text(png->png, png->info, text, 1); } if (png->palout) { text[0].key = "P"; text[0].text = ""; text[0].compression = PNG_TEXT_COMPRESSION_NONE; png_set_text(png->png, png->info, text, 1); } free(text); }
void get_text(struct PNGImage *png) { png_text *text; int i, numtxts, numremoved; png_get_text(png->png, png->info, &text, &numtxts); for (i = 0; i < numtxts; i++) { if (strcmp(text[i].key, "h") == 0 && !*text[i].text) { png->horizontal = true; png_free_data(png->png, png->info, PNG_FREE_TEXT, i); } else if (strcmp(text[i].key, "x") == 0) { png->trim = strtoul(text[i].text, NULL, 0); png_free_data(png->png, png->info, PNG_FREE_TEXT, i); } else if (strcmp(text[i].key, "t") == 0) { png->mapfile = text[i].text; png_free_data(png->png, png->info, PNG_FREE_TEXT, i); } else if (strcmp(text[i].key, "T") == 0 && !*text[i].text) { png->mapout = true; png_free_data(png->png, png->info, PNG_FREE_TEXT, i); } else if (strcmp(text[i].key, "p") == 0) { png->palfile = text[i].text; png_free_data(png->png, png->info, PNG_FREE_TEXT, i); } else if (strcmp(text[i].key, "P") == 0 && !*text[i].text) { png->palout = true; png_free_data(png->png, png->info, PNG_FREE_TEXT, i); } } /* TODO: Remove this and simply change the warning function not to warn instead. */ for (i = 0, numremoved = 0; i < numtxts; i++) { if (text[i].key == NULL) { numremoved++; } text[i].key = text[i + numremoved].key; text[i].text = text[i + numremoved].text; text[i].compression = text[i + numremoved].compression; } png_set_text(png->png, png->info, text, numtxts - numremoved); }
static gboolean real_save_png (GdkPixbuf *pixbuf, gchar **keys, gchar **values, GError **error, gboolean to_callback, FILE *f, GdkPixbufSaveFunc save_func, gpointer user_data) { png_structp png_ptr = NULL; png_infop info_ptr; png_textp text_ptr = NULL; guchar *ptr; guchar *pixels; int y; int i; png_bytep row_ptr; png_color_8 sig_bit; int w, h, rowstride; int has_alpha; int bpc; int num_keys; int compression = -1; gboolean success = TRUE; guchar *icc_profile = NULL; gsize icc_profile_size = 0; SaveToFunctionIoPtr to_callback_ioptr; num_keys = 0; if (keys && *keys) { gchar **kiter = keys; gchar **viter = values; while (*kiter) { if (strncmp (*kiter, "tEXt::", 6) == 0) { gchar *key = *kiter + 6; int len = strlen (key); if (len < 1 || len > 79) { g_set_error_literal (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_BAD_OPTION, _("Keys for PNG text chunks must have at least 1 and at most 79 characters.")); success = FALSE; goto cleanup; } for (i = 0; i < len; i++) { if ((guchar) key[i] > 127) { g_set_error_literal (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_BAD_OPTION, _("Keys for PNG text chunks must be ASCII characters.")); success = FALSE; goto cleanup; } } num_keys++; } else if (strcmp (*kiter, "icc-profile") == 0) { /* decode from base64 */ icc_profile = g_base64_decode (*viter, &icc_profile_size); if (icc_profile_size < 127) { /* This is a user-visible error */ g_set_error (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_BAD_OPTION, _("Color profile has invalid length %d."), (gint)icc_profile_size); success = FALSE; goto cleanup; } } else if (strcmp (*kiter, "compression") == 0) { char *endptr = NULL; compression = strtol (*viter, &endptr, 10); if (endptr == *viter) { g_set_error (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_BAD_OPTION, _("PNG compression level must be a value between 0 and 9; value '%s' could not be parsed."), *viter); success = FALSE; goto cleanup; } if (compression < 0 || compression > 9) { /* This is a user-visible error; * lets people skip the range-checking * in their app. */ g_set_error (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_BAD_OPTION, _("PNG compression level must be a value between 0 and 9; value '%d' is not allowed."), compression); success = FALSE; goto cleanup; } } else { g_warning ("Unrecognized parameter (%s) passed to PNG saver.", *kiter); } ++kiter; ++viter; } } if (num_keys > 0) { gchar **kiter = keys; gchar **viter = values; text_ptr = g_new0 (png_text, num_keys); for (i = 0; i < num_keys; i++) { if (strncmp (*kiter, "tEXt::", 6) != 0) { kiter++; viter++; } text_ptr[i].compression = PNG_TEXT_COMPRESSION_NONE; text_ptr[i].key = *kiter + 6; text_ptr[i].text = g_convert (*viter, -1, "ISO-8859-1", "UTF-8", NULL, &text_ptr[i].text_length, NULL); #ifdef PNG_iTXt_SUPPORTED if (!text_ptr[i].text) { text_ptr[i].compression = PNG_ITXT_COMPRESSION_NONE; text_ptr[i].text = g_strdup (*viter); text_ptr[i].text_length = 0; text_ptr[i].itxt_length = strlen (text_ptr[i].text); text_ptr[i].lang = NULL; text_ptr[i].lang_key = NULL; } #endif if (!text_ptr[i].text) { gint j; g_set_error (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_BAD_OPTION, _("Value for PNG text chunk %s cannot be converted to ISO-8859-1 encoding."), *kiter + 6); for (j = 0; j < i; j++) g_free (text_ptr[j].text); g_free (text_ptr); return FALSE; } kiter++; viter++; } } bpc = gdk_pixbuf_get_bits_per_sample (pixbuf); w = gdk_pixbuf_get_width (pixbuf); h = gdk_pixbuf_get_height (pixbuf); rowstride = gdk_pixbuf_get_rowstride (pixbuf); has_alpha = gdk_pixbuf_get_has_alpha (pixbuf); pixels = gdk_pixbuf_get_pixels (pixbuf); png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, error, png_simple_error_callback, png_simple_warning_callback); if (png_ptr == NULL) { success = FALSE; goto cleanup; } info_ptr = png_create_info_struct (png_ptr); if (info_ptr == NULL) { success = FALSE; goto cleanup; } if (setjmp (png_jmpbuf(png_ptr))) { success = FALSE; goto cleanup; } if (num_keys > 0) { png_set_text (png_ptr, info_ptr, text_ptr, num_keys); } if (to_callback) { to_callback_ioptr.save_func = save_func; to_callback_ioptr.user_data = user_data; to_callback_ioptr.error = error; png_set_write_fn (png_ptr, &to_callback_ioptr, png_save_to_callback_write_func, png_save_to_callback_flush_func); } else { png_init_io (png_ptr, f); } if (compression >= 0) png_set_compression_level (png_ptr, compression); #if defined(PNG_iCCP_SUPPORTED) /* the proper ICC profile title is encoded in the profile */ if (icc_profile != NULL) { png_set_iCCP (png_ptr, info_ptr, "ICC profile", PNG_COMPRESSION_TYPE_BASE, (png_bytep) icc_profile, icc_profile_size); } #endif if (has_alpha) { png_set_IHDR (png_ptr, info_ptr, w, h, bpc, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); } else { png_set_IHDR (png_ptr, info_ptr, w, h, bpc, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); } sig_bit.red = bpc; sig_bit.green = bpc; sig_bit.blue = bpc; sig_bit.alpha = bpc; 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); ptr = pixels; for (y = 0; y < h; y++) { row_ptr = (png_bytep)ptr; png_write_rows (png_ptr, &row_ptr, 1); ptr += rowstride; } png_write_end (png_ptr, info_ptr); cleanup: if (png_ptr != NULL) png_destroy_write_struct (&png_ptr, &info_ptr); g_free (icc_profile); if (text_ptr != NULL) { for (i = 0; i < num_keys; i++) g_free (text_ptr[i].text); g_free (text_ptr); } return success; }
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" ); }
/* write a png file */ GF_EXPORT GF_Err gf_img_png_enc(char *data, u32 width, u32 height, s32 stride, u32 pixel_format, char *dst, u32 *dst_size) { GFpng udta; png_color_8 sig_bit; png_int_32 k; png_bytep *row_pointers; png_structp png_ptr; png_infop info_ptr; u32 type, nb_comp; type = 0; nb_comp = 0; switch (pixel_format) { case GF_PIXEL_GREYSCALE: nb_comp = 1; type = PNG_COLOR_TYPE_GRAY; break; case GF_PIXEL_ALPHAGREY: nb_comp = 1; type = PNG_COLOR_TYPE_GRAY_ALPHA; break; case GF_PIXEL_RGB_24: case GF_PIXEL_BGR_24: case GF_PIXEL_RGB_32: case GF_PIXEL_BGR_32: nb_comp = 3; type = PNG_COLOR_TYPE_RGB; break; case GF_PIXEL_RGBA: case GF_PIXEL_ARGB: nb_comp = 4; type = PNG_COLOR_TYPE_RGB_ALPHA; break; default: return GF_NOT_SUPPORTED; } if (*dst_size < width*height*nb_comp) return GF_BUFFER_TOO_SMALL; png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); // png_voidp user_error_ptr, user_error_fn, user_warning_fn); if (png_ptr == NULL) return GF_IO_ERR; /* 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 GF_IO_ERR; } /* 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))) { png_destroy_write_struct(&png_ptr, &info_ptr); return GF_NON_COMPLIANT_BITSTREAM; } udta.buffer = dst; udta.pos = 0; png_set_write_fn(png_ptr, &udta, gf_png_write, gf_png_flush); png_set_IHDR(png_ptr, info_ptr, width, height, 8, type, 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; sig_bit.gray = 8; sig_bit.alpha = 8; png_set_sBIT(png_ptr, info_ptr, &sig_bit); #if 0 /* 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 png_write_info(png_ptr, info_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); if (pixel_format==GF_PIXEL_ARGB) { // png_set_swap_alpha(png_ptr); png_set_bgr(png_ptr); } switch (pixel_format) { case GF_PIXEL_RGB_32: png_set_filler(png_ptr, 0, PNG_FILLER_AFTER); png_set_bgr(png_ptr); break; case GF_PIXEL_BGR_32: png_set_filler(png_ptr, 0, PNG_FILLER_AFTER); break; case GF_PIXEL_BGR_24: png_set_bgr(png_ptr); break; } row_pointers = gf_malloc(sizeof(png_bytep)*height); for (k = 0; k < (s32)height; k++) row_pointers[k] = data + k*stride; png_write_image(png_ptr, row_pointers); png_write_end(png_ptr, info_ptr); gf_free(row_pointers); /* clean up after the write, and free any memory allocated */ png_destroy_write_struct(&png_ptr, &info_ptr); *dst_size = udta.pos; return GF_OK; }
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() ); }
int save_bitmap(TTF_Bitmap *bitmap, const char *filename, const char *title) { FILE *fp = NULL; png_structp png_ptr = NULL; png_infop info_ptr = NULL; png_byte *row = NULL; RETINIT(SUCCESS); CHECKFAIL(bitmap, warn("failed to save uninitialized bitmap")); // Open file for writing (binary mode) fp = fopen(filename, "wb"); CHECKFAIL(fp, warnerr("failed to open file '%s' for saving bitmap", filename)); // Initialize the write structure png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); CHECKFAIL(png_ptr, warn("failed to alloc png write struct")); // Initialize the info structure info_ptr = png_create_info_struct(png_ptr); CHECKFAIL(info_ptr, warn("failed to alloc png info struct")); // Setup libpng exception handling CHECKFAIL(setjmp(png_jmpbuf(png_ptr)) == 0, warn("error occurred during png creation")); png_init_io(png_ptr, fp); // Write header (8 bit colour depth) png_set_IHDR(png_ptr, info_ptr, bitmap->w, bitmap->h, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); // Set png title if (title) { png_text png_title; png_title.compression = PNG_TEXT_COMPRESSION_NONE; png_title.key = (png_charp) "Title"; png_title.text = (png_charp) title; png_set_text(png_ptr, info_ptr, &png_title, 1); } png_write_info(png_ptr, info_ptr); // Allocate memory for one row (RBG = 3 bytes / pixel) row = (png_byte *) malloc((bitmap->w * 3) * sizeof(*row)); CHECKFAIL(row, warnerr("failed to alloc png row")); // Write image data int x, y; for (y = 0; y < bitmap->h; y++) { for (x = 0; x < bitmap->w; x++) { set_rgb(&(row[x*3]), bitmap->data[y*bitmap->w + x]); } png_write_row(png_ptr, (png_bytep)row); } // End write png_write_end(png_ptr, NULL); RETRELEASE( /* RELEASE */ if (row) free(row); if (info_ptr) png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1); if (png_ptr) png_destroy_write_struct(&png_ptr, &info_ptr); if (fp) fclose(fp); );
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); }
/* returns 0 for success, 2 for libpng problem, 4 for out of memory, 11 for * unexpected pnmtype; note that outfile might be stdout */ int writepng_init(mainprog_info *mainprog_ptr) { /* This has been altered png_structp png_ptr;*/ /* note: temporary variables! */ png_infop info_ptr; int color_type, interlace_type; /* could also replace libpng warning-handler (final NULL), but no need: */ /* This was altered png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, mainprog_ptr, writepng_error_handler, NULL); */ if (!png_ptr) return 4; /* out of memory */ info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_write_struct(&png_ptr, NULL); return 4; /* out of memory */ } /* setjmp() must be called in every function that calls a PNG-writing * libpng function, unless an alternate error handler was installed-- * but compatible error handlers must either use longjmp() themselves * (as in this program) or exit immediately, so here we go: */ if (setjmp(mainprog_ptr->jmpbuf)) { png_destroy_write_struct(&png_ptr, &info_ptr); return 2; } /* make sure outfile is (re)opened in BINARY mode */ /* This was altered png_init_io(png_ptr, mainprog_ptr->outfile); */ /* set the compression levels--in general, always want to leave filtering * turned on (except for palette images) and allow all of the filters, * which is the default; want 32K zlib window, unless entire image buffer * is 16K or smaller (unknown here)--also the default; usually want max * compression (NOT the default); and remaining compression flags should * be left alone */ png_set_compression_level(png_ptr, Z_BEST_COMPRESSION); /* >> this is default for no filtering; Z_FILTERED is default otherwise: png_set_compression_strategy(png_ptr, Z_DEFAULT_STRATEGY); >> these are all defaults: png_set_compression_mem_level(png_ptr, 8); png_set_compression_window_bits(png_ptr, 15); png_set_compression_method(png_ptr, 8); */ /* set the image parameters appropriately */ if (mainprog_ptr->pnmtype == 5) color_type = PNG_COLOR_TYPE_GRAY; else if (mainprog_ptr->pnmtype == 6) color_type = PNG_COLOR_TYPE_RGB; else if (mainprog_ptr->pnmtype == 8) color_type = PNG_COLOR_TYPE_RGB_ALPHA; else { png_destroy_write_struct(&png_ptr, &info_ptr); return 11; } interlace_type = mainprog_ptr->interlaced? PNG_INTERLACE_ADAM7 : PNG_INTERLACE_NONE; png_set_IHDR(png_ptr, info_ptr, mainprog_ptr->width, mainprog_ptr->height, mainprog_ptr->sample_depth, color_type, interlace_type, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); if (mainprog_ptr->gamma > 0.0) png_set_gAMA(png_ptr, info_ptr, mainprog_ptr->gamma); if (mainprog_ptr->have_bg) { /* we know it's RGBA, not gray+alpha */ png_color_16 background; background.red = mainprog_ptr->bg_red; background.green = mainprog_ptr->bg_green; background.blue = mainprog_ptr->bg_blue; png_set_bKGD(png_ptr, info_ptr, &background); } if (mainprog_ptr->have_time) { png_time modtime; png_convert_from_time_t(&modtime, mainprog_ptr->modtime); png_set_tIME(png_ptr, info_ptr, &modtime); } if (mainprog_ptr->have_text) { png_text text[6]; int num_text = 0; if (mainprog_ptr->have_text & TEXT_TITLE) { text[num_text].compression = PNG_TEXT_COMPRESSION_NONE; text[num_text].key = "Title"; text[num_text].text = mainprog_ptr->title; ++num_text; } if (mainprog_ptr->have_text & TEXT_AUTHOR) { text[num_text].compression = PNG_TEXT_COMPRESSION_NONE; text[num_text].key = "Author"; text[num_text].text = mainprog_ptr->author; ++num_text; } if (mainprog_ptr->have_text & TEXT_DESC) { text[num_text].compression = PNG_TEXT_COMPRESSION_NONE; text[num_text].key = "Description"; text[num_text].text = mainprog_ptr->desc; ++num_text; } if (mainprog_ptr->have_text & TEXT_COPY) { text[num_text].compression = PNG_TEXT_COMPRESSION_NONE; text[num_text].key = "Copyright"; text[num_text].text = mainprog_ptr->copyright; ++num_text; } if (mainprog_ptr->have_text & TEXT_EMAIL) { text[num_text].compression = PNG_TEXT_COMPRESSION_NONE; text[num_text].key = "E-mail"; text[num_text].text = mainprog_ptr->email; ++num_text; } if (mainprog_ptr->have_text & TEXT_URL) { text[num_text].compression = PNG_TEXT_COMPRESSION_NONE; text[num_text].key = "URL"; text[num_text].text = mainprog_ptr->url; ++num_text; } png_set_text(png_ptr, info_ptr, text, num_text); } /* write all chunks up to (but not including) first IDAT */ png_write_info(png_ptr, info_ptr); /* if we wanted to write any more text info *after* the image data, we * would set up text struct(s) here and call png_set_text() again, with * just the new data; png_set_tIME() could also go here, but it would * have no effect since we already called it above (only one tIME chunk * allowed) */ /* set up the transformations: for now, just pack low-bit-depth pixels * into bytes (one, two or four pixels per byte) */ png_set_packing(png_ptr); /* png_set_shift(png_ptr, &sig_bit); to scale low-bit-depth values */ /* make sure we save our pointers for use in writepng_encode_image() */ mainprog_ptr->png_ptr = png_ptr; mainprog_ptr->info_ptr = info_ptr; /* OK, that's all we need to do for now; return happy */ return 0; }
static BOOL WriteMetadata(png_structp png_ptr, png_infop info_ptr, FIBITMAP *dib) { // XMP keyword const char *g_png_xmp_keyword = "XML:com.adobe.xmp"; FITAG *tag = NULL; FIMETADATA *mdhandle = NULL; BOOL bResult = TRUE; png_text text_metadata; png_time mod_time; // set the 'Comments' metadata as iTXt chuncks mdhandle = FreeImage_FindFirstMetadata(FIMD_COMMENTS, dib, &tag); if(mdhandle) { do { memset(&text_metadata, 0, sizeof(png_text)); text_metadata.compression = 1; // iTXt, none text_metadata.key = (char*)FreeImage_GetTagKey(tag); // keyword, 1-79 character description of "text" text_metadata.text = (char*)FreeImage_GetTagValue(tag); // comment, may be an empty string (ie "") text_metadata.text_length = FreeImage_GetTagLength(tag);// length of the text string text_metadata.itxt_length = FreeImage_GetTagLength(tag);// length of the itxt string text_metadata.lang = 0; // language code, 0-79 characters or a NULL pointer text_metadata.lang_key = 0; // keyword translated UTF-8 string, 0 or more chars or a NULL pointer // set the tag png_set_text(png_ptr, info_ptr, &text_metadata, 1); } while(FreeImage_FindNextMetadata(mdhandle, &tag)); FreeImage_FindCloseMetadata(mdhandle); bResult &= TRUE; } // set the 'XMP' metadata as iTXt chuncks tag = NULL; FreeImage_GetMetadata(FIMD_XMP, dib, g_TagLib_XMPFieldName, &tag); if(tag && FreeImage_GetTagLength(tag)) { memset(&text_metadata, 0, sizeof(png_text)); text_metadata.compression = 1; // iTXt, none text_metadata.key = (char*)g_png_xmp_keyword; // keyword, 1-79 character description of "text" text_metadata.text = (char*)FreeImage_GetTagValue(tag); // comment, may be an empty string (ie "") text_metadata.text_length = FreeImage_GetTagLength(tag);// length of the text string text_metadata.itxt_length = FreeImage_GetTagLength(tag);// length of the itxt string text_metadata.lang = 0; // language code, 0-79 characters or a NULL pointer text_metadata.lang_key = 0; // keyword translated UTF-8 string, 0 or more chars or a NULL pointer // set the tag png_set_text(png_ptr, info_ptr, &text_metadata, 1); bResult &= TRUE; } // set the Exif-TIFF 'DateTime' metadata as a tIME chunk tag = NULL; FreeImage_GetMetadata(FIMD_EXIF_MAIN, dib, "DateTime", &tag); if(tag && FreeImage_GetTagLength(tag)) { int year, month, day, hour, minute, second; const char *value = (char*)FreeImage_GetTagValue(tag); if(sscanf(value, "%4d:%02d:%02d %2d:%02d:%02d", &year, &month, &day, &hour, &minute, &second) == 6) { mod_time.year = year; mod_time.month = month; mod_time.day = day; mod_time.hour = hour; mod_time.minute = minute; mod_time.second = second; png_set_tIME (png_ptr, info_ptr, &mod_time); } } return bResult; }
bool KIPIWriteImage::write2PNG(const QString& destPath) { /* check this out for b/w support: http://lxr.kde.org/source/playground/graphics/krita-exp/kis_png_converter.cpp#607 */ QFile file(destPath); if (!file.open(QIODevice::ReadWrite)) { qDebug() << "Failed to open PNG file for writing" ; return false; } uchar* data = 0; int bitsDepth = d->sixteenBit ? 16 : 8; png_color_8 sig_bit; png_bytep row_ptr; png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); png_infop info_ptr = png_create_info_struct(png_ptr); png_set_write_fn(png_ptr, (void*)&file, kipi_png_write_fn, kipi_png_flush_fn); if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) // Intel png_set_bgr(png_ptr); else // PPC png_set_swap_alpha(png_ptr); if (d->hasAlpha) { png_set_IHDR(png_ptr, info_ptr, d->width, d->height, bitsDepth, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); if (d->sixteenBit) data = new uchar[d->width * 8 * sizeof(uchar)]; else data = new uchar[d->width * 4 * sizeof(uchar)]; } else { png_set_IHDR(png_ptr, info_ptr, d->width, d->height, bitsDepth, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); if (d->sixteenBit) data = new uchar[d->width * 6 * sizeof(uchar)]; else data = new uchar[d->width * 3 * sizeof(uchar)]; } sig_bit.red = bitsDepth; sig_bit.green = bitsDepth; sig_bit.blue = bitsDepth; sig_bit.alpha = bitsDepth; png_set_sBIT(png_ptr, info_ptr, &sig_bit); png_set_compression_level(png_ptr, 9); // Write Software info. QString libpngver(QLatin1String(PNG_HEADER_VERSION_STRING)); libpngver.replace(QLatin1Char('\n'), QLatin1Char(' ')); QByteArray softAscii = libpngver.toLatin1(); png_text text; text.key = (png_charp)"Software"; text.text = softAscii.data(); text.compression = PNG_TEXT_COMPRESSION_zTXt; png_set_text(png_ptr, info_ptr, &(text), 1); png_write_info(png_ptr, info_ptr); png_set_shift(png_ptr, &sig_bit); png_set_packing(png_ptr); uchar* ptr = (uchar*)d->data.data(); uint x, y, j; for (y = 0; y < d->height; ++y) { if (cancel()) { delete [] data; file.close(); png_destroy_write_struct(&png_ptr, (png_infopp) & info_ptr); png_destroy_info_struct(png_ptr, (png_infopp) & info_ptr); return false; } j = 0; for (x = 0; x < d->width*bytesDepth(); x+=bytesDepth()) { if (d->sixteenBit) { if (d->hasAlpha) { data[j++] = ptr[x+1]; // Blue data[j++] = ptr[ x ]; data[j++] = ptr[x+3]; // Green data[j++] = ptr[x+2]; data[j++] = ptr[x+5]; // Red data[j++] = ptr[x+4]; data[j++] = ptr[x+7]; // Alpha data[j++] = ptr[x+6]; } else { data[j++] = ptr[x+1]; // Blue data[j++] = ptr[ x ]; data[j++] = ptr[x+3]; // Green data[j++] = ptr[x+2]; data[j++] = ptr[x+5]; // Red data[j++] = ptr[x+4]; } } else { if (d->hasAlpha) { data[j++] = ptr[ x ]; // Blue data[j++] = ptr[x+1]; // Green data[j++] = ptr[x+2]; // Red data[j++] = ptr[x+3]; // Alpha } else { data[j++] = ptr[ x ]; // Blue data[j++] = ptr[x+1]; // Green data[j++] = ptr[x+2]; // Red } } } row_ptr = (png_bytep) data; png_write_rows(png_ptr, &row_ptr, 1); ptr += (d->width * bytesDepth()); } delete [] data; png_write_end(png_ptr, info_ptr); png_destroy_write_struct(&png_ptr, (png_infopp) & info_ptr); png_destroy_info_struct(png_ptr, (png_infopp) & info_ptr); file.close(); return true; }
static int write_file(struct img_pixmap *img, struct img_io *io) { png_struct *png; png_info *info; png_text txt; struct img_pixmap tmpimg; unsigned char **rows; unsigned char *pixptr; int i, coltype; img_init(&tmpimg); if(!(png = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0))) { return -1; } if(!(info = png_create_info_struct(png))) { png_destroy_write_struct(&png, 0); return -1; } /* if the input image is floating-point, we need to convert it to integer */ if(img_is_float(img)) { if(img_copy(&tmpimg, img) == -1) { return -1; } if(img_to_integer(&tmpimg) == -1) { img_destroy(&tmpimg); return -1; } img = &tmpimg; } txt.compression = PNG_TEXT_COMPRESSION_NONE; txt.key = "Software"; txt.text = "libimago2"; txt.text_length = 0; if(setjmp(png_jmpbuf(png))) { png_destroy_write_struct(&png, &info); img_destroy(&tmpimg); return -1; } png_set_write_fn(png, io, write_func, flush_func); coltype = fmt_to_png_type(img->fmt); png_set_IHDR(png, info, img->width, img->height, 8, coltype, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); png_set_text(png, info, &txt, 1); if(!(rows = malloc(img->height * sizeof *rows))) { png_destroy_write_struct(&png, &info); img_destroy(&tmpimg); return -1; } pixptr = img->pixels; for(i=0; i<img->height; i++) { rows[i] = pixptr; pixptr += img->width * img->pixelsz; } png_set_rows(png, info, rows); png_write_png(png, info, 0, 0); png_write_end(png, info); png_destroy_write_struct(&png, &info); free(rows); img_destroy(&tmpimg); return 0; }
void PNGImageFileWriter::write( const char* filename, const ICanvas& image, const ImageAttributes& image_attributes) { // Retrieve canvas properties. const CanvasProperties& props = image.properties(); // todo: lift these limitations. assert(props.m_channel_count == 3 || props.m_channel_count == 4); // Open the file in write mode. FILE* fp = fopen(filename, "wb"); if (fp == 0) throw ExceptionIOError(); // Allocate and initialize the png_struct structure. png_structp png_ptr = png_create_write_struct( PNG_LIBPNG_VER_STRING, fp, error_callback, warning_callback); if (png_ptr == 0) { fclose(fp); throw ExceptionMemoryError(); } // Allocate the png_info structure. png_infop info_ptr = png_create_info_struct(png_ptr); if (info_ptr == 0) { png_destroy_write_struct(&png_ptr, 0); fclose(fp); throw ExceptionMemoryError(); } // Set up the output control. png_init_io(png_ptr, fp); // Set image information. png_set_IHDR( png_ptr, info_ptr, static_cast<png_uint_32>(props.m_canvas_width), static_cast<png_uint_32>(props.m_canvas_height), 8, // bit depth -- todo: allow higher bit depths props.m_channel_count == 4 ? PNG_COLOR_TYPE_RGB_ALPHA : PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); // Mark the image as being sRGB (implying specific gamma and color matching functions). // See http://www.vias.org/pngguide/chapter10_07.html for details about intents. png_set_sRGB_gAMA_and_cHRM( png_ptr, info_ptr, PNG_sRGB_INTENT_PERCEPTUAL); // todo: allow the user to select different intents // Set the number of significant bits for each of the R, G, B and A channels. // todo: are we required to provide these information? png_color_8 sig_bit; sig_bit.red = 8; sig_bit.green = 8; sig_bit.blue = 8; sig_bit.alpha = 8; sig_bit.gray = 8; // for completeness png_set_sBIT(png_ptr, info_ptr, &sig_bit); // Add image attributes. vector<png_text_struct> text_chunks; add_attributes( png_ptr, info_ptr, text_chunks, image_attributes); if (!text_chunks.empty()) { png_set_text( png_ptr, info_ptr, &text_chunks[0], static_cast<int>(text_chunks.size())); } // Write the file header information. png_write_info(png_ptr, info_ptr); // Create the temporary buffer holding one row of tiles in target format. vector<uint8> buffer( props.m_canvas_width * props.m_tile_height * props.m_channel_count); // Construct pointers to each row of the temporary buffer. vector<uint8*> buffer_rows(props.m_tile_height); for (size_t y = 0; y < props.m_tile_height; ++y) buffer_rows[y] = &buffer[y * props.m_canvas_width * props.m_channel_count]; // Loop over the rows of tiles. for (size_t tile_y = 0; tile_y < props.m_tile_count_y; ++tile_y) { // Convert this row of tiles to target format. for (size_t tile_x = 0; tile_x < props.m_tile_count_x; ++tile_x) { const Tile& tile = image.tile(tile_x, tile_y); assert(tile.get_height() <= props.m_tile_height); for (size_t y = 0; y < tile.get_height(); ++y) { for (size_t x = 0; x < tile.get_width(); ++x) { // Horizontal coordinate of the pixel in the temporary buffer. const size_t buffer_x = tile_x * props.m_tile_width + x; // Index of the pixel in the temporary buffer. const size_t buffer_index = (y * props.m_canvas_width + buffer_x) * props.m_channel_count; // Fetch the pixel at coordinates (x, y) in the tile, // perform format conversion if necessary, and store // the converted pixel into the temporary buffer. if (tile.get_channel_count() == 3) { Color3i pixel; tile.get_pixel(x, y, pixel); buffer[buffer_index + 0] = pixel[0]; buffer[buffer_index + 1] = pixel[1]; buffer[buffer_index + 2] = pixel[2]; } else { assert(tile.get_channel_count() == 4); Color4i pixel; tile.get_pixel(x, y, pixel); buffer[buffer_index + 0] = pixel[0]; buffer[buffer_index + 1] = pixel[1]; buffer[buffer_index + 2] = pixel[2]; buffer[buffer_index + 3] = pixel[3]; } } } } // Write this row of tiles to the file. const size_t row_count = image.tile(0, tile_y).get_height(); png_write_rows( png_ptr, &buffer_rows[0], static_cast<png_uint_32>(row_count)); } // Finish writing the file. png_write_end(png_ptr, 0); // Deallocate the png_struct and png_info structures. png_destroy_write_struct(&png_ptr, &info_ptr); // Deallocate text chunks. destroy_text_chunks(text_chunks); // Close the file. fclose(fp); }
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; }
/** * Generic .PNG file image writer. * @param name Filename, including extension. * @param callb Callback function for generating lines of pixels. * @param userdata User data, passed on to \a callb. * @param w Width of the image in pixels. * @param h Height of the image in pixels. * @param pixelformat Bits per pixel (bpp), either 8 or 32. * @param palette %Colour palette (for 8bpp images). * @return File was written successfully. * @see ScreenshotHandlerProc */ static bool MakePNGImage(const char *name, ScreenshotCallback *callb, void *userdata, uint w, uint h, int pixelformat, const Colour *palette) { png_color rq[256]; FILE *f; uint i, y, n; uint maxlines; uint bpp = pixelformat / 8; png_structp png_ptr; png_infop info_ptr; /* only implemented for 8bit and 32bit images so far. */ if (pixelformat != 8 && pixelformat != 32) return false; f = fopen(name, "wb"); if (f == NULL) return false; png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, const_cast<char *>(name), png_my_error, png_my_warning); if (png_ptr == NULL) { fclose(f); return false; } info_ptr = png_create_info_struct(png_ptr); if (info_ptr == NULL) { png_destroy_write_struct(&png_ptr, (png_infopp)NULL); fclose(f); return false; } if (setjmp(png_jmpbuf(png_ptr))) { png_destroy_write_struct(&png_ptr, &info_ptr); fclose(f); return false; } png_init_io(png_ptr, f); png_set_filter(png_ptr, 0, PNG_FILTER_NONE); png_set_IHDR(png_ptr, info_ptr, w, h, 8, pixelformat == 8 ? PNG_COLOR_TYPE_PALETTE : PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); #ifdef PNG_TEXT_SUPPORTED /* Try to add some game metadata to the PNG screenshot so * it's more useful for debugging and archival purposes. */ png_text_struct text[2]; memset(text, 0, sizeof(text)); text[0].key = const_cast<char *>("Software"); text[0].text = const_cast<char *>(_openttd_revision); text[0].text_length = strlen(_openttd_revision); text[0].compression = PNG_TEXT_COMPRESSION_NONE; char buf[8192]; char *p = buf; p += seprintf(p, lastof(buf), "Graphics set: %s (%u)\n", BaseGraphics::GetUsedSet()->name, BaseGraphics::GetUsedSet()->version); p = strecpy(p, "NewGRFs:\n", lastof(buf)); for (const GRFConfig *c = _game_mode == GM_MENU ? NULL : _grfconfig; c != NULL; c = c->next) { p += seprintf(p, lastof(buf), "%08X ", BSWAP32(c->ident.grfid)); p = md5sumToString(p, lastof(buf), c->ident.md5sum); p += seprintf(p, lastof(buf), " %s\n", c->filename); } p = strecpy(p, "\nCompanies:\n", lastof(buf)); const Company *c; FOR_ALL_COMPANIES(c) { if (c->ai_info == NULL) { p += seprintf(p, lastof(buf), "%2i: Human\n", (int)c->index); } else { p += seprintf(p, lastof(buf), "%2i: %s (v%d)\n", (int)c->index, c->ai_info->GetName(), c->ai_info->GetVersion()); } } text[1].key = const_cast<char *>("Description"); text[1].text = buf; text[1].text_length = p - buf; text[1].compression = PNG_TEXT_COMPRESSION_zTXt; png_set_text(png_ptr, info_ptr, text, 2); #endif /* PNG_TEXT_SUPPORTED */ if (pixelformat == 8) { /* convert the palette to the .PNG format. */ for (i = 0; i != 256; i++) { rq[i].red = palette[i].r; rq[i].green = palette[i].g; rq[i].blue = palette[i].b; } png_set_PLTE(png_ptr, info_ptr, rq, 256); } png_write_info(png_ptr, info_ptr); png_set_flush(png_ptr, 512); if (pixelformat == 32) { png_color_8 sig_bit; /* Save exact colour/alpha resolution */ sig_bit.alpha = 0; sig_bit.blue = 8; sig_bit.green = 8; sig_bit.red = 8; sig_bit.gray = 8; png_set_sBIT(png_ptr, info_ptr, &sig_bit); #if TTD_ENDIAN == TTD_LITTLE_ENDIAN png_set_bgr(png_ptr); png_set_filler(png_ptr, 0, PNG_FILLER_AFTER); #else png_set_filler(png_ptr, 0, PNG_FILLER_BEFORE); #endif /* TTD_ENDIAN == TTD_LITTLE_ENDIAN */ } /* use by default 64k temp memory */ maxlines = Clamp(65536 / w, 16, 128); /* now generate the bitmap bits */ void *buff = CallocT<uint8>(w * maxlines * bpp); // by default generate 128 lines at a time. y = 0; do { /* determine # lines to write */ n = min(h - y, maxlines); /* render the pixels into the buffer */ callb(userdata, buff, y, w, n); y += n; /* write them to png */ for (i = 0; i != n; i++) { png_write_row(png_ptr, (png_bytep)buff + i * w * bpp); } } while (y != h); png_write_end(png_ptr, info_ptr); png_destroy_write_struct(&png_ptr, &info_ptr); free(buff); fclose(f); return true; }
/* write a png file */ int write_png(char *file_name, int width, int height,BYTE *prgb_data) { int l=0; FILE *fp; png_structp png_ptr; png_infop info_ptr; png_text text_ptr[3]; png_bytep row_pointers[height]; /* open the file */ fp = fopen(file_name, "wb"); if (fp == NULL) return (1); /* 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. */ png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (png_ptr == NULL) { fclose(fp); return (2); } /* Allocate/initialize the image information data. */ info_ptr = png_create_info_struct(png_ptr); if (info_ptr == NULL) { fclose(fp); png_destroy_write_struct(&png_ptr, NULL); return (3); } /* 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 */ fclose(fp); png_destroy_write_struct(&png_ptr, &info_ptr); return (4); } /* set up the output control using standard C streams */ png_init_io(png_ptr, fp); /* turn on or off filtering, and/or choose specific filters. You can use either a single PNG_FILTER_VALUE_NAME or the bitwise OR of one or more PNG_FILTER_NAME masks. */ /* 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_AVE | PNG_FILTER_VALUE_AVE | PNG_FILTER_PAETH | PNG_FILTER_VALUE_PAETH| PNG_ALL_FILTERS);*/ /* set the zlib compression level */ //png_set_compression_level(png_ptr, // Z_BEST_COMPRESSION); /* set other zlib parameters */ //png_set_compression_mem_level(png_ptr, 8); //png_set_compression_strategy(png_ptr, // Z_DEFAULT_STRATEGY); //png_set_compression_window_bits(png_ptr, 15); //png_set_compression_method(png_ptr, 8); //png_set_compression_buffer_size(png_ptr, 8192); png_set_IHDR(png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); /* 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 = file_name; text_ptr[0].compression = PNG_TEXT_COMPRESSION_NONE; text_ptr[1].key = "Software"; text_ptr[1].text = "guvcview"; text_ptr[1].compression = PNG_TEXT_COMPRESSION_NONE; text_ptr[2].key = "Description"; text_ptr[2].text = "File generated by guvcview <http://guvcview.sourceforge.net>"; text_ptr[2].compression = PNG_TEXT_COMPRESSION_NONE; #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); /* Write the file header information. REQUIRED */ png_write_info(png_ptr, info_ptr); /* flip BGR pixels to RGB */ //png_set_bgr(png_ptr); /*?no longuer required?*/ /* Write the image data.*/ for (l = 0; l < height; l++) row_pointers[l] = prgb_data + l*width*3; png_write_image(png_ptr, row_pointers); /* You can write optional chunks like tEXt, zTXt, and tIME at the end * as well. Shouldn't be necessary in 1.1.0 and up as all the public * chunks are supported and you can use png_set_unknown_chunks() to * register unknown chunks into the info structure to be written out. */ /* It is REQUIRED to call this to finish writing the rest of the file */ png_write_end(png_ptr, info_ptr); /* If you png_malloced a palette, free it here (don't free info_ptr->palette, as recommended in versions 1.0.5m and earlier of this example; if libpng mallocs info_ptr->palette, libpng will free it). If you allocated it with malloc() instead of png_malloc(), use free() instead of png_free(). */ //png_free(png_ptr, palette); //palette=NULL; /* Similarly, if you png_malloced any data that you passed in with png_set_something(), such as a hist or trans array, free it here, when you can be sure that libpng is through with it. */ //png_free(png_ptr, trans); //trans=NULL; /* clean up after the write, and free any memory allocated */ png_destroy_write_struct(&png_ptr, &info_ptr); /* close the file */ fflush(fp); //flush data stream to file system if(fsync(fileno(fp)) || fclose(fp)) { perror("PNG ERROR - couldn't write to file"); return(5); } for(l=0;l<height;l++) { row_pointers[l]=NULL; } /* that's it */ return (0); }
short imb_savepng(struct ImBuf *ibuf, char *name, int flags) { png_structp png_ptr; png_infop info_ptr; unsigned char *pixels = 0; unsigned char *from, *to; png_bytepp row_pointers = 0; int i, bytesperpixel, color_type = PNG_COLOR_TYPE_GRAY; FILE *fp = 0; bytesperpixel = (ibuf->depth + 7) >> 3; if ((bytesperpixel > 4) || (bytesperpixel == 2)) { printf("imb_savepng: unsupported bytes per pixel: %d\n", bytesperpixel); return (0); } png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (png_ptr == NULL) { printf("Cannot png_create_write_struct\n"); return 0; } info_ptr = png_create_info_struct(png_ptr); if (info_ptr == NULL) { png_destroy_write_struct(&png_ptr, (png_infopp)NULL); printf("Cannot png_create_info_struct\n"); return 0; } if (setjmp(png_jmpbuf(png_ptr))) { png_destroy_write_struct(&png_ptr, &info_ptr); if (pixels) MEM_freeN(pixels); if (row_pointers) MEM_freeN(row_pointers); // printf("Aborting\n"); if (fp) { fflush(fp); fclose(fp); } return 0; } // copy image data pixels = MEM_mallocN(ibuf->x * ibuf->y * bytesperpixel * sizeof(unsigned char), "pixels"); if (pixels == NULL) { printf("Cannot allocate pixels array\n"); return 0; } from = (unsigned char *) ibuf->rect; to = pixels; switch (bytesperpixel) { case 4: color_type = PNG_COLOR_TYPE_RGBA; 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; 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; 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 = fopen(name, "wb"); if (!fp) { MEM_freeN(pixels); return 0; } png_init_io(png_ptr, fp); } /* 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); png_set_compression_level(png_ptr, Z_BEST_COMPRESSION); */ // png image settings png_set_IHDR(png_ptr, info_ptr, ibuf->x, ibuf->y, 8, color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); /* image text info */ if (ibuf->img_info) { png_text* imginfo; ImgInfo* iptr; int num_text = 0; iptr = ibuf->img_info; while (iptr) { num_text++; iptr = iptr->next; } imginfo = MEM_callocN(num_text*sizeof(png_text), "png_imginfo"); iptr = ibuf->img_info; num_text = 0; while (iptr) { imginfo[num_text].compression = PNG_TEXT_COMPRESSION_NONE; imginfo[num_text].key = iptr->key; imginfo[num_text].text = iptr->value; num_text++; iptr = iptr->next; } png_set_text(png_ptr, info_ptr, imginfo, num_text); MEM_freeN(imginfo); } // write the file header information png_write_info(png_ptr, info_ptr); // 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("Cannot allocate row-pointers array\n"); MEM_freeN(pixels); return 0; } // set the individual row-pointers to point at the correct offsets 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 MEM_freeN(pixels); MEM_freeN(row_pointers); png_destroy_write_struct(&png_ptr, &info_ptr); if (fp) { fflush(fp); fclose(fp); } return(1); }
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 }
/*! * 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; }
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; }
int begin(char* base_file_name, unsigned char *png_buf, long long png_length) { MY_PNG_READ_OFFSET = 0; PNG_LENGTH = png_length; ENTIRE_PNG_BUF = png_buf; if (png_sig_cmp(ENTIRE_PNG_BUF, 0, 8) != 0) { error(-1, "png_sig_cmp", E_INVALID); return -1; } DEBUG_PRINT(("Initial png size is %lld bytes\n", PNG_LENGTH)); my_png_meta *pm = calloc(1, sizeof(my_png_meta)); my_init_libpng(pm); //If libpng errors, we end up here if (setjmp(png_jmpbuf(pm->read_ptr))) { DEBUG_PRINT(("libpng called setjmp!\n")); my_deinit_libpng(pm); free(ENTIRE_PNG_BUF); error(-1, "libpng", "libpng encountered an error\n"); return -1; } //Normally a file, but instead make it our buffer void *read_io_ptr = png_get_io_ptr(pm->read_ptr); png_set_read_fn(pm->read_ptr, read_io_ptr, my_png_read_fn); //Transform all PNG image types to RGB int transforms = PNG_TRANSFORM_GRAY_TO_RGB | PNG_TRANSFORM_STRIP_ALPHA | PNG_TRANSFORM_EXPAND; png_read_png(pm->read_ptr, pm->info_ptr, transforms, NULL); //Now that it was read and transformed, its size will differ PNG_LENGTH = 0; //Lets collect our metadata struct ihdr_infos_s ihdr_infos; ihdr_infos.bit_depth = png_get_bit_depth(pm->read_ptr, pm->info_ptr); ihdr_infos.color_type = png_get_color_type(pm->read_ptr, pm->info_ptr); ihdr_infos.filter_method = png_get_filter_type(pm->read_ptr, pm->info_ptr); ihdr_infos.compression_type = png_get_compression_type(pm->read_ptr, pm->info_ptr); ihdr_infos.interlace_type = png_get_interlace_type(pm->read_ptr, pm->info_ptr); ihdr_infos.height = png_get_image_height(pm->read_ptr, pm->info_ptr); ihdr_infos.width = png_get_image_width(pm->read_ptr, pm->info_ptr); if (ihdr_infos.color_type != 2) { DEBUG_PRINT((E_INVALID)); free(ENTIRE_PNG_BUF); my_deinit_libpng(pm); DEBUG_PRINT(("Looks like libpng could not correctly convert to RGB\n")); return -1; } //Just in case we want to enable alpha, etc switch(ihdr_infos.color_type) { case 0: //greyscale case 3: //indexed ihdr_infos.bytes_per_pixel = 1; break; case 4: ihdr_infos.bytes_per_pixel = 2; break; //greyscale w/ alpha case 2: ihdr_infos.bytes_per_pixel = 3; break; //Truecolour (RGB) case 6: ihdr_infos.bytes_per_pixel = 4; break; //Truecolour w/ alpha default: error_fatal(1, "ihdr_infos", "Unknown image type"); //should never happen } ihdr_infos.scanline_len = (ihdr_infos.bytes_per_pixel * ihdr_infos.width) + 1; DEBUG_PRINT(("HEIGHT: %u\n", ihdr_infos.height)); DEBUG_PRINT(("WIDTH: %u\n", ihdr_infos.width)); DEBUG_PRINT(("BIT_DEPTH: %u\n", ihdr_infos.bit_depth)); // Don't compress, since we are merely copying it to memory, // we will be decompressing it again anyway png_set_compression_level(pm->write_ptr, Z_NO_COMPRESSION); void *write_io_ptr = png_get_io_ptr(pm->write_ptr); png_set_write_fn(pm->write_ptr, write_io_ptr, my_png_write_fn, my_png_dummy_flush); //Make sure we use all filters png_set_filter(pm->write_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); //Set our comment struct png_text_struct comment_struct; comment_struct.compression = -1; comment_struct.key = " Glitched by pnglitch.xyz "; comment_struct.text = NULL; comment_struct.text_length = 0; png_set_text(pm->write_ptr, pm->info_ptr, &comment_struct, 1); //Buffer is Written using callback my_png_write_fn to buffer //ENTIRE_PNG_BUF. PNG_LENGTH will be updated automatically by it png_write_png(pm->write_ptr, pm->info_ptr, PNG_TRANSFORM_IDENTITY, NULL); my_deinit_libpng(pm); DEBUG_PRINT(("libpng output buf is %lld bytes\n", PNG_LENGTH)); //Now that libpng has converted the image //and we have it in a buffer, we process it by hand with zlib struct z_stream_s inflate_stream; my_init_zlib(&inflate_stream); inflateInit(&inflate_stream); //Pointer to keep track of where we are unsigned char *pngp = ENTIRE_PNG_BUF; //Skip PNG Signature pngp += 8; //Get Header unsigned char ihdr_bytes_buf[4+4+13+4]; // size + label + content + crc buf_read(ihdr_bytes_buf, &pngp, 4+4+13+4); //When we run into non-idat chunks, we will want to preserve them. //The spec says there's no chunk that needs to go after IDAT, //so we can simply concatenate all of these chunks into a buffer //then write them all at once after the IHDR //ancillary chunks, eg comments unsigned char *ancil_chunks_buf = calloc(1,1); long long ancil_chunks_len = 0; unsigned char *unzip_idats_buf = calloc(1, 1); long unzip_buf_len = 1; long unzip_buf_offset = 0; long long zipped_idats_len = 0; //Length of all idats as we read them unsigned long accum_png_len = 8 + (4+4+13+4); int chunk_count = 0; printf("Uncompressing image data...\n"); while (1) { unsigned char chunk_label[4]; unsigned char chunk_len_buf[4]; buf_read(chunk_len_buf, &pngp, 4); //first 4 bytes are the length of data section long chunk_len = four_bytes_to_int(chunk_len_buf); accum_png_len += chunk_len + 4 + 4 + 4; // plus len, crc, label DEBUG_PRINT(("at %lu --> %lld\n", accum_png_len, PNG_LENGTH)); //leave at end of buffer if (accum_png_len >= PNG_LENGTH) break; //read the chunk label (name of this header) buf_read(chunk_label, &pngp, 4); DEBUG_PRINT(("Reading chunk %d with label '%c%c%c%c', size %ld\n", chunk_count, chunk_label[0], chunk_label[1], chunk_label[2], chunk_label[3], chunk_len)); chunk_count += 1; if (memcmp(chunk_label, "IDAT", 4) == 0) { zipped_idats_len += chunk_len; //read the chunk's data section unsigned char *raw_chunk_buf = calloc(chunk_len, 1); buf_read(raw_chunk_buf, &pngp, chunk_len); //Tell inflate to uncompress it inflate_stream.next_in = raw_chunk_buf; inflate_stream.avail_in = chunk_len; //Now uncompress it (resizes buffer automatically) unsigned char *check_uncompress = uncompress_buffer(&inflate_stream, unzip_idats_buf, &unzip_buf_len, &unzip_buf_offset); //Stop if error if (check_uncompress == NULL) { DEBUG_PRINT((E_GLITCH)); free(ancil_chunks_buf); free(raw_chunk_buf); free(unzip_idats_buf); free(ENTIRE_PNG_BUF); return -1; } //Moving on unzip_idats_buf = check_uncompress; free(raw_chunk_buf); pngp += 4; // skip CRC } else { //This is not an idat ancil_chunks_buf = realloc(ancil_chunks_buf, ancil_chunks_len + 4 + 4 + chunk_len + 4); //make room for new data //append length and label bytes append_bytes(ancil_chunks_buf, chunk_len_buf, &ancil_chunks_len, 4); append_bytes(ancil_chunks_buf, chunk_label, &ancil_chunks_len, 4); //append chunk data unsigned char *raw_chunk_buf = calloc(chunk_len, 1); buf_read(raw_chunk_buf, &pngp, chunk_len); append_bytes(ancil_chunks_buf, raw_chunk_buf, &ancil_chunks_len, chunk_len ); //append chunk crc unsigned char chunk_crc_buf[4]; buf_read(chunk_crc_buf, &pngp, 4); append_bytes(ancil_chunks_buf, chunk_crc_buf, &ancil_chunks_len, 4); free(raw_chunk_buf); DEBUG_PRINT(("ancillary chunks length: %lld\n", ancil_chunks_len)); } } //buf contains all idats uncompressed, concatenated unsigned long unzipped_idats_len = inflate_stream.total_out; unzip_idats_buf = realloc(unzip_idats_buf, unzipped_idats_len); //we already have ancillary chunks and idats, don't need the original free(ENTIRE_PNG_BUF); inflateEnd(&inflate_stream); printf("Uncompressed %lld bytes to %ld bytes\n", zipped_idats_len, unzipped_idats_len); printf("Glitching image data...\n"); for (int g=0;g<NUM_OUTPUT_FILES;g++) { //do glitches switch(g) { case 5: glitch_random(unzip_idats_buf, unzipped_idats_len, ihdr_infos.scanline_len, 0.0005); break; case 6: glitch_random_filter(unzip_idats_buf, unzipped_idats_len, ihdr_infos.scanline_len); break; default: glitch_filter(unzip_idats_buf, unzipped_idats_len, ihdr_infos.scanline_len, g); } //recompress so we can write them to file long long glitched_idats_len = 0; unsigned char *glitched_idats = zip_idats(unzip_idats_buf, unzipped_idats_len, &glitched_idats_len); if (glitched_idats == NULL) { DEBUG_PRINT((E_GLITCH)); free (unzip_idats_buf); free (ancil_chunks_buf); return -1; } char path[MAX_PATH_LENGTH]; bzero(path, MAX_PATH_LENGTH); snprintf(path, MAX_PATH_LENGTH, "%s%s%s-%d.png", OUTPUT_DIRECTORY, DIR_SEP, base_file_name, g); DEBUG_PRINT(("Output file name is %s\n", path)); FILE *outfp = fopen(path, "wb"); write_glitched_image(glitched_idats, glitched_idats_len, ihdr_bytes_buf, ancil_chunks_buf, ancil_chunks_len, outfp); printf("%s\n", path); fflush(stdout); fclose(outfp); free(glitched_idats); } free(ancil_chunks_buf); free(unzip_idats_buf); return 0; }
/* TextureToPng Inputs: data : This is an array of RGBA with 8 bits per channel. 4 bytes for each pixel. row_stride: Determines the amount of bytes per row of pixels. */ bool TextureToPng(u8* data, int row_stride, const std::string& filename, int width, int height, bool saveAlpha) { bool success = false; if (!data) return false; char title[] = "Dolphin Screenshot"; char title_key[] = "Title"; png_structp png_ptr = nullptr; png_infop info_ptr = nullptr; // Open file for writing (binary mode) File::IOFile fp(filename, "wb"); if (!fp.IsOpen()) { PanicAlertT("Screenshot failed: Could not open file \"%s\" (error %d)", filename.c_str(), errno); goto finalise; } // Initialize write structure png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); if (png_ptr == nullptr) { PanicAlert("Screenshot failed: Could not allocate write struct"); goto finalise; } // Initialize info structure info_ptr = png_create_info_struct(png_ptr); if (info_ptr == nullptr) { PanicAlert("Screenshot failed: Could not allocate info struct"); goto finalise; } // Setup Exception handling if (setjmp(png_jmpbuf(png_ptr))) { PanicAlert("Screenshot failed: Error during PNG creation"); goto finalise; } png_init_io(png_ptr, fp.GetHandle()); // Write header (8 bit color depth) 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); png_text title_text; title_text.compression = PNG_TEXT_COMPRESSION_NONE; title_text.key = title_key; title_text.text = title; png_set_text(png_ptr, info_ptr, &title_text, 1); png_write_info(png_ptr, info_ptr); // Write image data for (auto y = 0; y < height; ++y) { u8* row_ptr = (u8*)data + y * row_stride; u8* ptr = row_ptr; for (auto x = 0; x < row_stride / 4; ++x) { if (!saveAlpha) ptr[3] = 0xff; ptr += 4; } png_write_row(png_ptr, row_ptr); } // End write png_write_end(png_ptr, nullptr); success = true; finalise: if (info_ptr != nullptr) png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1); if (png_ptr != nullptr) png_destroy_write_struct(&png_ptr, (png_infopp)nullptr); return success; }