void PNGAPI png_set_rows(png_structp png_ptr, png_infop info_ptr, png_bytepp row_pointers) { png_debug1(1, "in %s storage function\n", "rows"); if (png_ptr == NULL || info_ptr == NULL) return; if(info_ptr->row_pointers && (info_ptr->row_pointers != row_pointers)) png_free_data(png_ptr, info_ptr, PNG_FREE_ROWS, 0); info_ptr->row_pointers = row_pointers; if(row_pointers) info_ptr->valid |= PNG_INFO_IDAT; }
bool PNGWriter::write_end() { if (setjmp(png_jmpbuf(png_ptr))) return false; png_write_end(png_ptr, NULL); // cleanup heap allocation png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1); png_destroy_write_struct(&png_ptr, (png_infopp)NULL); fclose(fp); return true; }
void PNGImage::saveImage(const char* outpath) { int error_code = 0; FILE* file_descriptor = nullptr; png_structp png_ptr = nullptr; png_infop info_ptr = nullptr; png_bytep row = nullptr; file_descriptor = fopen(outpath, "wb"); if (file_descriptor == nullptr) { error_code = 101; goto ERROR_PNG_write; } png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); if (png_ptr == nullptr) { error_code = 10; goto ERROR_PNG_write; } info_ptr = png_create_info_struct(png_ptr); if (info_ptr == nullptr) { error_code = 5; goto ERROR_PNG_write; } if (setjmp(png_jmpbuf(png_ptr))) { error_code = 6; goto ERROR_PNG_write; } png_init_io(png_ptr, file_descriptor); // Write header (8 bit colour depth) png_set_IHDR(png_ptr, info_ptr, m_width, m_height, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); png_write_info(png_ptr, info_ptr); // Allocate memory for one row (3 bytes per pixel - RGB) row = (png_bytep) malloc(3 * m_width * sizeof(png_byte)); for (int y = 0; y < m_height; ++y) { for (int x = 0; x < m_width; ++x) { setRGB(&(row[x * 3]), m_buffer[y * m_width + x]); } png_write_row(png_ptr, row); } png_write_end(png_ptr, nullptr); ERROR_PNG_write: if (error_code != 0) { m_error_code = error_code; ERR("Error while writing PNG file: %s, code %i", outpath, m_error_code); } if (file_descriptor != nullptr) fclose(file_descriptor); 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)NULL); if (row != nullptr) free(row); }
void PNGAPI png_set_iCCP(png_structp png_ptr, png_infop info_ptr, png_const_charp name, int compression_type, png_const_bytep profile, png_uint_32 proflen) { png_charp new_iccp_name; png_bytep new_iccp_profile; png_size_t length; png_debug1(1, "in %s storage function", "iCCP"); if (png_ptr == NULL || info_ptr == NULL || name == NULL || profile == NULL) return; length = png_strlen(name)+1; new_iccp_name = (png_charp)png_malloc_warn(png_ptr, length); if (new_iccp_name == NULL) { png_warning(png_ptr, "Insufficient memory to process iCCP chunk"); return; } png_memcpy(new_iccp_name, name, length); new_iccp_profile = (png_bytep)png_malloc_warn(png_ptr, proflen); if (new_iccp_profile == NULL) { png_free (png_ptr, new_iccp_name); png_warning(png_ptr, "Insufficient memory to process iCCP profile"); return; } png_memcpy(new_iccp_profile, profile, (png_size_t)proflen); png_free_data(png_ptr, info_ptr, PNG_FREE_ICCP, 0); info_ptr->iccp_proflen = proflen; info_ptr->iccp_name = new_iccp_name; info_ptr->iccp_profile = new_iccp_profile; /* Compression is always zero but is here so the API and info structure * does not have to change if we introduce multiple compression types */ info_ptr->iccp_compression = (png_byte)compression_type; info_ptr->free_me |= PNG_FREE_ICCP; info_ptr->valid |= PNG_INFO_iCCP; }
static void write_png(const char *filename, bitmap_t bitmap) { int j; FILE *png; png_structp png_ptr; png_infop info_ptr; png = open_binary_write(filename, "write_png"); png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (png_ptr == NULL) { fprintf(stderr, "write_png(): can't create write structure\n"); exit(1); } info_ptr = png_create_info_struct(png_ptr); if (info_ptr == NULL) { fprintf(stderr, "write_png(): can't create info structure\n"); exit (1); } if (setjmp(png_jmpbuf(png_ptr))) { fprintf(stderr, "write_png(): can't create png\n"); exit (1); } png_init_io(png_ptr, png); png_set_IHDR(png_ptr, info_ptr, bitmap.width, bitmap.height, 8, PNG_COLOR_TYPE_GRAY, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); /* --- write png */ png_write_info(png_ptr, info_ptr); for ( j = 0; j < bitmap.height; j++ ) png_write_row(png_ptr, bitmap.data[j]); png_write_end(png_ptr, NULL); /* --- */ fclose(png); png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1); png_destroy_write_struct(&png_ptr, (png_infopp)NULL); return; }
/* This is an internal routine to free any memory that the info struct is * pointing to before re-using it or freeing the struct itself. Recall * that png_free() checks for NULL pointers for us. */ void png_info_destroy(png_structp png_ptr, png_infop info_ptr) { png_debug(1, "in png_info_destroy\n"); png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1); #if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) if (png_ptr->num_chunk_list) { png_free(png_ptr, png_ptr->chunk_list); png_ptr->num_chunk_list=0; } #endif png_info_init(info_ptr); }
void MapEditor::finalise_lightmap() { delete compile_thread; compile_thread = 0; /* write PNG */ create_directory("maps", home_workdir); std::string filename = home_workdir + dir_separator + "maps" + dir_separator + wmap->get_name() + ".lmp"; FILE *f = fopen(filename.c_str(), "wb"); if (!f) { show_messagebox(Gui::MessageBoxIconError, "Light Map", "Saving failed: " + std::string(strerror(errno))); return; } png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0); png_infop info_ptr = png_create_info_struct(png_ptr); png_init_io(png_ptr, f); png_set_IHDR(png_ptr, info_ptr, lightmap_w, lightmap_h, 8, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); png_write_info(png_ptr, info_ptr); /* delete image after writing each row */ for (int y = 0; y < lightmap_h; y++) { png_write_row(png_ptr, lightmap[y]); delete[] lightmap[y]; } delete[] lightmap; /* close file */ png_write_end(png_ptr, 0); 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); } fclose(f); if (wmap) { wmap->create_lightmap(); } pop_window(); }
/* This is an internal routine to free any memory that the info struct is * pointing to before re-using it or freeing the struct itself. Recall * that png_free() checks for NULL pointers for us. */ void /* PRIVATE */ png_info_destroy(png_structp png_ptr, png_infop info_ptr) { png_debug(1, "in png_info_destroy\n"); png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1); #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) if (png_ptr->num_chunk_list) { png_free(png_ptr, png_ptr->chunk_list); png_ptr->chunk_list=NULL; png_ptr->num_chunk_list=0; } #endif png_info_init_3(&info_ptr, png_sizeof(png_info)); }
void PNGAPI png_set_PLTE(png_structp png_ptr, png_infop info_ptr, png_const_colorp palette, int num_palette) { png_debug1(1, "in %s storage function", "PLTE"); if (png_ptr == NULL || info_ptr == NULL) return; if (num_palette < 0 || num_palette > PNG_MAX_PALETTE_LENGTH) { if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) png_error(png_ptr, "Invalid palette length"); else { png_warning(png_ptr, "Invalid palette length"); return; } } /* It may not actually be necessary to set png_ptr->palette here; * we do it for backward compatibility with the way the png_handle_tRNS * function used to do the allocation. */ png_free_data(png_ptr, info_ptr, PNG_FREE_PLTE, 0); /* Changed in libpng-1.2.1 to allocate PNG_MAX_PALETTE_LENGTH instead * of num_palette entries, in case of an invalid PNG file that has * too-large sample values. */ png_ptr->palette = (png_colorp)png_calloc(png_ptr, PNG_MAX_PALETTE_LENGTH * png_sizeof(png_color)); png_memcpy(png_ptr->palette, palette, num_palette * png_sizeof(png_color)); info_ptr->palette = png_ptr->palette; info_ptr->num_palette = png_ptr->num_palette = (png_uint_16)num_palette; info_ptr->free_me |= PNG_FREE_PLTE; info_ptr->valid |= PNG_INFO_PLTE; }
void PNGAPI png_set_hIST(png_const_structrp png_ptr, png_inforp info_ptr, png_const_uint_16p hist) { int i; png_debug1(1, "in %s storage function", "hIST"); if (png_ptr == NULL || info_ptr == NULL) return; if (info_ptr->num_palette == 0 || info_ptr->num_palette > PNG_MAX_PALETTE_LENGTH) { png_warning(png_ptr, "Invalid palette size, hIST allocation skipped"); return; } png_free_data(png_ptr, info_ptr, PNG_FREE_HIST, 0); /* Changed from info->num_palette to PNG_MAX_PALETTE_LENGTH in * version 1.2.1 */ info_ptr->hist = png_voidcast(png_uint_16p, png_malloc_warn(png_ptr, PNG_MAX_PALETTE_LENGTH * (sizeof (png_uint_16)))); if (info_ptr->hist == NULL) { png_warning(png_ptr, "Insufficient memory for hIST chunk data"); return; } info_ptr->free_me |= PNG_FREE_HIST; for (i = 0; i < info_ptr->num_palette; i++) info_ptr->hist[i] = hist[i]; info_ptr->valid |= PNG_INFO_hIST; }
static void pngstdout(XImage *img) { png_structp png_struct_p; png_infop png_info_p; void (*convert)(unsigned char *, unsigned char *, XImage *); unsigned char *drow = NULL, *srow; int h; png_struct_p = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); png_info_p = png_create_info_struct(png_struct_p); if(!png_struct_p || !png_info_p || setjmp(png_jmpbuf(png_struct_p))) die("failed to initialize libpng"); png_init_io(png_struct_p, stdout); png_set_IHDR(png_struct_p, png_info_p, img->width, img->height, 8, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); png_write_info(png_struct_p, png_info_p); srow = (unsigned char *)img->data; drow = calloc(1, img->width * 4); /* output RGBA */ if(!drow) die("Can't calloc"); if(img->byte_order == LSBFirst) convert = convertrow_lsb; else convert = convertrow_msb; for(h = 0; h < img->height; h++) { convert(drow, srow, img); srow += img->bytes_per_line; png_write_row(png_struct_p, drow); } png_write_end(png_struct_p, NULL); free(drow); png_free_data(png_struct_p, png_info_p, PNG_FREE_ALL, -1); png_destroy_write_struct(&png_struct_p, NULL); }
void PNGAPI png_set_tRNS(png_structp png_ptr, png_infop info_ptr, png_bytep trans, int num_trans, png_color_16p trans_values) { png_debug1(1, "in %s storage function\n", "tRNS"); if (png_ptr == NULL || info_ptr == NULL) return; if (trans != NULL) { /* * It may not actually be necessary to set png_ptr->trans here; * we do it for backward compatibility with the way the png_handle_tRNS * function used to do the allocation. */ #ifdef PNG_FREE_ME_SUPPORTED png_free_data(png_ptr, info_ptr, PNG_FREE_TRNS, 0); #endif /* Changed from num_trans to 256 in version 1.2.1 */ png_ptr->trans = info_ptr->trans = (png_bytep)png_malloc(png_ptr, (png_uint_32)256); png_memcpy(info_ptr->trans, trans, (png_size_t)num_trans); #ifdef PNG_FREE_ME_SUPPORTED info_ptr->free_me |= PNG_FREE_TRNS; #else png_ptr->flags |= PNG_FLAG_FREE_TRNS; #endif } if (trans_values != NULL) { png_memcpy(&(info_ptr->trans_values), trans_values, sizeof(png_color_16)); if (num_trans == 0) num_trans = 1; } info_ptr->num_trans = (png_uint_16)num_trans; info_ptr->valid |= PNG_INFO_tRNS; }
void PNGAPI png_set_hIST(png_structp png_ptr, png_infop info_ptr, png_uint_16p hist) { int i; png_debug1(1, "in %s storage function\n", "hIST"); if (png_ptr == NULL || info_ptr == NULL) return; if (info_ptr->num_palette == 0) { png_warning(png_ptr, "Palette size 0, hIST allocation skipped."); return; } #ifdef PNG_FREE_ME_SUPPORTED png_free_data(png_ptr, info_ptr, PNG_FREE_HIST, 0); #endif /* Changed from info->num_palette to 256 in version 1.2.1 */ png_ptr->hist = (png_uint_16p)png_malloc_warn(png_ptr, (png_uint_32)(256 * sizeof (png_uint_16))); if (png_ptr->hist == NULL) { png_warning(png_ptr, "Insufficient memory for hIST chunk data."); return; } for (i = 0; i < info_ptr->num_palette; i++) png_ptr->hist[i] = hist[i]; info_ptr->hist = png_ptr->hist; info_ptr->valid |= PNG_INFO_hIST; #ifdef PNG_FREE_ME_SUPPORTED info_ptr->free_me |= PNG_FREE_HIST; #else png_ptr->flags |= PNG_FLAG_FREE_HIST; #endif }
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); }
void input_png_file(struct Options opts, struct PNGImage *img) { FILE *f; int i, y, num_trans; bool has_palette = false; png_byte *trans_alpha; png_color_16 *trans_values; bool *full_alpha; png_color *palette; f = fopen(opts.infile, "rb"); if (!f) { err(1, "Opening input png file '%s' failed", opts.infile); } img->png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!img->png) { errx(1, "Creating png structure failed"); } img->info = png_create_info_struct(img->png); if (!img->info) { errx(1, "Creating png info structure failed"); } /* Better error handling here? */ if (setjmp(png_jmpbuf(img->png))) { exit(1); } png_init_io(img->png, f); png_read_info(img->png, img->info); img->width = png_get_image_width(img->png, img->info); img->height = png_get_image_height(img->png, img->info); img->depth = png_get_bit_depth(img->png, img->info); img->type = png_get_color_type(img->png, img->info); if (img->type & PNG_COLOR_MASK_ALPHA) { png_set_strip_alpha(img->png); } if (img->depth != depth) { if (opts.verbose) { warnx("Image bit depth is not %i (is %i).", depth, img->depth); } } if (img->type == PNG_COLOR_TYPE_GRAY) { if (img->depth < 8) { png_set_expand_gray_1_2_4_to_8(img->png); } png_set_gray_to_rgb(img->png); } else { if (img->depth < 8) { png_set_expand_gray_1_2_4_to_8(img->png); } has_palette = png_get_PLTE(img->png, img->info, &palette, &colors); } if (png_get_tRNS(img->png, img->info, &trans_alpha, &num_trans, &trans_values)) { if (img->type == PNG_COLOR_TYPE_PALETTE) { full_alpha = malloc(sizeof(bool) * num_trans); for (i = 0; i < num_trans; i++) { if (trans_alpha[i] > 0) { full_alpha[i] = false; } else { full_alpha[i] = true; } } for (i = 0; i < num_trans; i++) { if (full_alpha[i]) { palette[i].red = 0xFF; palette[i].green = 0x00; palette[i].blue = 0xFF; /* * Set to the lightest color in the * palette. */ } } free(full_alpha); } else { /* Set to the lightest color in the image. */ } png_free_data(img->png, img->info, PNG_FREE_TRNS, -1); } if (has_palette) { /* Make sure palette only has the amount of colors you want. */ } else { /* * Eventually when this copies colors from the image itself, * make sure order is lightest to darkest. */ palette = malloc(sizeof(png_color) * colors); if (strcmp(opts.infile, "rgb.png") == 0) { palette[0].red = 0xFF; palette[0].green = 0xEF; palette[0].blue = 0xFF; palette[1].red = 0xF7; palette[1].green = 0xF7; palette[1].blue = 0x8C; palette[2].red = 0x94; palette[2].green = 0x94; palette[2].blue = 0xC6; palette[3].red = 0x39; palette[3].green = 0x39; palette[3].blue = 0x84; } else { palette[0].red = 0xFF; palette[0].green = 0xFF; palette[0].blue = 0xFF; palette[1].red = 0xA9; palette[1].green = 0xA9; palette[1].blue = 0xA9; palette[2].red = 0x55; palette[2].green = 0x55; palette[2].blue = 0x55; palette[3].red = 0x00; palette[3].green = 0x00; palette[3].blue = 0x00; } } /* * Also unfortunately, this sets it at 8 bit, and I can't find any * option to reduce to 2 or 1 bit. */ #if PNG_LIBPNG_VER < 10402 png_set_dither(img->png, palette, colors, colors, NULL, 1); #else png_set_quantize(img->png, palette, colors, colors, NULL, 1); #endif if (!has_palette) { png_set_PLTE(img->png, img->info, palette, colors); free(palette); } /* * If other useless chunks exist (sRGB, bKGD, pHYs, gAMA, cHRM, iCCP, * etc.) offer to remove? */ png_read_update_info(img->png, img->info); img->data = malloc(sizeof(png_byte *) * img->height); for (y = 0; y < img->height; y++) { img->data[y] = malloc(png_get_rowbytes(img->png, img->info)); } png_read_image(img->png, img->data); png_read_end(img->png, img->info); fclose(f); }
void PNGAPI png_free_data(png_structp png_ptr, png_infop info_ptr, png_uint_32 mask, int num) { png_debug(1, "in png_free_data\n"); if (png_ptr == NULL || info_ptr == NULL) return; #if defined(PNG_TEXT_SUPPORTED) /* free text item num or (if num == -1) all text items */ #ifdef PNG_FREE_ME_SUPPORTED if ((mask & PNG_FREE_TEXT) & info_ptr->free_me) #else if (mask & PNG_FREE_TEXT) #endif { if (num != -1) { if (info_ptr->text && info_ptr->text[num].key) { png_free(png_ptr, info_ptr->text[num].key); info_ptr->text[num].key = NULL; } } else { int i; for (i = 0; i < info_ptr->num_text; i++) png_free_data(png_ptr, info_ptr, PNG_FREE_TEXT, i); png_free(png_ptr, info_ptr->text); info_ptr->text = NULL; info_ptr->num_text=0; } } #endif #if defined(PNG_tRNS_SUPPORTED) /* free any tRNS entry */ #ifdef PNG_FREE_ME_SUPPORTED if ((mask & PNG_FREE_TRNS) & info_ptr->free_me) #else if ((mask & PNG_FREE_TRNS) && (png_ptr->flags & PNG_FLAG_FREE_TRNS)) #endif { png_free(png_ptr, info_ptr->trans); info_ptr->valid &= ~PNG_INFO_tRNS; #ifndef PNG_FREE_ME_SUPPORTED png_ptr->flags &= ~PNG_FLAG_FREE_TRNS; #endif info_ptr->trans = NULL; } #endif #if defined(PNG_sCAL_SUPPORTED) /* free any sCAL entry */ #ifdef PNG_FREE_ME_SUPPORTED if ((mask & PNG_FREE_SCAL) & info_ptr->free_me) #else if (mask & PNG_FREE_SCAL) #endif { #if defined(PNG_FIXED_POINT_SUPPORTED) && !defined(PNG_FLOATING_POINT_SUPPORTED) png_free(png_ptr, info_ptr->scal_s_width); png_free(png_ptr, info_ptr->scal_s_height); info_ptr->scal_s_width = NULL; info_ptr->scal_s_height = NULL; #endif info_ptr->valid &= ~PNG_INFO_sCAL; } #endif #if defined(PNG_pCAL_SUPPORTED) /* free any pCAL entry */ #ifdef PNG_FREE_ME_SUPPORTED if ((mask & PNG_FREE_PCAL) & info_ptr->free_me) #else if (mask & PNG_FREE_PCAL) #endif { png_free(png_ptr, info_ptr->pcal_purpose); png_free(png_ptr, info_ptr->pcal_units); info_ptr->pcal_purpose = NULL; info_ptr->pcal_units = NULL; if (info_ptr->pcal_params != NULL) { int i; for (i = 0; i < (int)info_ptr->pcal_nparams; i++) { png_free(png_ptr, info_ptr->pcal_params[i]); info_ptr->pcal_params[i]=NULL; } png_free(png_ptr, info_ptr->pcal_params); info_ptr->pcal_params = NULL; } info_ptr->valid &= ~PNG_INFO_pCAL; } #endif #if defined(PNG_iCCP_SUPPORTED) /* free any iCCP entry */ #ifdef PNG_FREE_ME_SUPPORTED if ((mask & PNG_FREE_ICCP) & info_ptr->free_me) #else if (mask & PNG_FREE_ICCP) #endif { png_free(png_ptr, info_ptr->iccp_name); png_free(png_ptr, info_ptr->iccp_profile); info_ptr->iccp_name = NULL; info_ptr->iccp_profile = NULL; info_ptr->valid &= ~PNG_INFO_iCCP; } #endif #if defined(PNG_sPLT_SUPPORTED) /* free a given sPLT entry, or (if num == -1) all sPLT entries */ #ifdef PNG_FREE_ME_SUPPORTED if ((mask & PNG_FREE_SPLT) & info_ptr->free_me) #else if (mask & PNG_FREE_SPLT) #endif { if (num != -1) { if(info_ptr->splt_palettes) { png_free(png_ptr, info_ptr->splt_palettes[num].name); png_free(png_ptr, info_ptr->splt_palettes[num].entries); info_ptr->splt_palettes[num].name = NULL; info_ptr->splt_palettes[num].entries = NULL; } } else { if(info_ptr->splt_palettes_num) { int i; for (i = 0; i < (int)info_ptr->splt_palettes_num; i++) png_free_data(png_ptr, info_ptr, PNG_FREE_SPLT, i); png_free(png_ptr, info_ptr->splt_palettes); info_ptr->splt_palettes = NULL; info_ptr->splt_palettes_num = 0; } info_ptr->valid &= ~PNG_INFO_sPLT; } } #endif #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) #ifdef PNG_FREE_ME_SUPPORTED if ((mask & PNG_FREE_UNKN) & info_ptr->free_me) #else if (mask & PNG_FREE_UNKN) #endif { if (num != -1) { if(info_ptr->unknown_chunks) { png_free(png_ptr, info_ptr->unknown_chunks[num].data); info_ptr->unknown_chunks[num].data = NULL; } } else { int i; if(info_ptr->unknown_chunks_num) { for (i = 0; i < (int)info_ptr->unknown_chunks_num; i++) png_free_data(png_ptr, info_ptr, PNG_FREE_UNKN, i); png_free(png_ptr, info_ptr->unknown_chunks); info_ptr->unknown_chunks = NULL; info_ptr->unknown_chunks_num = 0; } } } #endif #if defined(PNG_hIST_SUPPORTED) /* free any hIST entry */ #ifdef PNG_FREE_ME_SUPPORTED if ((mask & PNG_FREE_HIST) & info_ptr->free_me) #else if ((mask & PNG_FREE_HIST) && (png_ptr->flags & PNG_FLAG_FREE_HIST)) #endif { png_free(png_ptr, info_ptr->hist); info_ptr->hist = NULL; info_ptr->valid &= ~PNG_INFO_hIST; #ifndef PNG_FREE_ME_SUPPORTED png_ptr->flags &= ~PNG_FLAG_FREE_HIST; #endif } #endif /* free any PLTE entry that was internally allocated */ #ifdef PNG_FREE_ME_SUPPORTED if ((mask & PNG_FREE_PLTE) & info_ptr->free_me) #else if ((mask & PNG_FREE_PLTE) && (png_ptr->flags & PNG_FLAG_FREE_PLTE)) #endif { png_zfree(png_ptr, info_ptr->palette); info_ptr->palette = NULL; info_ptr->valid &= ~PNG_INFO_PLTE; #ifndef PNG_FREE_ME_SUPPORTED png_ptr->flags &= ~PNG_FLAG_FREE_PLTE; #endif info_ptr->num_palette = 0; } #if defined(PNG_INFO_IMAGE_SUPPORTED) /* free any image bits attached to the info structure */ #ifdef PNG_FREE_ME_SUPPORTED if ((mask & PNG_FREE_ROWS) & info_ptr->free_me) #else if (mask & PNG_FREE_ROWS) #endif { if(info_ptr->row_pointers) { int row; for (row = 0; row < (int)info_ptr->height; row++) { png_free(png_ptr, info_ptr->row_pointers[row]); info_ptr->row_pointers[row]=NULL; } png_free(png_ptr, info_ptr->row_pointers); info_ptr->row_pointers=NULL; } info_ptr->valid &= ~PNG_INFO_IDAT; } #endif #ifdef PNG_FREE_ME_SUPPORTED if(num == -1) info_ptr->free_me &= ~mask; else info_ptr->free_me &= ~(mask & ~PNG_FREE_MUL); #endif }
void SavePng(const Image<unsigned char>& image, const pangolin::PixelFormat& fmt, const std::string& filename, bool top_line_first) { PANGOLIN_UNUSED(image); PANGOLIN_UNUSED(filename); PANGOLIN_UNUSED(top_line_first); // Check image has supported bit depth for(unsigned int i=1; i < fmt.channels; ++i) { if( fmt.channel_bits[i] != fmt.channel_bits[0] ) { throw std::runtime_error("PNG Saving only supported for images where each channel has the same bit depth."); } } #ifdef HAVE_PNG FILE *fp; png_structp png_ptr; png_infop info_ptr; // Open file for writing (binary mode) fp = fopen(filename.c_str(), "wb"); if (fp == NULL) { throw std::runtime_error( "PNG Error: Could not open file '" + filename + "' for writing" ); } // Initialize write structure png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (png_ptr == NULL) { fclose(fp); throw std::runtime_error( "PNG Error: Could not allocate write struct." ); } // Initialize info structure info_ptr = png_create_info_struct(png_ptr); if (info_ptr == NULL) { png_destroy_write_struct(&png_ptr, (png_infopp)NULL); fclose(fp); throw std::runtime_error( "PNG Error: Could not allocate info struct." ); } // Setup Exception handling if (setjmp(png_jmpbuf(png_ptr))) { png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1); png_destroy_write_struct(&png_ptr, (png_infopp)NULL); fclose(fp); throw std::runtime_error( "PNG Error: Error during png creation." ); } png_init_io(png_ptr, fp); const int bit_depth = fmt.channel_bits[0]; int colour_type; switch (fmt.channels) { case 1: colour_type = PNG_COLOR_TYPE_GRAY; break; case 2: colour_type = PNG_COLOR_TYPE_GRAY_ALPHA; break; case 3: colour_type = PNG_COLOR_TYPE_RGB; break; case 4: colour_type = PNG_COLOR_TYPE_RGBA; break; default: throw std::runtime_error( "PNG Error: unexpected image channel number"); } // Write header png_set_IHDR( png_ptr, info_ptr, (png_uint_32)image.w, (png_uint_32)image.h, bit_depth, colour_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT ); // Setup rows to write: std::vector<png_bytep> rows(image.h); if(top_line_first) { for (unsigned int y = 0; y< image.h; y++) { rows[y] = image.ptr + y*image.pitch; } }else{ for (unsigned int y = 0; y< image.h; y++) { rows[y] = image.ptr + (image.h-1-y)*image.pitch; } } png_set_rows(png_ptr,info_ptr, &rows[0]); // Write image data: switch to little-endian byte order, to match host. png_write_png(png_ptr,info_ptr, PNG_TRANSFORM_SWAP_ENDIAN, 0); // Free resources fclose(fp); png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1); png_destroy_write_struct(&png_ptr, (png_infopp)NULL); #else throw std::runtime_error("PNG Support not enabled. Please rebuild Pangolin."); #endif }
int export_png_rgb_16(image *i, output *output) { const char *fn; int shouldclose; png_structp png_ptr; png_infop info_ptr; png_uint_32 n, x, y, pi; pixelref src; colour rgb; png_bytep pp; uint16_t *bp; FILE *fp; pixel_converter_fn converter; converter = convert_pixels(i->format, PF_RGB, 16); if(!converter) { fprintf(stderr, "cannot locate a suitable format converter for RGB-16\n"); return -1; } fn = output_filename(output, i, &shouldclose); fp = fopen(fn, "wb"); if(!fp) { return -1; } png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if(!png_ptr) { return -1; } info_ptr = png_create_info_struct(png_ptr); if(!info_ptr) { return -1; } png_init_io(png_ptr, fp); png_set_IHDR(png_ptr, info_ptr, i->width, i->height, 16, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); png_write_info(png_ptr, info_ptr); pp = (png_bytep) malloc(i->width * 3 * sizeof(uint16_t)); bp = (uint16_t *) pp; src.pixel[0] = i->pixels[0]; src.pixel[1] = i->pixels[1]; src.pixel[2] = i->pixels[2]; for(y = 0, pi = 0; y < i->height; y++) { for(n = 0, x = 0; x < i->width; x++, pi++) { converter(src, &rgb); src.pixel[0]++; src.pixel[1]++; src.pixel[2]++; bp[n] = htons(rgb.p.rgb.r); n++; bp[n] = htons(rgb.p.rgb.g); n++; bp[n] = htons(rgb.p.rgb.b); n++; } png_write_row(png_ptr, pp); } png_write_end(png_ptr, NULL); free(pp); fclose(fp); png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1); png_destroy_write_struct(&png_ptr, NULL); return 0; }
/* * Reduce the image type to a lower bit depth and color type, * by removing redundant bits. * Possible reductions: 16bpp to 8bpp; RGB to gray; strip alpha. * The parameter reductions indicates the intended reductions. * The function returns the successful reductions. * All reductions are performed in a single step. */ png_uint_32 /* PRIVATE */ opng_reduce_bits(png_structp png_ptr, png_infop info_ptr, png_uint_32 reductions) { png_bytepp row_ptr; png_bytep src_ptr, dest_ptr; png_uint_32 height, width, i, j; unsigned int src_bit_depth, dest_bit_depth; unsigned int src_byte_depth, dest_byte_depth; unsigned int src_color_type, dest_color_type; unsigned int src_channels, dest_channels; unsigned int src_sample_size, dest_sample_size; unsigned int dest_pixel_depth; unsigned int src_offset_alpha; unsigned int tran_tbl[8]; unsigned int k; opng_debug(1, "in opng_reduce_bits"); /* See which reductions may be performed. */ reductions = opng_analyze_bits(png_ptr, info_ptr, reductions); /* Strip the filler even if it is not an alpha channel. */ if (png_ptr->transformations & PNG_FILLER) reductions |= OPNG_REDUCE_STRIP_ALPHA; if (reductions == OPNG_REDUCE_NONE) return OPNG_REDUCE_NONE; /* nothing can be reduced */ /* Compute the new image parameters bit_depth, color_type, etc. */ src_bit_depth = info_ptr->bit_depth; OPNG_ASSERT(src_bit_depth >= 8); if (reductions & OPNG_REDUCE_16_TO_8) { OPNG_ASSERT(src_bit_depth == 16); dest_bit_depth = 8; } else dest_bit_depth = src_bit_depth; src_byte_depth = src_bit_depth / 8; dest_byte_depth = dest_bit_depth / 8; src_color_type = dest_color_type = info_ptr->color_type; if (reductions & OPNG_REDUCE_RGB_TO_GRAY) { OPNG_ASSERT(src_color_type & PNG_COLOR_MASK_COLOR); dest_color_type &= ~PNG_COLOR_MASK_COLOR; } if (reductions & OPNG_REDUCE_STRIP_ALPHA) { OPNG_ASSERT(src_color_type & PNG_COLOR_MASK_ALPHA); dest_color_type &= ~PNG_COLOR_MASK_ALPHA; } src_channels = (png_ptr->usr_channels > 0) ? png_ptr->usr_channels : info_ptr->channels; dest_channels = ((dest_color_type & PNG_COLOR_MASK_COLOR) ? 3 : 1) + ((dest_color_type & PNG_COLOR_MASK_ALPHA) ? 1 : 0); src_sample_size = src_channels * src_byte_depth; dest_sample_size = dest_channels * dest_byte_depth; dest_pixel_depth = dest_channels * dest_bit_depth; if (!(png_ptr->transformations & PNG_FILLER) || (png_ptr->flags & PNG_FLAG_FILLER_AFTER)) src_offset_alpha = (src_channels - 1) * src_byte_depth; else src_offset_alpha = 0; /* Pre-compute the intra-sample translation table. */ for (k = 0; k < 4 * dest_byte_depth; ++k) tran_tbl[k] = k * src_bit_depth / dest_bit_depth; /* If rgb -> gray and the alpha channel remains in the right, shift the alpha component two positions to the left. */ if ((reductions & OPNG_REDUCE_RGB_TO_GRAY) && (dest_color_type & PNG_COLOR_MASK_ALPHA) && (src_offset_alpha != 0)) { tran_tbl[dest_byte_depth] = tran_tbl[3 * dest_byte_depth]; if (dest_byte_depth == 2) tran_tbl[dest_byte_depth + 1] = tran_tbl[3 * dest_byte_depth + 1]; } /* If alpha is in the left, and it is being stripped, shift the components that come after it. */ if ((src_channels == 2 || src_channels == 4) /* alpha or filler */ && !(dest_color_type & PNG_COLOR_MASK_ALPHA) && (src_offset_alpha == 0)) { for (k = 0; k < dest_sample_size; ) { if (dest_byte_depth == 1) { tran_tbl[k] = tran_tbl[k + 1]; ++k; } else { tran_tbl[k] = tran_tbl[k + 2]; tran_tbl[k + 1] = tran_tbl[k + 3]; k += 2; } } } /* Translate the samples to the new image type. */ OPNG_ASSERT(src_sample_size > dest_sample_size); row_ptr = info_ptr->row_pointers; height = info_ptr->height; width = info_ptr->width; for (i = 0; i < height; ++i, ++row_ptr) { src_ptr = dest_ptr = *row_ptr; for (j = 0; j < width; ++j) { for (k = 0; k < dest_sample_size; ++k) dest_ptr[k] = src_ptr[tran_tbl[k]]; src_ptr += src_sample_size; dest_ptr += dest_sample_size; } } #if defined(PNG_bKGD_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) /* Update the ancillary chunk info. */ if (info_ptr->valid & PNG_INFO_bKGD) { png_color_16p background = &info_ptr->background; if (reductions & OPNG_REDUCE_16_TO_8) { background->red &= 255; background->green &= 255; background->blue &= 255; background->gray &= 255; } if (reductions & OPNG_REDUCE_RGB_TO_GRAY) background->gray = background->red; } #endif #if defined(PNG_sBIT_SUPPORTED) if (info_ptr->valid & PNG_INFO_sBIT) { png_color_8p sig_bits = &info_ptr->sig_bit; if (reductions & OPNG_REDUCE_16_TO_8) { if (sig_bits->red > 8) png_ptr->sig_bit.red = sig_bits->red = 8; if (sig_bits->green > 8) png_ptr->sig_bit.green = sig_bits->green = 8; if (sig_bits->blue > 8) png_ptr->sig_bit.blue = sig_bits->blue = 8; if (sig_bits->gray > 8) png_ptr->sig_bit.gray = sig_bits->gray = 8; if (sig_bits->alpha > 8) png_ptr->sig_bit.alpha = sig_bits->alpha = 8; } if (reductions & OPNG_REDUCE_RGB_TO_GRAY) { png_byte max_sig_bit = sig_bits->red; if (max_sig_bit < sig_bits->green) max_sig_bit = sig_bits->green; if (max_sig_bit < sig_bits->blue) max_sig_bit = sig_bits->blue; png_ptr->sig_bit.gray = sig_bits->gray = max_sig_bit; } } #endif if (info_ptr->valid & PNG_INFO_tRNS) { png_color_16p trans_color = &info_ptr->trans_color; if (reductions & OPNG_REDUCE_16_TO_8) { if (trans_color->red % 257 == 0 && trans_color->green % 257 == 0 && trans_color->blue % 257 == 0 && trans_color->gray % 257 == 0) { trans_color->red &= 255; trans_color->green &= 255; trans_color->blue &= 255; trans_color->gray &= 255; } else { /* 16-bit tRNS in 8-bit samples: all pixels are 100% opaque. */ png_free_data(png_ptr, info_ptr, PNG_FREE_TRNS, -1); info_ptr->valid &= ~PNG_INFO_tRNS; } } if (reductions & OPNG_REDUCE_RGB_TO_GRAY) { if (trans_color->red == trans_color->green || trans_color->red == trans_color->blue) trans_color->gray = trans_color->red; else { /* Non-gray tRNS in grayscale image: all pixels are 100% opaque. */ png_free_data(png_ptr, info_ptr, PNG_FREE_TRNS, -1); info_ptr->valid &= ~PNG_INFO_tRNS; } } } /* Update the image info. */ png_ptr->rowbytes = info_ptr->rowbytes = 0; png_ptr->bit_depth = info_ptr->bit_depth = (png_byte)dest_bit_depth; png_ptr->color_type = info_ptr->color_type = (png_byte)dest_color_type; png_ptr->channels = info_ptr->channels = (png_byte)dest_channels; png_ptr->pixel_depth = info_ptr->pixel_depth = (png_byte)dest_pixel_depth; if (reductions & OPNG_REDUCE_STRIP_ALPHA) { png_ptr->transformations &= ~PNG_FILLER; if (png_ptr->usr_channels > 0) --png_ptr->usr_channels; } return reductions; }
/* * Reduce the palette (only the fast method is implemented). * The parameter reductions indicates the intended reductions. * The function returns the successful reductions. */ png_uint_32 /* PRIVATE */ opng_reduce_palette(png_structp png_ptr, png_infop info_ptr, png_uint_32 reductions) { png_uint_32 result; png_colorp palette; png_bytep trans_alpha; png_bytepp rows; png_uint_32 width, height, i, j; png_byte is_used[256]; int num_palette, num_trans, last_color_index, last_trans_index, is_gray, k; png_color_16 gray_trans; png_byte crt_trans_value, last_trans_value; opng_debug(1, "in opng_reduce_palette"); height = info_ptr->height; width = info_ptr->width; palette = info_ptr->palette; num_palette = info_ptr->num_palette; rows = info_ptr->row_pointers; if (info_ptr->valid & PNG_INFO_tRNS) { trans_alpha = info_ptr->trans_alpha; num_trans = info_ptr->num_trans; OPNG_ASSERT(trans_alpha != NULL && num_trans > 0); } else { trans_alpha = NULL; num_trans = 0; } /* Analyze the possible reductions. */ /* Also check the integrity of PLTE and tRNS. */ opng_analyze_sample_usage(png_ptr, info_ptr, is_used); /* Palette-to-gray does not work (yet) if the bit depth is below 8. */ is_gray = (reductions & OPNG_REDUCE_PALETTE_TO_GRAY) && (info_ptr->bit_depth == 8); last_color_index = last_trans_index = -1; for (k = 0; k < 256; ++k) { if (!is_used[k]) continue; last_color_index = k; if (k < num_trans && trans_alpha[k] < 255) last_trans_index = k; if (is_gray) if (palette[k].red != palette[k].green || palette[k].red != palette[k].blue) is_gray = 0; } OPNG_ASSERT(last_color_index >= 0); if (last_color_index >= num_palette) { png_warning(png_ptr, "Too few colors in palette"); /* Fix the palette by adding blank entries at the end. */ num_palette = last_color_index + 1; info_ptr->num_palette = (png_uint_16)num_palette; } if (num_trans > num_palette) { png_warning(png_ptr, "Too many alpha values in tRNS"); info_ptr->num_trans = info_ptr->num_palette; } num_trans = last_trans_index + 1; OPNG_ASSERT(num_trans <= num_palette); /* Check if tRNS can be reduced to grayscale. */ if (is_gray && num_trans > 0) { gray_trans.gray = palette[last_trans_index].red; last_trans_value = trans_alpha[last_trans_index]; for (k = 0; k <= last_color_index; ++k) { if (!is_used[k]) continue; if (k <= last_trans_index) { crt_trans_value = trans_alpha[k]; /* Cannot reduce if different colors have transparency. */ if (crt_trans_value < 255 && palette[k].red != gray_trans.gray) { is_gray = 0; break; } } else crt_trans_value = 255; /* Cannot reduce if same color has multiple transparency levels. */ if (palette[k].red == gray_trans.gray && crt_trans_value != last_trans_value) { is_gray = 0; break; } } } /* Initialize result value. */ result = OPNG_REDUCE_NONE; /* Remove tRNS if possible. */ if ((info_ptr->valid & PNG_INFO_tRNS) && num_trans == 0) { png_free_data(png_ptr, info_ptr, PNG_FREE_TRNS, -1); info_ptr->valid &= ~PNG_INFO_tRNS; result = OPNG_REDUCE_PALETTE_FAST; } if (reductions & OPNG_REDUCE_PALETTE_FAST) { if (num_palette != last_color_index + 1) { /* Reduce PLTE. */ /* hIST is reduced automatically. */ info_ptr->num_palette = (png_uint_16)(last_color_index + 1); result = OPNG_REDUCE_PALETTE_FAST; } if ((info_ptr->valid & PNG_INFO_tRNS) && (int)info_ptr->num_trans != num_trans) { /* Reduce tRNS. */ info_ptr->num_trans = (png_uint_16)num_trans; result = OPNG_REDUCE_PALETTE_FAST; } } if (reductions & OPNG_REDUCE_8_TO_4_2_1) result |= opng_reduce_palette_bits(png_ptr, info_ptr, reductions); if (info_ptr->bit_depth < 8 || !is_gray) return result; /* Reduce palette -> grayscale. */ for (i = 0; i < height; ++i) for (j = 0; j < width; ++j) rows[i][j] = palette[rows[i][j]].red; #if defined(PNG_bKGD_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) /* Update the ancillary chunk info. */ if (info_ptr->valid & PNG_INFO_bKGD) info_ptr->background.gray = palette[info_ptr->background.index].red; #endif #if defined(PNG_hIST_SUPPORTED) if (info_ptr->valid & PNG_INFO_hIST) { png_free_data(png_ptr, info_ptr, PNG_FREE_HIST, -1); info_ptr->valid &= ~PNG_INFO_hIST; } #endif #if defined(PNG_sBIT_SUPPORTED) if (info_ptr->valid & PNG_INFO_sBIT) { png_color_8p sig_bit_ptr = &info_ptr->sig_bit; png_byte max_sig_bit = sig_bit_ptr->red; if (max_sig_bit < sig_bit_ptr->green) max_sig_bit = sig_bit_ptr->green; if (max_sig_bit < sig_bit_ptr->blue) max_sig_bit = sig_bit_ptr->blue; png_ptr->sig_bit.gray = info_ptr->sig_bit.gray = max_sig_bit; } #endif if (info_ptr->valid & PNG_INFO_tRNS) png_set_tRNS(png_ptr, info_ptr, NULL, 0, &gray_trans); /* Update the image info. */ png_ptr->rowbytes = info_ptr->rowbytes = 0; png_ptr->color_type = info_ptr->color_type = PNG_COLOR_TYPE_GRAY; png_free_data(png_ptr, info_ptr, PNG_FREE_PLTE, -1); info_ptr->valid &= ~PNG_INFO_PLTE; return OPNG_REDUCE_PALETTE_TO_GRAY; /* ignore the former result */ }
void PNGAPI png_set_iCCP(png_const_structrp png_ptr, png_inforp info_ptr, png_const_charp name, int compression_type, png_const_bytep profile, png_uint_32 proflen) { png_charp new_iccp_name; png_bytep new_iccp_profile; png_size_t length; png_debug1(1, "in %s storage function", "iCCP"); if (png_ptr == NULL || info_ptr == NULL || name == NULL || profile == NULL) return; if (compression_type != PNG_COMPRESSION_TYPE_BASE) png_app_error(png_ptr, "Invalid iCCP compression method"); /* Set the colorspace first because this validates the profile; do not * override previously set app cHRM or gAMA here (because likely as not the * application knows better than libpng what the correct values are.) Pass * the info_ptr color_type field to png_colorspace_set_ICC because in the * write case it has not yet been stored in png_ptr. */ { int result = png_colorspace_set_ICC(png_ptr, &info_ptr->colorspace, name, proflen, profile, info_ptr->color_type); png_colorspace_sync_info(png_ptr, info_ptr); /* Don't do any of the copying if the profile was bad, or inconsistent. */ if (result == 0) return; /* But do write the gAMA and cHRM chunks from the profile. */ info_ptr->colorspace.flags |= PNG_COLORSPACE_FROM_gAMA|PNG_COLORSPACE_FROM_cHRM; } length = strlen(name)+1; new_iccp_name = png_voidcast(png_charp, png_malloc_warn(png_ptr, length)); if (new_iccp_name == NULL) { png_benign_error(png_ptr, "Insufficient memory to process iCCP chunk"); return; } memcpy(new_iccp_name, name, length); new_iccp_profile = png_voidcast(png_bytep, png_malloc_warn(png_ptr, proflen)); if (new_iccp_profile == NULL) { png_free(png_ptr, new_iccp_name); png_benign_error(png_ptr, "Insufficient memory to process iCCP profile"); return; } memcpy(new_iccp_profile, profile, proflen); png_free_data(png_ptr, info_ptr, PNG_FREE_ICCP, 0); info_ptr->iccp_proflen = proflen; info_ptr->iccp_name = new_iccp_name; info_ptr->iccp_profile = new_iccp_profile; info_ptr->free_me |= PNG_FREE_ICCP; info_ptr->valid |= PNG_INFO_iCCP; }
void PNGAPI png_read_png(png_structp png_ptr, png_infop info_ptr, int transforms, voidp params) { int row; if (png_ptr == NULL) return; /* png_read_info() gives us all of the information from the * PNG file before the first IDAT (image data chunk). */ png_read_info(png_ptr, info_ptr); if (info_ptr->height > PNG_UINT_32_MAX/png_sizeof(png_bytep)) png_error(png_ptr, "Image is too high to process with png_read_png()"); /* -------------- image transformations start here ------------------- */ #ifdef PNG_READ_16_TO_8_SUPPORTED /* Tell libpng to strip 16 bit/color files down to 8 bits per color. */ if (transforms & PNG_TRANSFORM_STRIP_16) png_set_strip_16(png_ptr); #endif #ifdef PNG_READ_STRIP_ALPHA_SUPPORTED /* Strip alpha bytes from the input data without combining with * the background (not recommended). */ if (transforms & PNG_TRANSFORM_STRIP_ALPHA) png_set_strip_alpha(png_ptr); #endif #if defined(PNG_READ_PACK_SUPPORTED) && !defined(PNG_READ_EXPAND_SUPPORTED) /* Extract multiple pixels with bit depths of 1, 2, or 4 from a single * byte into separate bytes (useful for paletted and grayscale images). */ if (transforms & PNG_TRANSFORM_PACKING) png_set_packing(png_ptr); #endif #ifdef PNG_READ_PACKSWAP_SUPPORTED /* Change the order of packed pixels to least significant bit first * (not useful if you are using png_set_packing). */ if (transforms & PNG_TRANSFORM_PACKSWAP) png_set_packswap(png_ptr); #endif #ifdef PNG_READ_EXPAND_SUPPORTED /* Expand paletted colors into true RGB triplets * Expand grayscale images to full 8 bits from 1, 2, or 4 bits/pixel * Expand paletted or RGB images with transparency to full alpha * channels so the data will be available as RGBA quartets. */ if (transforms & PNG_TRANSFORM_EXPAND) if ((png_ptr->bit_depth < 8) || (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) || (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))) png_set_expand(png_ptr); #endif /* We don't handle background color or gamma transformation or quantizing. */ #ifdef PNG_READ_INVERT_SUPPORTED /* Invert monochrome files to have 0 as white and 1 as black */ if (transforms & PNG_TRANSFORM_INVERT_MONO) png_set_invert_mono(png_ptr); #endif #ifdef PNG_READ_SHIFT_SUPPORTED /* If you want to shift the pixel values from the range [0,255] or * [0,65535] to the original [0,7] or [0,31], or whatever range the * colors were originally in: */ if ((transforms & PNG_TRANSFORM_SHIFT) && png_get_valid(png_ptr, info_ptr, PNG_INFO_sBIT)) { png_color_8p sig_bit; png_get_sBIT(png_ptr, info_ptr, &sig_bit); png_set_shift(png_ptr, sig_bit); } #endif #ifdef PNG_READ_BGR_SUPPORTED /* Flip the RGB pixels to BGR (or RGBA to BGRA) */ if (transforms & PNG_TRANSFORM_BGR) png_set_bgr(png_ptr); #endif #ifdef PNG_READ_SWAP_ALPHA_SUPPORTED /* Swap the RGBA or GA data to ARGB or AG (or BGRA to ABGR) */ if (transforms & PNG_TRANSFORM_SWAP_ALPHA) png_set_swap_alpha(png_ptr); #endif #ifdef PNG_READ_SWAP_SUPPORTED /* Swap bytes of 16 bit files to least significant byte first */ if (transforms & PNG_TRANSFORM_SWAP_ENDIAN) png_set_swap(png_ptr); #endif /* Added at libpng-1.2.41 */ #ifdef PNG_READ_INVERT_ALPHA_SUPPORTED /* Invert the alpha channel from opacity to transparency */ if (transforms & PNG_TRANSFORM_INVERT_ALPHA) png_set_invert_alpha(png_ptr); #endif /* Added at libpng-1.2.41 */ #ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED /* Expand grayscale image to RGB */ if (transforms & PNG_TRANSFORM_GRAY_TO_RGB) png_set_gray_to_rgb(png_ptr); #endif /* We don't handle adding filler bytes */ /* Optional call to gamma correct and add the background to the palette * and update info structure. REQUIRED if you are expecting libpng to * update the palette for you (i.e., you selected such a transform above). */ png_read_update_info(png_ptr, info_ptr); /* -------------- image transformations end here ------------------- */ png_free_data(png_ptr, info_ptr, PNG_FREE_ROWS, 0); if (info_ptr->row_pointers == NULL) { png_uint_32 iptr; info_ptr->row_pointers = (png_bytepp)png_malloc(png_ptr, info_ptr->height * png_sizeof(png_bytep)); for (iptr=0; iptr<info_ptr->height; iptr++) info_ptr->row_pointers[iptr] = NULL; info_ptr->free_me |= PNG_FREE_ROWS; for (row = 0; row < (int)info_ptr->height; row++) info_ptr->row_pointers[row] = (png_bytep)png_malloc(png_ptr, png_get_rowbytes(png_ptr, info_ptr)); } png_read_image(png_ptr, info_ptr->row_pointers); info_ptr->valid |= PNG_INFO_IDAT; /* Read rest of file, and get additional chunks in info_ptr - REQUIRED */ png_read_end(png_ptr, info_ptr); transforms = transforms; /* Quiet compiler warnings */ params = params; }
void Functions::FileImage::SaveToPNG(std::string filename, RGBA* imageRGBA, unsigned short width, unsigned short height){ FILE *fp = NULL; png_structp png_ptr = NULL; png_infop info_ptr = NULL; png_bytep row = NULL; // Open file for writing (binary mode) fp = fopen(filename.c_str(), "wb"); if (fp == NULL) { LOGSYSTEM->Error("Could not open file for writing"); return; } // Initialize write structure png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (png_ptr == NULL) { LOGSYSTEM->Error("Could not allocate write struct"); fclose(fp); return; } // Initialize info structure info_ptr = png_create_info_struct(png_ptr); if (info_ptr == NULL) { LOGSYSTEM->Error("Could not allocate info struct"); fclose(fp); png_destroy_write_struct(&png_ptr, (png_infopp)NULL); return; } // Setup Exception handling if (setjmp(png_jmpbuf(png_ptr))) { LOGSYSTEM->Error("Error during png creation"); fclose(fp); png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1); png_destroy_write_struct(&png_ptr, (png_infopp)NULL); return; } 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_write_info(png_ptr, info_ptr); // Allocate memory for one row (3 bytes per pixel - RGB) row = (png_bytep) malloc(4 * width * sizeof(png_byte)); // Write image data int x, y; for (y=0 ; y<height ; y++) { for (x=0 ; x<width ; x++) { row[x*4] = imageRGBA[y*width + x].R; row[x*4+1] = imageRGBA[y*width + x].G; row[x*4+2] = imageRGBA[y*width + x].B; row[x*4+3] = imageRGBA[y*width + x].A; } png_write_row(png_ptr, row); } // End write png_write_end(png_ptr, NULL); fclose(fp); png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1); png_destroy_write_struct(&png_ptr, (png_infopp)NULL); free(row); }
/* * Reduce the image type to a lower bit depth and color type, * by removing redundant bits. * Possible reductions: 16bpp to 8bpp; RGB to gray; strip alpha. * The parameter reductions indicates the intended reductions. * The function returns the successful reductions. * All reductions are performed in a single step. */ static png_uint_32 opng_reduce_bits(png_structp png_ptr, png_infop info_ptr, png_uint_32 reductions) { png_bytep src_ptr, dest_ptr; png_uint_32 width, height; int interlace_type, compression_type, filter_type, src_bit_depth, dest_bit_depth, src_color_type; /* See which reductions may be performed. */ reductions = opng_analyze_bits(png_ptr, info_ptr, reductions); if (reductions == OPNG_REDUCE_NONE) return OPNG_REDUCE_NONE; /* exit early */ png_get_IHDR(png_ptr, info_ptr, &width, &height, &src_bit_depth, &src_color_type, &interlace_type, &compression_type, &filter_type); if (!height || !width){ return OPNG_REDUCE_NONE; } /* Compute the new image parameters bit_depth, color_type, etc. */ OPNG_ASSERT(src_bit_depth >= 8); if (reductions & OPNG_REDUCE_16_TO_8) { OPNG_ASSERT(src_bit_depth == 16); dest_bit_depth = 8; } else dest_bit_depth = src_bit_depth; int src_byte_depth = src_bit_depth / 8; int dest_byte_depth = dest_bit_depth / 8; int dest_color_type = src_color_type; if (reductions & OPNG_REDUCE_RGB_TO_GRAY) { OPNG_ASSERT(src_color_type & PNG_COLOR_MASK_COLOR); dest_color_type &= ~PNG_COLOR_MASK_COLOR; } if (reductions & OPNG_REDUCE_STRIP_ALPHA) { OPNG_ASSERT(src_color_type & PNG_COLOR_MASK_ALPHA); dest_color_type &= ~PNG_COLOR_MASK_ALPHA; } int src_channels = png_get_channels(png_ptr, info_ptr); int dest_channels = ((dest_color_type & PNG_COLOR_MASK_COLOR) ? 3 : 1) + ((dest_color_type & PNG_COLOR_MASK_ALPHA) ? 1 : 0); int src_sample_size = src_channels * src_byte_depth; int dest_sample_size = dest_channels * dest_byte_depth; /* Pre-compute the intra-sample translation table. */ int k; int tran_tbl[8]; for (k = 0; k < 4 * dest_byte_depth; ++k) tran_tbl[k] = k * src_bit_depth / dest_bit_depth; /* If rgb --> gray, shift the alpha component two positions to the left. */ if ((reductions & OPNG_REDUCE_RGB_TO_GRAY) && (dest_color_type & PNG_COLOR_MASK_ALPHA)) { tran_tbl[dest_byte_depth] = tran_tbl[3 * dest_byte_depth]; if (dest_byte_depth == 2) tran_tbl[dest_byte_depth + 1] = tran_tbl[3 * dest_byte_depth + 1]; } /* Translate the samples to the new image type. */ OPNG_ASSERT(src_sample_size > dest_sample_size); png_bytepp row_ptr = png_get_rows(png_ptr, info_ptr); for (png_uint_32 i = 0; i < height; ++i, ++row_ptr) { src_ptr = dest_ptr = *row_ptr; for (png_uint_32 j = 0; j < width; ++j) { for (k = 0; k < dest_sample_size; ++k) dest_ptr[k] = src_ptr[tran_tbl[k]]; src_ptr += src_sample_size; dest_ptr += dest_sample_size; } } png_color_16p trans_color; /* Update the ancillary information. */ if (png_get_tRNS(png_ptr, info_ptr, 0, 0, &trans_color)) { if (reductions & OPNG_REDUCE_16_TO_8) { if (trans_color->red % 257 == 0 && trans_color->green % 257 == 0 && trans_color->blue % 257 == 0 && trans_color->gray % 257 == 0) { trans_color->red &= 255; trans_color->green &= 255; trans_color->blue &= 255; trans_color->gray &= 255; } else { /* 16-bit tRNS in 8-bit samples: all pixels are 100% opaque. */ png_free_data(png_ptr, info_ptr, PNG_FREE_TRNS, -1); png_set_invalid(png_ptr, info_ptr, PNG_INFO_tRNS); } } if (reductions & OPNG_REDUCE_RGB_TO_GRAY) { if (trans_color->red == trans_color->green || trans_color->red == trans_color->blue) trans_color->gray = trans_color->red; else { /* Non-gray tRNS in grayscale image: all pixels are 100% opaque. */ png_free_data(png_ptr, info_ptr, PNG_FREE_TRNS, -1); png_set_invalid(png_ptr, info_ptr, PNG_INFO_tRNS); } } } #ifdef PNG_bKGD_SUPPORTED png_color_16p background; if (png_get_bKGD(png_ptr, info_ptr, &background)) { if (reductions & OPNG_REDUCE_16_TO_8) { background->red &= 255; background->green &= 255; background->blue &= 255; background->gray &= 255; } if (reductions & OPNG_REDUCE_RGB_TO_GRAY) background->gray = background->red; } #endif #ifdef PNG_sBIT_SUPPORTED png_color_8p sig_bits; if (png_get_sBIT(png_ptr, info_ptr, &sig_bits)) { if (reductions & OPNG_REDUCE_16_TO_8) { if (sig_bits->red > 8) sig_bits->red = 8; if (sig_bits->green > 8) sig_bits->green = 8; if (sig_bits->blue > 8) sig_bits->blue = 8; if (sig_bits->gray > 8) sig_bits->gray = 8; if (sig_bits->alpha > 8) sig_bits->alpha = 8; } if (reductions & OPNG_REDUCE_RGB_TO_GRAY) { png_byte max_sig_bits = sig_bits->red; if (max_sig_bits < sig_bits->green) max_sig_bits = sig_bits->green; if (max_sig_bits < sig_bits->blue) max_sig_bits = sig_bits->blue; sig_bits->gray = max_sig_bits; } } #endif /* Update the image information. */ png_set_IHDR(png_ptr, info_ptr, width, height, dest_bit_depth, dest_color_type, interlace_type, compression_type, filter_type); return reductions; }
int IMG_SavePNG_RW (SDL_Surface *face, SDL_RWops *src) { png_structp png_ptr = 0; png_infop info_ptr = NULL; //png_colorp palette = 0; png_bytep *row_pointers = NULL; int i; int colortype; int result = -1; png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, png_user_error, png_user_warn); if (png_ptr == NULL) { IMG_SetError ("Couldn't allocate memory for PNG file"); return -1; } /* Allocate/initialize the image information data. REQUIRED */ info_ptr = png_create_info_struct(png_ptr); if (info_ptr == NULL) { IMG_SetError("Couldn't create image information for PNG file"); goto done; } /* Set error handling. */ if (setjmp(png_jmpbuf(png_ptr))) { /* If we get here, we had a problem reading the file */ IMG_SetError("Error writing the PNG file"); goto done; } png_set_write_fn (png_ptr, src, png_write_data, png_io_flush); /* 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 */ colortype = png_colortype_from_surface (face); png_set_IHDR (png_ptr, info_ptr, face->w, face->h, 8, colortype, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); /* Write the file header information. REQUIRED */ png_write_info (png_ptr, info_ptr); /* pack pixels into bytes */ png_set_packing (png_ptr); /* Create the array of pointers to image data */ row_pointers = (png_bytep*) malloc (sizeof(png_bytep)*face->h); if (row_pointers == NULL) { IMG_SetError("Couldn't allocate PNG row pointers"); goto done; } for (i = 0; i < face->h; i++) row_pointers[i] = (png_bytep)(Uint8 *)face->pixels + i*face->pitch; /* write out the entire image data in one call */ png_write_image (png_ptr, row_pointers); png_write_end(png_ptr, info_ptr); result = 0; /* success! */ done: if (row_pointers != NULL) free (row_pointers); if (info_ptr != NULL) png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1); png_destroy_write_struct (&png_ptr, (png_infopp)NULL); return result; }
void PNGAPI png_set_tRNS(png_structrp png_ptr, png_inforp info_ptr, png_const_bytep trans_alpha, int num_trans, png_const_color_16p trans_color) { png_debug1(1, "in %s storage function", "tRNS"); if (png_ptr == NULL || info_ptr == NULL) return; if (trans_alpha != NULL) { /* It may not actually be necessary to set png_ptr->trans_alpha here; * we do it for backward compatibility with the way the png_handle_tRNS * function used to do the allocation. * * 1.6.0: The above statement is incorrect; png_handle_tRNS effectively * relies on png_set_tRNS storing the information in png_struct * (otherwise it won't be there for the code in pngrtran.c). */ png_free_data(png_ptr, info_ptr, PNG_FREE_TRNS, 0); /* Changed from num_trans to PNG_MAX_PALETTE_LENGTH in version 1.2.1 */ png_ptr->trans_alpha = info_ptr->trans_alpha = png_voidcast(png_bytep, png_malloc(png_ptr, PNG_MAX_PALETTE_LENGTH)); if (num_trans > 0 && num_trans <= PNG_MAX_PALETTE_LENGTH) memcpy(info_ptr->trans_alpha, trans_alpha, (png_size_t)num_trans); } if (trans_color != NULL) { #ifdef PNG_WARNINGS_SUPPORTED if (info_ptr->bit_depth < 16) { int sample_max = (1 << info_ptr->bit_depth) - 1; if ((info_ptr->color_type == PNG_COLOR_TYPE_GRAY && trans_color->gray > sample_max) || (info_ptr->color_type == PNG_COLOR_TYPE_RGB && (trans_color->red > sample_max || trans_color->green > sample_max || trans_color->blue > sample_max))) png_warning(png_ptr, "tRNS chunk has out-of-range samples for bit_depth"); } #endif info_ptr->trans_color = *trans_color; if (num_trans == 0) num_trans = 1; } info_ptr->num_trans = (png_uint_16)num_trans; if (num_trans != 0) { info_ptr->valid |= PNG_INFO_tRNS; info_ptr->free_me |= PNG_FREE_TRNS; } }
int gr_save_screenshot(const char *dest) { uint32_t y, stride_bytes; int res = -1; GGLContext *gl = NULL; GGLSurface surface; uint8_t * volatile img_data = NULL; uint8_t *ptr; FILE *fp = NULL; png_structp png_ptr = NULL; png_infop info_ptr = NULL; fp = fopen(dest, "wb"); if(!fp) goto exit; img_data = malloc(vi.xres * vi.yres * 3); surface.version = sizeof(surface); surface.width = gr_mem_surface.width; surface.height = gr_mem_surface.height; surface.stride = gr_mem_surface.width; surface.data = img_data; surface.format = GGL_PIXEL_FORMAT_RGB_888; gglInit(&gl); gl->colorBuffer(gl, &surface); gl->activeTexture(gl, 0); gl->bindTexture(gl, &gr_mem_surface); gl->texEnvi(gl, GGL_TEXTURE_ENV, GGL_TEXTURE_ENV_MODE, GGL_REPLACE); gl->texGeni(gl, GGL_S, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE); gl->texGeni(gl, GGL_T, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE); gl->enable(gl, GGL_TEXTURE_2D); gl->texCoord2i(gl, 0, 0); gl->recti(gl, 0, 0, gr_mem_surface.width, gr_mem_surface.height); gglUninit(gl); gl = NULL; png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!png_ptr) goto exit; info_ptr = png_create_info_struct(png_ptr); if (info_ptr == NULL) goto exit; if (setjmp(png_jmpbuf(png_ptr))) goto exit; png_init_io(png_ptr, fp); png_set_IHDR(png_ptr, info_ptr, surface.width, surface.height, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); png_write_info(png_ptr, info_ptr); ptr = img_data; stride_bytes = surface.width*3; for(y = 0; y < surface.height; ++y) { png_write_row(png_ptr, ptr); ptr += stride_bytes; } png_write_end(png_ptr, NULL); res = 0; exit: if(info_ptr) png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1); if(png_ptr) png_destroy_write_struct(&png_ptr, (png_infopp)NULL); if(gl) gglUninit(gl); if(img_data) free(img_data); if(fp) fclose(fp); return res; }
/* * Reduce the palette. (Only the fast method is implemented.) * The parameter reductions indicates the intended reductions. * The function returns the successful reductions. */ static png_uint_32 opng_reduce_palette(png_structp png_ptr, png_infop info_ptr, png_uint_32 reductions) { png_colorp palette; png_bytep trans_alpha; png_uint_32 width, height; int bit_depth, color_type, interlace_type, compression_type, filter_type; int num_palette, num_trans; int last_color_index, last_trans_index; png_byte crt_trans_value, last_trans_value; png_byte is_used[256]; png_color_16 gray_trans; png_uint_32 result = OPNG_REDUCE_NONE; png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, &compression_type, &filter_type); if (!height || !width){ return OPNG_REDUCE_NONE; } png_bytepp row_ptr = png_get_rows(png_ptr, info_ptr); if (!png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette)) { palette = 0; num_palette = 0; } if (!png_get_tRNS(png_ptr, info_ptr, &trans_alpha, &num_trans, 0)) { trans_alpha = 0; num_trans = 0; } else OPNG_ASSERT(trans_alpha && num_trans > 0); opng_analyze_sample_usage(png_ptr, info_ptr, is_used); /* Palette-to-gray does not work (yet) if the bit depth is below 8. */ int is_gray = (reductions & OPNG_REDUCE_PALETTE_TO_GRAY) && (bit_depth == 8); last_color_index = last_trans_index = -1; int k; for (k= 0; k < 256; ++k) { if (!is_used[k]) continue; last_color_index = k; if (k < num_trans && trans_alpha[k] < 255) last_trans_index = k; if (is_gray) if (palette[k].red != palette[k].green || palette[k].red != palette[k].blue) is_gray = 0; } OPNG_ASSERT(last_color_index >= 0); OPNG_ASSERT(last_color_index >= last_trans_index); /* Check the integrity of PLTE and tRNS. */ if (last_color_index >= num_palette) { png_warning(png_ptr, "Too few colors in PLTE"); /* Fix the palette by adding blank entries at the end. */ opng_realloc_PLTE(png_ptr, info_ptr, last_color_index + 1); png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette); OPNG_ASSERT(num_palette == last_color_index + 1); result |= OPNG_REDUCE_REPAIR; } if (num_trans > num_palette) { png_warning(png_ptr, "Too many alpha values in tRNS"); /* Transparency will be fixed further below. */ result |= OPNG_REDUCE_REPAIR; } /* Check if tRNS can be reduced to grayscale. */ if (is_gray && last_trans_index >= 0) { gray_trans.gray = palette[last_trans_index].red; last_trans_value = trans_alpha[last_trans_index]; for (k = 0; k <= last_color_index; ++k) { if (!is_used[k]) continue; if (k <= last_trans_index) { crt_trans_value = trans_alpha[k]; /* Cannot reduce if different colors have transparency. */ if (crt_trans_value < 255 && palette[k].red != gray_trans.gray) { is_gray = 0; break; } } else crt_trans_value = 255; /* Cannot reduce if same color has multiple transparency levels. */ if (palette[k].red == gray_trans.gray && crt_trans_value != last_trans_value) { is_gray = 0; break; } } } /* Remove tRNS if it is entirely sterile. */ if (num_trans > 0 && last_trans_index < 0) { num_trans = 0; png_free_data(png_ptr, info_ptr, PNG_FREE_TRNS, -1); png_set_invalid(png_ptr, info_ptr, PNG_INFO_tRNS); result |= OPNG_REDUCE_PALETTE_FAST; } if (reductions & OPNG_REDUCE_PALETTE_FAST) { if (num_palette != last_color_index + 1) { /* Reduce PLTE. */ /* hIST is reduced automatically. */ opng_realloc_PLTE(png_ptr, info_ptr, last_color_index + 1); png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette); OPNG_ASSERT(num_palette == last_color_index + 1); result |= OPNG_REDUCE_PALETTE_FAST; } if (num_trans > 0 && num_trans != last_trans_index + 1) { /* Reduce tRNS. */ opng_realloc_tRNS(png_ptr, info_ptr, last_trans_index + 1); png_get_tRNS(png_ptr, info_ptr, &trans_alpha, &num_trans, 0); OPNG_ASSERT(num_trans == last_trans_index + 1); result |= OPNG_REDUCE_PALETTE_FAST; } } if (reductions & OPNG_REDUCE_8_TO_4_2_1) { result |= opng_reduce_palette_bits(png_ptr, info_ptr, reductions); /* Refresh the image information. */ bit_depth = png_get_bit_depth(png_ptr, info_ptr); } if ((bit_depth < 8) || !is_gray) return result; /* Reduce palette --> grayscale. */ for (png_uint_32 i = 0; i < height; ++i) { for (png_uint_32 j = 0; j < width; ++j) row_ptr[i][j] = palette[row_ptr[i][j]].red; } /* Update the ancillary information. */ if (num_trans > 0) png_set_tRNS(png_ptr, info_ptr, 0, 0, &gray_trans); #ifdef PNG_bKGD_SUPPORTED png_color_16p background; if (png_get_bKGD(png_ptr, info_ptr, &background)) background->gray = palette[background->index].red; #endif #ifdef PNG_hIST_SUPPORTED png_uint_16p hist; if (png_get_hIST(png_ptr, info_ptr, &hist)) { png_free_data(png_ptr, info_ptr, PNG_FREE_HIST, -1); png_set_invalid(png_ptr, info_ptr, PNG_INFO_hIST); } #endif #ifdef PNG_sBIT_SUPPORTED png_color_8p sig_bits; if (png_get_sBIT(png_ptr, info_ptr, &sig_bits)) { png_byte max_sig_bits = sig_bits->red; if (max_sig_bits < sig_bits->green) max_sig_bits = sig_bits->green; if (max_sig_bits < sig_bits->blue) max_sig_bits = sig_bits->blue; sig_bits->gray = max_sig_bits; } #endif /* Update the image information. */ png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, PNG_COLOR_TYPE_GRAY, interlace_type, compression_type, filter_type); png_free_data(png_ptr, info_ptr, PNG_FREE_PLTE, -1); png_set_invalid(png_ptr, info_ptr, PNG_INFO_PLTE); return OPNG_REDUCE_PALETTE_TO_GRAY; /* ignore the former result */ }
/* Free all memory used by the read */ void PNGAPI png_destroy_read_struct(png_structpp png_ptr_ptr, png_infopp info_ptr_ptr, png_infopp end_info_ptr_ptr) { png_structp png_ptr = NULL; png_infop info_ptr = NULL, end_info_ptr = NULL; #ifdef PNG_USER_MEM_SUPPORTED png_free_ptr free_fn = NULL; png_voidp mem_ptr = NULL; #endif png_debug(1, "in png_destroy_read_struct"); if (png_ptr_ptr != NULL) png_ptr = *png_ptr_ptr; if (png_ptr == NULL) return; #ifdef PNG_USER_MEM_SUPPORTED free_fn = png_ptr->free_fn; mem_ptr = png_ptr->mem_ptr; #endif if (info_ptr_ptr != NULL) info_ptr = *info_ptr_ptr; if (end_info_ptr_ptr != NULL) end_info_ptr = *end_info_ptr_ptr; png_read_destroy(png_ptr, info_ptr, end_info_ptr); if (info_ptr != NULL) { #ifdef PNG_TEXT_SUPPORTED png_free_data(png_ptr, info_ptr, PNG_FREE_TEXT, -1); #endif #ifdef PNG_USER_MEM_SUPPORTED png_destroy_struct_2((png_voidp)info_ptr, (png_free_ptr)free_fn, (png_voidp)mem_ptr); #else png_destroy_struct((png_voidp)info_ptr); #endif *info_ptr_ptr = NULL; } if (end_info_ptr != NULL) { #ifdef PNG_READ_TEXT_SUPPORTED png_free_data(png_ptr, end_info_ptr, PNG_FREE_TEXT, -1); #endif #ifdef PNG_USER_MEM_SUPPORTED png_destroy_struct_2((png_voidp)end_info_ptr, (png_free_ptr)free_fn, (png_voidp)mem_ptr); #else png_destroy_struct((png_voidp)end_info_ptr); #endif *end_info_ptr_ptr = NULL; } if (png_ptr != NULL) { #ifdef PNG_USER_MEM_SUPPORTED png_destroy_struct_2((png_voidp)png_ptr, (png_free_ptr)free_fn, (png_voidp)mem_ptr); #else png_destroy_struct((png_voidp)png_ptr); #endif *png_ptr_ptr = NULL; } }
void PNGAPI png_set_PLTE(png_structrp png_ptr, png_inforp info_ptr, png_const_colorp palette, int num_palette) { png_uint_32 max_palette_length; png_debug1(1, "in %s storage function", "PLTE"); if (png_ptr == NULL || info_ptr == NULL) return; max_palette_length = (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) ? (1 << info_ptr->bit_depth) : PNG_MAX_PALETTE_LENGTH; if (num_palette < 0 || num_palette > (int) max_palette_length) { if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) png_error(png_ptr, "Invalid palette length"); else { png_warning(png_ptr, "Invalid palette length"); return; } } if ((num_palette > 0 && palette == NULL) || (num_palette == 0 # ifdef PNG_MNG_FEATURES_SUPPORTED && (png_ptr->mng_features_permitted & PNG_FLAG_MNG_EMPTY_PLTE) == 0 # endif )) { png_error(png_ptr, "Invalid palette"); } /* It may not actually be necessary to set png_ptr->palette here; * we do it for backward compatibility with the way the png_handle_tRNS * function used to do the allocation. * * 1.6.0: the above statement appears to be incorrect; something has to set * the palette inside png_struct on read. */ png_free_data(png_ptr, info_ptr, PNG_FREE_PLTE, 0); /* Changed in libpng-1.2.1 to allocate PNG_MAX_PALETTE_LENGTH instead * of num_palette entries, in case of an invalid PNG file or incorrect * call to png_set_PLTE() with too-large sample values. */ png_ptr->palette = png_voidcast(png_colorp, png_calloc(png_ptr, PNG_MAX_PALETTE_LENGTH * (sizeof (png_color)))); if (num_palette > 0) memcpy(png_ptr->palette, palette, num_palette * (sizeof (png_color))); info_ptr->palette = png_ptr->palette; info_ptr->num_palette = png_ptr->num_palette = (png_uint_16)num_palette; info_ptr->free_me |= PNG_FREE_PLTE; info_ptr->valid |= PNG_INFO_PLTE; }