pngquant_error rwpng_write_image8(FILE *outfile, png8_image *mainprog_ptr) { png_structp png_ptr; png_infop info_ptr; pngquant_error retval = rwpng_write_image_init((png_image*)mainprog_ptr, &png_ptr, &info_ptr, outfile); if (retval) return retval; /* set the image parameters appropriately */ int sample_depth; if (mainprog_ptr->num_palette <= 2) sample_depth = 1; else if (mainprog_ptr->num_palette <= 4) sample_depth = 2; else if (mainprog_ptr->num_palette <= 16) sample_depth = 4; else sample_depth = 8; png_set_IHDR(png_ptr, info_ptr, mainprog_ptr->width, mainprog_ptr->height, sample_depth, PNG_COLOR_TYPE_PALETTE, 0, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_BASE); png_set_PLTE(png_ptr, info_ptr, &mainprog_ptr->palette[0], mainprog_ptr->num_palette); if (mainprog_ptr->num_trans > 0) png_set_tRNS(png_ptr, info_ptr, mainprog_ptr->trans, mainprog_ptr->num_trans, NULL); rwpng_write_end(&info_ptr, &png_ptr, (png_image*)mainprog_ptr); return SUCCESS; }
static int save_png_sub(RefImage *image, Value w) { 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_bytepp rows = malloc(sizeof(png_bytep) * image->height); png_bytep data = (png_bytep)image->data; int color_type = PNG_COLOR_TYPE_RGB; int i; png_set_write_fn(png_ptr, &w, png_write_callback, png_flush_callback); switch (image->bands) { case BAND_L: color_type = PNG_COLOR_TYPE_GRAY; break; case BAND_LA: color_type = PNG_COLOR_TYPE_GRAY_ALPHA; break; case BAND_P: color_type = PNG_COLOR_TYPE_PALETTE; break; case BAND_RGB: color_type = PNG_COLOR_TYPE_RGB; break; case BAND_RGBA: color_type = PNG_COLOR_TYPE_RGB_ALPHA; break; } if (setjmp(png_jmpbuf(png_ptr))) { png_destroy_write_struct(&png_ptr, &info_ptr); free(rows); return FALSE; } png_set_IHDR(png_ptr, info_ptr, image->width, image->height, 8, color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); for (i = 0; i < image->height; i++) { rows[i] = data + image->pitch * i; } if (image->bands == BAND_P) { uint32_t *pal = image->palette; uint8_t trans[PALETTE_NUM]; png_color pcolor[PALETTE_NUM]; if (palette_has_alpha(pal)) { for (i = 0; i < PALETTE_NUM; i++) { trans[i] = (pal[i] & COLOR_A_MASK) >> COLOR_A_SHIFT; } png_set_tRNS(png_ptr, info_ptr, trans, PALETTE_NUM, NULL); } for (i = 0; i < PALETTE_NUM; i++) { pcolor[i].red = (pal[i] & COLOR_R_MASK) >> COLOR_R_SHIFT; pcolor[i].green = (pal[i] & COLOR_G_MASK) >> COLOR_G_SHIFT; pcolor[i].blue = (pal[i] & COLOR_B_MASK) >> COLOR_B_SHIFT; } png_set_PLTE(png_ptr, info_ptr, pcolor, PALETTE_NUM); }
void writeSetup2(png_structp png_ptr_write, png_infop info_ptr_write, png_structp png_ptr_read, png_infop info_ptr_read) { /* IHDR */ png_uint_32 width; png_uint_32 height; int bit_depth; int colour_type; int interlace_method; int compression_method; int filter_method; /* PLTE */ png_colorp palette = NULL; int palette_size = 0; /* gAMA */ double gamma; /* tRNS */ png_bytep trans; int num_trans; png_color_16p trans_values; /* bKGD */ png_color_16p background; png_get_IHDR(png_ptr_read, info_ptr_read, &width, &height, &bit_depth, &colour_type, &interlace_method, &compression_method, &filter_method); png_set_IHDR(png_ptr_write, info_ptr_write, width, height, bit_depth, colour_type, interlace_method, compression_method, filter_method); if(png_get_valid(png_ptr_read, info_ptr_read, PNG_INFO_PLTE)) { png_get_PLTE(png_ptr_read, info_ptr_read, &palette, &palette_size); png_set_PLTE(png_ptr_write, info_ptr_write, palette, palette_size); } if(png_get_valid(png_ptr_read, info_ptr_read, PNG_INFO_gAMA)) { png_get_gAMA(png_ptr_read, info_ptr_read, &gamma); png_set_gAMA(png_ptr_write, info_ptr_write, gamma); } if(png_get_valid(png_ptr_read, info_ptr_read, PNG_INFO_tRNS)) { png_get_tRNS(png_ptr_read, info_ptr_read, &trans, &num_trans, &trans_values); png_set_tRNS(png_ptr_write, info_ptr_write, trans, num_trans, trans_values); } if(png_get_valid(png_ptr_read, info_ptr_read, PNG_INFO_bKGD)) { png_get_bKGD(png_ptr_read, info_ptr_read, &background); png_set_bKGD(png_ptr_write, info_ptr_write, background); } }
/** Write out the PLTE (and possibly tRNS) chunk to the png. This will create the * palette data to set in the PLTE chunk and set it, and if the colorkey is set * for the surface an appropriate tRNS chunk is generated. * * \param png_ptr A pointer to the png write structure. * \param info_ptr A pointer to the png info structure. * \param surf The surface that is being written to the png. * \return -1 on error, 0 if the palette chunk was written without problems. */ static int write_palette_chunk(png_structp png_ptr, png_infop info_ptr, SDL_Surface *surf) { png_colorp palette; Uint8 *alphas; int slot; SDL_PixelFormat *fmt = surf -> format; SDL_Color *sourcepal = fmt -> palette -> colors; // Write the image header first... png_set_IHDR(png_ptr, info_ptr, surf -> w, surf -> h, 8, PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); // now sort out the palette if(!(palette = (png_colorp)malloc(fmt -> palette -> ncolors * sizeof(png_color)))) { SDL_SetError("Unable to create memory for palette storage"); return -1; } // Copy the palette over. Can't just use a straight // memcpy as sdl palettes have pad bytes. for(slot = 0; slot < fmt -> palette -> ncolors; ++slot) { memcpy(&palette[slot], &sourcepal[slot], 3); } // Set it... png_set_PLTE(png_ptr, info_ptr, palette, fmt -> palette -> ncolors); // Done with the palette now free(palette); Uint32 colorkey; int ck = SDL_GetColorKey(surf, &colorkey); // If we have a colour key, we need to set up the alphas for each palette colour if(ck == 0) { // According the the PNG spec (section 4.2.1.1) we only need enough entries // to store transparencies up to the transparent pixel. if(!(alphas = (Uint8 *)malloc((colorkey + 1) * sizeof(Uint8)))) { SDL_SetError("Unable to create memory for transparency storage"); return -1; } // Set all of the alpha values to full memset(alphas, 255, (colorkey + 1) * sizeof(Uint8)); // And handle the transparent pixel alphas[colorkey] = 0; // Write the chunk, and then we're done with the transparencies png_set_tRNS(png_ptr, info_ptr, alphas, colorkey + 1, NULL); free(alphas); } return 0; }
static void pngx_set_GIF_transparent(png_structp png_ptr, png_infop info_ptr, unsigned int transparent) { png_byte trans[256]; unsigned int i; PNGX_ASSERT(transparent < 256); for (i = 0; i < transparent; ++i) trans[i] = 255; trans[transparent] = 0; png_set_tRNS(png_ptr, info_ptr, trans, (int)transparent + 1, NULL); }
/* * Change the size of the transparency buffer. * Changing info_ptr->num_trans directly, avoiding reallocation, should * have been sufficient, but can't be done using the current libpng API. */ static void opng_realloc_tRNS(png_structp png_ptr, png_infop info_ptr, int num_trans) { png_byte buffer[PNG_MAX_PALETTE_LENGTH]; png_bytep trans_alpha; int src_num_trans = 0; png_get_tRNS(png_ptr, info_ptr, &trans_alpha, &src_num_trans, 0); if (num_trans == src_num_trans) return; memcpy(buffer, trans_alpha, (size_t)num_trans); if (num_trans > src_num_trans) memset(buffer + src_num_trans, 0, num_trans - src_num_trans); png_set_tRNS(png_ptr, info_ptr, buffer, num_trans, 0); }
/* * bta_transparent_toPNG */ void bta_transparent_toPNG(bta_cell_t *in, btstring_t *fname) { FILE *outfp; png_structp png_ptr; png_infop info_ptr; uint32_t i; int trans = 0; png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!png_ptr) { printf("Failed allocating png_ptr\n"); return; } info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { printf("Failed allocating info_ptr\n"); return; } outfp = fopen(fname->buf, "wb"); if (outfp == NULL) { printf("Error opening %s\n", fname->buf); return; } png_init_io(png_ptr, outfp); png_set_IHDR(png_ptr, info_ptr, in->width, in->height, 8, \ PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE, \ PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); png_set_PLTE(png_ptr, info_ptr, egapalette, 16); png_set_tRNS(png_ptr, info_ptr, (png_bytep)&trans, 1, NULL); png_write_info(png_ptr, info_ptr); for (i = 0; i < in->height; i++) png_write_row(png_ptr, &in->gfx->buf[i * in->width]); png_write_end(png_ptr, info_ptr); png_destroy_write_struct(&png_ptr, &info_ptr); fclose(outfp); bts_free(fname); }
pngquant_error rwpng_write_image8(FILE *outfile, png8_image *mainprog_ptr) { png_structp png_ptr; png_infop info_ptr; pngquant_error retval = rwpng_write_image_init((png_image*)mainprog_ptr, &png_ptr, &info_ptr, outfile); if (retval) return retval; // Palette images generally don't gain anything from filtering png_set_filter(png_ptr, PNG_FILTER_TYPE_BASE, PNG_FILTER_VALUE_NONE); rwpng_set_gamma(info_ptr, png_ptr, mainprog_ptr->gamma); /* set the image parameters appropriately */ int sample_depth; if (mainprog_ptr->num_palette <= 2) sample_depth = 1; else if (mainprog_ptr->num_palette <= 4) sample_depth = 2; else if (mainprog_ptr->num_palette <= 16) sample_depth = 4; else sample_depth = 8; png_set_IHDR(png_ptr, info_ptr, mainprog_ptr->width, mainprog_ptr->height, sample_depth, PNG_COLOR_TYPE_PALETTE, 0, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_BASE); png_set_PLTE(png_ptr, info_ptr, &mainprog_ptr->palette[0], mainprog_ptr->num_palette); if (mainprog_ptr->num_trans > 0) png_set_tRNS(png_ptr, info_ptr, mainprog_ptr->trans, mainprog_ptr->num_trans, NULL); png_bytepp row_pointers = rwpng_create_row_pointers(info_ptr, png_ptr, mainprog_ptr->indexed_data, mainprog_ptr->height, mainprog_ptr->width); rwpng_write_end(&info_ptr, &png_ptr, row_pointers); free(row_pointers); return SUCCESS; }
void write() const { assert(m_png); assert(m_info); sync_ihdr(); if (m_color_type == color_type_palette) { if (! m_palette.empty()) { png_set_PLTE(m_png, m_info, const_cast< color* >(& m_palette[0]), (int) m_palette.size()); } if (! m_tRNS.empty()) { #ifdef PNG_tRNS_SUPPORTED png_set_tRNS(m_png, m_info, const_cast< byte* >(& m_tRNS[0]), m_tRNS.size(), NULL); #else throw error("attempted to write tRNS chunk; recompile with PNG_tRNS_SUPPORTED"); #endif } } if (m_gamma > 0) { #ifdef PNG_gAMA_SUPPORTED #ifdef PNG_FLOATING_POINT_SUPPORTED png_set_gAMA(m_png, m_info, m_gamma); #else png_set_gAMA_fixed(m_png, m_info, (png_fixed_point)(m_gamma * 100000)); #endif #else throw error("attempted to write gAMA chunk; recompile with PNG_gAMA_SUPPORTED"); #endif } png_write_info(m_png, m_info); }
int spupng_write_png(struct spupng_t *sp, struct eia608_screen* data, png_structp png_ptr, png_infop info_ptr, png_bytep image, png_bytep* row_pointer, unsigned int ww, unsigned int wh) { unsigned int i; if (setjmp(png_jmpbuf(png_ptr))) return 0; png_init_io (png_ptr, sp->fppng); png_set_IHDR (png_ptr, info_ptr, ww, wh, /* bit_depth */ 8, PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); png_set_PLTE (png_ptr, info_ptr, palette, sizeof(palette) / sizeof(palette[0])); png_set_tRNS (png_ptr, info_ptr, alpha, sizeof(alpha) / sizeof(alpha[0]), NULL); png_set_gAMA (png_ptr, info_ptr, 1.0 / 2.2); png_write_info (png_ptr, info_ptr); for (i = 0; i < wh; i++) row_pointer[i] = image + i * ww; png_write_image (png_ptr, row_pointer); png_write_end (png_ptr, info_ptr); return 1; }
static void init_output(void) { int bit_depth ; int color_type ; int invert_mono = 0 ; png_colorp pngpalette = NULL ; png_bytep ptrans = NULL ; outfile = openout(flatspec.output_filename); libpng = png_create_write_struct(PNG_LIBPNG_VER_STRING, png_voidp_NULL, my_error_callback, png_error_ptr_NULL); if( !libpng ) FatalUnexpected(_("Couldn't initialize libpng library")); libpng2 = png_create_info_struct(libpng); if( !libpng2 ) FatalUnexpected("Couldn't create PNG info structure"); png_init_io(libpng,outfile); bit_depth = 8; switch( flatspec.out_color_mode ) { case COLOR_GRAY: if( flatspec.default_pixel == PERHAPS_ALPHA_CHANNEL || flatspec.default_pixel == FORCE_ALPHA_CHANNEL ) color_type = PNG_COLOR_TYPE_GRAY_ALPHA ; else color_type = PNG_COLOR_TYPE_GRAY ; break ; case COLOR_RGB: if( flatspec.default_pixel == PERHAPS_ALPHA_CHANNEL || flatspec.default_pixel == FORCE_ALPHA_CHANNEL ) color_type = PNG_COLOR_TYPE_RGB_ALPHA ; else color_type = PNG_COLOR_TYPE_RGB ; break ; case COLOR_INDEXED: if( paletteSize == 2 && palette[0] == NEWALPHA(0,255) && palette[1] == NEWALPHA(-1,255) ) { color_type = PNG_COLOR_TYPE_GRAY ; bit_depth = 1 ; } else if( paletteSize == 2 && palette[0] == NEWALPHA(-1,255) && palette[1] == NEWALPHA(0,255) ) { color_type = PNG_COLOR_TYPE_GRAY ; bit_depth = 1 ; invert_mono = 1 ; } else { unsigned i ; int need_trans = flatspec.default_pixel == FORCE_ALPHA_CHANNEL ; color_type = PNG_COLOR_TYPE_PALETTE ; pngpalette = xcfmalloc(paletteSize*sizeof(png_color)) ; ptrans = xcfmalloc(paletteSize); for(i = 0; i<paletteSize; i++ ) { pngpalette[i].red = 255 & (palette[i] >> RED_SHIFT); pngpalette[i].green = 255 & (palette[i] >> GREEN_SHIFT); pngpalette[i].blue = 255 & (palette[i] >> BLUE_SHIFT); if( (ptrans[i] = ALPHA(palette[i])) != 255 ) need_trans = 1 ; } if( !need_trans ) { xcffree(ptrans); ptrans = NULL ; } if( paletteSize <= 2 ) bit_depth = 1 ; else if( paletteSize <= 4 ) bit_depth = 2 ; else if( paletteSize <= 16 ) bit_depth = 4 ; else bit_depth = 8; } break ; default: FatalUnexpected("This can't happen (unknown out_color_mode)"); } if( verboseFlag ) { fprintf(stderr,"Writing PNG: %s%s%s%s, %d bits", color_type & PNG_COLOR_MASK_COLOR ? _("color") : _("grayscale"), color_type & PNG_COLOR_MASK_PALETTE ? _("+palette") : "", color_type & PNG_COLOR_MASK_ALPHA ? _("+alpha") : "", ptrans || NULLALPHA(flatspec.default_pixel) ? _("+transparency") : "", bit_depth); if( pngpalette ) fprintf(stderr,_(" (%d colors)"),paletteSize); fprintf(stderr,"\n"); } png_set_IHDR(libpng,libpng2,flatspec.dim.width,flatspec.dim.height, bit_depth, color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); if( invert_mono ) png_set_invert_mono(libpng); if( pngpalette ) png_set_PLTE(libpng,libpng2,pngpalette,paletteSize); if( ptrans ) png_set_tRNS(libpng,libpng2,ptrans,paletteSize,NULL); else if ( !pngpalette && NULLALPHA(flatspec.default_pixel) ) { static png_color_16 trans ; trans.gray = trans.red = 255 & (flatspec.default_pixel >> RED_SHIFT) ; trans.green = 255 & (flatspec.default_pixel >> GREEN_SHIFT) ; trans.blue = 255 & (flatspec.default_pixel >> BLUE_SHIFT) ; png_set_tRNS(libpng,libpng2,NULL,0,&trans); } /* png_set_text here */ png_write_info(libpng,libpng2); if( bit_depth < 8 ) png_set_packing(libpng); switch( color_type ) { case PNG_COLOR_TYPE_RGB: case PNG_COLOR_TYPE_RGBA: #if (BLUE_SHIFT < RED_SHIFT) == !defined(WORDS_BIGENDIAN) png_set_bgr(libpng); #endif if( color_type == PNG_COLOR_TYPE_RGB ) #if (ALPHA_SHIFT < RED_SHIFT) == !defined(WORDS_BIGENDIAN) png_set_filler(libpng,0,PNG_FILLER_BEFORE); else png_set_swap_alpha(libpng); #else png_set_filler(libpng,0,PNG_FILLER_AFTER); #endif break ; case PNG_COLOR_TYPE_GRAY: png_set_filler(libpng,0,PNG_FILLER_AFTER); break ; case PNG_COLOR_TYPE_GRAY_ALPHA: case PNG_COLOR_TYPE_PALETTE: break ; default: FatalUnexpected("This can't happen (unexpected png color_type)"); } }
bool wxPNGHandler::SaveFile( wxImage *image, wxOutputStream& stream, bool verbose ) { wxPNGInfoStruct wxinfo; wxinfo.verbose = verbose; wxinfo.stream.out = &stream; png_structp png_ptr = png_create_write_struct ( PNG_LIBPNG_VER_STRING, NULL, wx_PNG_error, wx_PNG_warning ); if (!png_ptr) { if (verbose) { wxLogError(_("Couldn't save PNG image.")); } return false; } png_infop info_ptr = png_create_info_struct(png_ptr); if (info_ptr == NULL) { png_destroy_write_struct( &png_ptr, (png_infopp)NULL ); if (verbose) { wxLogError(_("Couldn't save PNG image.")); } return false; } if (setjmp(wxinfo.jmpbuf)) { png_destroy_write_struct( &png_ptr, (png_infopp)NULL ); if (verbose) { wxLogError(_("Couldn't save PNG image.")); } return false; } // NB: please see the comment near wxPNGInfoStruct declaration for // explanation why this line is mandatory png_set_write_fn( png_ptr, &wxinfo, wx_PNG_stream_writer, NULL); const int iHeight = image->GetHeight(); const int iWidth = image->GetWidth(); const bool bHasPngFormatOption = image->HasOption(wxIMAGE_OPTION_PNG_FORMAT); int iColorType = bHasPngFormatOption ? image->GetOptionInt(wxIMAGE_OPTION_PNG_FORMAT) : wxPNG_TYPE_COLOUR; bool bHasAlpha = image->HasAlpha(); bool bHasMask = image->HasMask(); bool bUsePalette = iColorType == wxPNG_TYPE_PALETTE #if wxUSE_PALETTE || (!bHasPngFormatOption && image->HasPalette() ) #endif ; png_color_8 mask = { 0, 0, 0, 0, 0 }; if (bHasMask) { mask.red = image->GetMaskRed(); mask.green = image->GetMaskGreen(); mask.blue = image->GetMaskBlue(); } PaletteMap palette; if (bUsePalette) { png_color png_rgb [PNG_MAX_PALETTE_LENGTH]; png_byte png_trans[PNG_MAX_PALETTE_LENGTH]; const unsigned char *pColors = image->GetData(); const unsigned char* pAlpha = image->GetAlpha(); if (bHasMask && !pAlpha) { // Mask must be first PaletteAdd(&palette, mask); } for (int y = 0; y < iHeight; y++) { for (int x = 0; x < iWidth; x++) { png_color_8 rgba; rgba.red = *pColors++; rgba.green = *pColors++; rgba.blue = *pColors++; rgba.gray = 0; rgba.alpha = (pAlpha && !bHasMask) ? *pAlpha++ : 0; // save in our palette long index = PaletteAdd(&palette, rgba); if (index < PNG_MAX_PALETTE_LENGTH) { // save in libpng's palette png_rgb[index].red = rgba.red; png_rgb[index].green = rgba.green; png_rgb[index].blue = rgba.blue; png_trans[index] = rgba.alpha; } else { bUsePalette = false; break; } } } if (bUsePalette) { png_set_PLTE(png_ptr, info_ptr, png_rgb, palette.size()); if (bHasMask && !pAlpha) { wxASSERT(PaletteFind(palette, mask) == 0); png_trans[0] = 0; png_set_tRNS(png_ptr, info_ptr, png_trans, 1, NULL); } else if (pAlpha && !bHasMask) { png_set_tRNS(png_ptr, info_ptr, png_trans, palette.size(), NULL); } } } /* If saving palettised was requested but it was decided we can't use a palette then reset the colour type to RGB. */ if (!bUsePalette && iColorType == wxPNG_TYPE_PALETTE) { iColorType = wxPNG_TYPE_COLOUR; } bool bUseAlpha = !bUsePalette && (bHasAlpha || bHasMask); int iPngColorType; if (bUsePalette) { iPngColorType = PNG_COLOR_TYPE_PALETTE; iColorType = wxPNG_TYPE_PALETTE; } else if ( iColorType==wxPNG_TYPE_COLOUR ) { iPngColorType = bUseAlpha ? PNG_COLOR_TYPE_RGB_ALPHA : PNG_COLOR_TYPE_RGB; } else { iPngColorType = bUseAlpha ? PNG_COLOR_TYPE_GRAY_ALPHA : PNG_COLOR_TYPE_GRAY; } if (image->HasOption(wxIMAGE_OPTION_PNG_FILTER)) png_set_filter( png_ptr, PNG_FILTER_TYPE_BASE, image->GetOptionInt(wxIMAGE_OPTION_PNG_FILTER) ); if (image->HasOption(wxIMAGE_OPTION_PNG_COMPRESSION_LEVEL)) png_set_compression_level( png_ptr, image->GetOptionInt(wxIMAGE_OPTION_PNG_COMPRESSION_LEVEL) ); if (image->HasOption(wxIMAGE_OPTION_PNG_COMPRESSION_MEM_LEVEL)) png_set_compression_mem_level( png_ptr, image->GetOptionInt(wxIMAGE_OPTION_PNG_COMPRESSION_MEM_LEVEL) ); if (image->HasOption(wxIMAGE_OPTION_PNG_COMPRESSION_STRATEGY)) png_set_compression_strategy( png_ptr, image->GetOptionInt(wxIMAGE_OPTION_PNG_COMPRESSION_STRATEGY) ); if (image->HasOption(wxIMAGE_OPTION_PNG_COMPRESSION_BUFFER_SIZE)) png_set_compression_buffer_size( png_ptr, image->GetOptionInt(wxIMAGE_OPTION_PNG_COMPRESSION_BUFFER_SIZE) ); int iBitDepth = !bUsePalette && image->HasOption(wxIMAGE_OPTION_PNG_BITDEPTH) ? image->GetOptionInt(wxIMAGE_OPTION_PNG_BITDEPTH) : 8; png_set_IHDR( png_ptr, info_ptr, image->GetWidth(), image->GetHeight(), iBitDepth, iPngColorType, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); int iElements; png_color_8 sig_bit; if ( iPngColorType & PNG_COLOR_MASK_COLOR ) { sig_bit.red = sig_bit.green = sig_bit.blue = (png_byte)iBitDepth; iElements = 3; } else // grey { sig_bit.gray = (png_byte)iBitDepth; iElements = 1; } if ( bUseAlpha ) { sig_bit.alpha = (png_byte)iBitDepth; iElements++; } if ( iBitDepth == 16 ) iElements *= 2; // save the image resolution if we have it int resX, resY; switch ( GetResolutionFromOptions(*image, &resX, &resY) ) { case wxIMAGE_RESOLUTION_INCHES: { const double INCHES_IN_METER = 10000.0 / 254; resX = int(resX * INCHES_IN_METER); resY = int(resY * INCHES_IN_METER); } break; case wxIMAGE_RESOLUTION_CM: resX *= 100; resY *= 100; break; case wxIMAGE_RESOLUTION_NONE: break; default: wxFAIL_MSG( wxT("unsupported image resolution units") ); } if ( resX && resY ) png_set_pHYs( png_ptr, info_ptr, resX, resY, PNG_RESOLUTION_METER ); png_set_sBIT( png_ptr, info_ptr, &sig_bit ); png_write_info( png_ptr, info_ptr ); png_set_shift( png_ptr, &sig_bit ); png_set_packing( png_ptr ); unsigned char * data = (unsigned char *)malloc( image->GetWidth() * iElements ); if ( !data ) { png_destroy_write_struct( &png_ptr, (png_infopp)NULL ); return false; } const unsigned char * pAlpha = (const unsigned char *)(bHasAlpha ? image->GetAlpha() : NULL); const unsigned char *pColors = image->GetData(); for (int y = 0; y != iHeight; ++y) { unsigned char *pData = data; for (int x = 0; x != iWidth; x++) { png_color_8 clr; clr.red = *pColors++; clr.green = *pColors++; clr.blue = *pColors++; clr.gray = 0; clr.alpha = (bUsePalette && pAlpha) ? *pAlpha++ : 0; // use with wxPNG_TYPE_PALETTE only switch ( iColorType ) { default: wxFAIL_MSG( wxT("unknown wxPNG_TYPE_XXX") ); // fall through case wxPNG_TYPE_COLOUR: *pData++ = clr.red; if ( iBitDepth == 16 ) *pData++ = 0; *pData++ = clr.green; if ( iBitDepth == 16 ) *pData++ = 0; *pData++ = clr.blue; if ( iBitDepth == 16 ) *pData++ = 0; break; case wxPNG_TYPE_GREY: { // where do these coefficients come from? maybe we // should have image options for them as well? unsigned uiColor = (unsigned) (76.544*(unsigned)clr.red + 150.272*(unsigned)clr.green + 36.864*(unsigned)clr.blue); *pData++ = (unsigned char)((uiColor >> 8) & 0xFF); if ( iBitDepth == 16 ) *pData++ = (unsigned char)(uiColor & 0xFF); } break; case wxPNG_TYPE_GREY_RED: *pData++ = clr.red; if ( iBitDepth == 16 ) *pData++ = 0; break; case wxPNG_TYPE_PALETTE: *pData++ = (unsigned char) PaletteFind(palette, clr); break; } if ( bUseAlpha ) { unsigned char uchAlpha = 255; if ( bHasAlpha ) uchAlpha = *pAlpha++; if ( bHasMask ) { if ( (clr.red == mask.red) && (clr.green == mask.green) && (clr.blue == mask.blue) ) uchAlpha = 0; } *pData++ = uchAlpha; if ( iBitDepth == 16 ) *pData++ = 0; } } png_bytep row_ptr = data; png_write_rows( png_ptr, &row_ptr, 1 ); } free(data); png_write_end( png_ptr, info_ptr ); png_destroy_write_struct( &png_ptr, (png_infopp)&info_ptr ); return true; }
/* * 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 */ }
void save_as_png(T & file, std::vector<mapnik::rgb> const& palette, mapnik::image_gray8 const& image, unsigned width, unsigned height, unsigned color_depth, std::vector<unsigned> const&alpha, png_options const& opts) { png_voidp error_ptr=0; png_structp png_ptr=png_create_write_struct(PNG_LIBPNG_VER_STRING, error_ptr,0, 0); if (!png_ptr) { return; } // switch on optimization only if supported #if defined(PNG_LIBPNG_VER) && (PNG_LIBPNG_VER >= 10200) && defined(PNG_MMX_CODE_SUPPORTED) png_uint_32 mask, flags; flags = png_get_asm_flags(png_ptr); mask = png_get_asm_flagmask(PNG_SELECT_READ | PNG_SELECT_WRITE); png_set_asm_flags(png_ptr, flags | mask); #endif png_set_filter(png_ptr, PNG_FILTER_TYPE_BASE, PNG_FILTER_NONE); png_infop info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_write_struct(&png_ptr,static_cast<png_infopp>(0)); return; } jmp_buf* jmp_context = static_cast<jmp_buf*>(png_get_error_ptr(png_ptr)); if (jmp_context) { png_destroy_write_struct(&png_ptr, &info_ptr); return; } png_set_write_fn (png_ptr, &file, &write_data<T>, &flush_data<T>); png_set_compression_level(png_ptr, opts.compression); png_set_compression_strategy(png_ptr, opts.strategy); png_set_compression_buffer_size(png_ptr, 32768); png_set_IHDR(png_ptr, info_ptr,width,height,color_depth, PNG_COLOR_TYPE_PALETTE,PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,PNG_FILTER_TYPE_DEFAULT); png_color* pal = const_cast<png_color*>(reinterpret_cast<const png_color*>(&palette[0])); png_set_PLTE(png_ptr, info_ptr, pal, static_cast<unsigned>(palette.size())); // make transparent lowest indexes, so tRNS is small if (alpha.size()>0) { std::vector<png_byte> trans(alpha.size()); unsigned alphaSize=0;//truncate to nonopaque values for(unsigned i=0; i < alpha.size(); i++) { trans[i]=alpha[i]; if (alpha[i]<255) { alphaSize = i+1; } } if (alphaSize>0) { png_set_tRNS(png_ptr, info_ptr, static_cast<png_bytep>(&trans[0]), alphaSize, 0); } } png_write_info(png_ptr, info_ptr); for (unsigned i=0;i<height;i++) { png_write_row(png_ptr,const_cast<png_bytep>(image.get_row(i))); } png_write_end(png_ptr, info_ptr); png_destroy_write_struct(&png_ptr, &info_ptr); }
static BOOL DLL_CALLCONV Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) { png_structp png_ptr; png_infop info_ptr; png_colorp palette = NULL; png_uint_32 width, height; BOOL has_alpha_channel = FALSE; RGBQUAD *pal; // pointer to dib palette int bit_depth, pixel_depth; // pixel_depth = bit_depth * channels int palette_entries; int interlace_type; fi_ioStructure fio; fio.s_handle = handle; fio.s_io = io; if ((dib) && (handle)) { try { // create the chunk manage structure png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, error_handler, warning_handler); if (!png_ptr) { return FALSE; } // allocate/initialize the image information data. info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_write_struct(&png_ptr, (png_infopp)NULL); return FALSE; } // Set error handling. REQUIRED if you aren't supplying your own // error handling functions in the png_create_write_struct() call. if (setjmp(png_jmpbuf(png_ptr))) { // if we get here, we had a problem reading the file png_destroy_write_struct(&png_ptr, &info_ptr); return FALSE; } // init the IO png_set_write_fn(png_ptr, &fio, _WriteProc, _FlushProc); // set physical resolution png_uint_32 res_x = (png_uint_32)FreeImage_GetDotsPerMeterX(dib); png_uint_32 res_y = (png_uint_32)FreeImage_GetDotsPerMeterY(dib); if ((res_x > 0) && (res_y > 0)) { png_set_pHYs(png_ptr, info_ptr, res_x, res_y, PNG_RESOLUTION_METER); } // Set the image information here. Width and height are up to 2^31, // bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on // the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY, // PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB, // or PNG_COLOR_TYPE_RGB_ALPHA. interlace is either PNG_INTERLACE_NONE or // PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST // currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED width = FreeImage_GetWidth(dib); height = FreeImage_GetHeight(dib); pixel_depth = FreeImage_GetBPP(dib); BOOL bInterlaced = FALSE; if( (flags & PNG_INTERLACED) == PNG_INTERLACED) { interlace_type = PNG_INTERLACE_ADAM7; bInterlaced = TRUE; } else { interlace_type = PNG_INTERLACE_NONE; } // set the ZLIB compression level or default to PNG default compression level (ZLIB level = 6) int zlib_level = flags & 0x0F; if((zlib_level >= 1) && (zlib_level <= 9)) { png_set_compression_level(png_ptr, zlib_level); } else if((flags & PNG_Z_NO_COMPRESSION) == PNG_Z_NO_COMPRESSION) { png_set_compression_level(png_ptr, Z_NO_COMPRESSION); } // filtered strategy works better for high color images if(pixel_depth >= 16){ png_set_compression_strategy(png_ptr, Z_FILTERED); png_set_filter(png_ptr, 0, PNG_FILTER_NONE|PNG_FILTER_SUB|PNG_FILTER_PAETH); } else { png_set_compression_strategy(png_ptr, Z_DEFAULT_STRATEGY); } FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib); if(image_type == FIT_BITMAP) { // standard image type bit_depth = (pixel_depth > 8) ? 8 : pixel_depth; } else { // 16-bit greyscale or 16-bit RGB(A) bit_depth = 16; } switch (FreeImage_GetColorType(dib)) { case FIC_MINISWHITE: // Invert monochrome files to have 0 as black and 1 as white (no break here) png_set_invert_mono(png_ptr); case FIC_MINISBLACK: png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, PNG_COLOR_TYPE_GRAY, interlace_type, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); break; case FIC_PALETTE: { png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, PNG_COLOR_TYPE_PALETTE, interlace_type, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); // set the palette palette_entries = 1 << bit_depth; palette = (png_colorp)png_malloc(png_ptr, palette_entries * sizeof (png_color)); pal = FreeImage_GetPalette(dib); for (int i = 0; i < palette_entries; i++) { palette[i].red = pal[i].rgbRed; palette[i].green = pal[i].rgbGreen; palette[i].blue = pal[i].rgbBlue; } png_set_PLTE(png_ptr, info_ptr, palette, palette_entries); // You must not free palette here, because png_set_PLTE only makes a link to // the palette that you malloced. Wait until you are about to destroy // the png structure. break; } case FIC_RGBALPHA : has_alpha_channel = TRUE; png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, PNG_COLOR_TYPE_RGBA, interlace_type, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); #if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR // flip BGR pixels to RGB if(image_type == FIT_BITMAP) { png_set_bgr(png_ptr); } #endif break; case FIC_RGB: png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, PNG_COLOR_TYPE_RGB, interlace_type, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); #if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR // flip BGR pixels to RGB if(image_type == FIT_BITMAP) { png_set_bgr(png_ptr); } #endif break; case FIC_CMYK: break; } // write possible ICC profile FIICCPROFILE *iccProfile = FreeImage_GetICCProfile(dib); if (iccProfile->size && iccProfile->data) { png_set_iCCP(png_ptr, info_ptr, "Embedded Profile", 0, (png_const_bytep)iccProfile->data, iccProfile->size); } // write metadata WriteMetadata(png_ptr, info_ptr, dib); // Optional gamma chunk is strongly suggested if you have any guess // as to the correct gamma of the image. // png_set_gAMA(png_ptr, info_ptr, gamma); // set the transparency table if (FreeImage_IsTransparent(dib) && (FreeImage_GetTransparencyCount(dib) > 0)) { png_set_tRNS(png_ptr, info_ptr, FreeImage_GetTransparencyTable(dib), FreeImage_GetTransparencyCount(dib), NULL); } // set the background color if(FreeImage_HasBackgroundColor(dib)) { png_color_16 image_background; RGBQUAD rgbBkColor; FreeImage_GetBackgroundColor(dib, &rgbBkColor); memset(&image_background, 0, sizeof(png_color_16)); image_background.blue = rgbBkColor.rgbBlue; image_background.green = rgbBkColor.rgbGreen; image_background.red = rgbBkColor.rgbRed; image_background.index = rgbBkColor.rgbReserved; png_set_bKGD(png_ptr, info_ptr, &image_background); } // Write the file header information. png_write_info(png_ptr, info_ptr); // write out the image data #ifndef FREEIMAGE_BIGENDIAN if (bit_depth == 16) { // turn on 16 bit byte swapping png_set_swap(png_ptr); } #endif int number_passes = 1; if (bInterlaced) { number_passes = png_set_interlace_handling(png_ptr); } if ((pixel_depth == 32) && (!has_alpha_channel)) { BYTE *buffer = (BYTE *)malloc(width * 3); // transparent conversion to 24-bit // the number of passes is either 1 for non-interlaced images, or 7 for interlaced images for (int pass = 0; pass < number_passes; pass++) { for (png_uint_32 k = 0; k < height; k++) { FreeImage_ConvertLine32To24(buffer, FreeImage_GetScanLine(dib, height - k - 1), width); png_write_row(png_ptr, buffer); } } free(buffer); } else { // the number of passes is either 1 for non-interlaced images, or 7 for interlaced images for (int pass = 0; pass < number_passes; pass++) { for (png_uint_32 k = 0; k < height; k++) { png_write_row(png_ptr, FreeImage_GetScanLine(dib, height - k - 1)); } } } // It is REQUIRED to call this to finish writing the rest of the file // Bug with png_flush png_write_end(png_ptr, info_ptr); // clean up after the write, and free any memory allocated if (palette) { png_free(png_ptr, palette); } png_destroy_write_struct(&png_ptr, &info_ptr); return TRUE; } catch (const char *text) { FreeImage_OutputMessageProc(s_format_id, text); } } return FALSE; }
bool MCImageEncodePNG(MCImageIndexedBitmap *p_indexed, IO_handle p_stream, uindex_t &r_bytes_written) { bool t_success = true; MCPNGWriteContext t_context; t_context.stream = p_stream; t_context.byte_count = 0; png_structp t_png_ptr = nil; png_infop t_info_ptr = nil; png_color *t_png_palette = nil; png_byte *t_png_transparency = nil; png_bytep t_data_ptr = nil; uindex_t t_stride = 0; /*init png stuff*/ if (t_success) { t_success = nil != (t_png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, (png_error_ptr)NULL, (png_error_ptr)NULL)); } if (t_success) t_success = nil != (t_info_ptr = png_create_info_struct(t_png_ptr)); /*in case of png error*/ if (setjmp(png_jmpbuf(t_png_ptr))) t_success = false; if (t_success) png_set_write_fn(t_png_ptr,(png_voidp)&t_context,fakewrite,fakeflush); if (t_success) { png_set_IHDR(t_png_ptr, t_info_ptr, p_indexed->width, p_indexed->height, 8, PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); png_set_gAMA(t_png_ptr, t_info_ptr, 1/MCgamma); } if (t_success) t_success = MCMemoryNewArray(p_indexed->palette_size, t_png_palette); /*create palette for 8 bit*/ if (t_success) { for (uindex_t i = 0; i < p_indexed->palette_size ; i++) { t_png_palette[i].red = p_indexed->palette[i].red >> 8; t_png_palette[i].green = p_indexed->palette[i].green >> 8; t_png_palette[i].blue = p_indexed->palette[i].blue >> 8; } png_set_PLTE(t_png_ptr, t_info_ptr, t_png_palette, p_indexed->palette_size); } if (MCImageIndexedBitmapHasTransparency(p_indexed)) { if (t_success) t_success = MCMemoryAllocate(p_indexed->palette_size, t_png_transparency); if (t_success) { memset(t_png_transparency, 0xFF, p_indexed->palette_size); t_png_transparency[p_indexed->transparent_index] = 0x00; png_set_tRNS(t_png_ptr, t_info_ptr, t_png_transparency, p_indexed->palette_size, NULL); } } if (t_success) png_write_info(t_png_ptr, t_info_ptr); if (t_success) { t_data_ptr = (png_bytep)p_indexed->data; t_stride = p_indexed->stride; } if (t_success) { for (uindex_t i = 0; i < p_indexed->height; i++) { png_write_row(t_png_ptr, t_data_ptr); t_data_ptr += t_stride; } } if (t_success) png_write_end(t_png_ptr, t_info_ptr); if (t_png_ptr != nil) png_destroy_write_struct(&t_png_ptr, &t_info_ptr); if (t_png_palette != nil) MCMemoryDeleteArray(t_png_palette); if (t_png_transparency != nil) MCMemoryDeallocate(t_png_transparency); if (t_success) r_bytes_written = t_context.byte_count; return t_success; }
AFRAMES OneFrameReader(png_structp png_ptr_read, png_infop info_ptr_read, png_structp png_ptr_write, png_infop info_ptr_write, png_uint_32 width, png_uint_32 height) { /* IHDR */ /* struct data need from background */ AFRAMES pframe; png_uint_32 garbage; int bit_depth; int colour_type; int interlace_method; int compression_method; int filter_method; /* PLTE */ png_colorp palette = NULL; int palette_size = 0; /* gAMA */ double gamma; /* tRNS */ png_bytep trans; int num_trans; png_color_16p trans_values; /* bKGD */ png_color_16p background; png_get_IHDR(png_ptr_read, info_ptr_read, &garbage, &garbage, &bit_depth, &colour_type, &interlace_method, &compression_method, &filter_method); png_set_IHDR(png_ptr_write, info_ptr_write, width, height, bit_depth, colour_type, interlace_method, compression_method, filter_method); int r = 0, g = 0, b = 0; int bpp = bit_depth; switch (colour_type) { case PNG_COLOR_TYPE_GRAY : break; case PNG_COLOR_TYPE_RGB : bpp *= 3; break; case PNG_COLOR_TYPE_PALETTE : break; case PNG_COLOR_TYPE_GRAY_ALPHA: bpp *= 2; break; case PNG_COLOR_TYPE_RGB_ALPHA : bpp *= 4; break; default: return pframe; } pframe.colortype = colour_type; pframe.bytedep = bpp; pframe.pngBG = trans_values; /////////qDebug() << "### color type " << colour_type << bpp << num_trans; if(png_get_valid(png_ptr_read, info_ptr_read, PNG_INFO_PLTE)) { /////////qDebug() << "### PNG_INFO_PLTE"; png_get_PLTE(png_ptr_read, info_ptr_read, &palette, &palette_size); png_set_PLTE(png_ptr_write, info_ptr_write, palette, palette_size); } if(png_get_valid(png_ptr_read, info_ptr_read, PNG_INFO_gAMA)) { /////////qDebug() << "### PNG_INFO_gAMA"; png_get_gAMA(png_ptr_read, info_ptr_read, &gamma); png_set_gAMA(png_ptr_write, info_ptr_write, gamma); } if(png_get_valid(png_ptr_read, info_ptr_read, PNG_INFO_tRNS)) { //////////qDebug() << "### PNG_INFO_tRNS"; png_get_tRNS(png_ptr_read, info_ptr_read, &trans, &num_trans, &trans_values); png_set_tRNS(png_ptr_write, info_ptr_write, trans, num_trans, trans_values); } if(png_get_valid(png_ptr_read, info_ptr_read, PNG_INFO_bKGD)) { ////////qDebug() << "### PNG_INFO_bKGD"; png_get_bKGD(png_ptr_read, info_ptr_read, &background); png_set_bKGD(png_ptr_write, info_ptr_write, background); png_color_16p bkgd = background; pframe.pngBG = background; int r = 0, g = 0, b = 0; if(colour_type == PNG_COLOR_TYPE_RGB || colour_type == PNG_COLOR_TYPE_RGB_ALPHA) { r = bkgd->red; g = bkgd->green; b = bkgd->blue; } if(colour_type == PNG_COLOR_TYPE_GRAY || colour_type == PNG_COLOR_TYPE_GRAY_ALPHA) { r = g = b = bkgd->gray; } if(colour_type == PNG_COLOR_TYPE_PALETTE) { r = palette[bkgd->index].red; g = palette[bkgd->index].green; b = palette[bkgd->index].blue; } // scale down to 8 bit color since QColor doesn't support 16 bit colors if(bit_depth > 8) { r >>= 8; g >>= 8; b >>= 8; } ////////qDebug() << "### color " << r << g << b; pframe.bg = QColor(r,g,b); pframe.foundcolor = true; } else {
cache Png_Create(int width, int height, int numpal, dPalette_t* pal, int bits, cache data, int lump, int* size) { int i = 0; int x = 0; int j = 0; cache image; cache out; cache* row_pointers; png_structp png_ptr; png_infop info_ptr; png_colorp palette; // setup png pointer png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0); if(png_ptr == NULL) { WGen_Complain("Png_Create: Failed getting png_ptr"); return NULL; } // setup info pointer info_ptr = png_create_info_struct(png_ptr); if(info_ptr == NULL) { png_destroy_write_struct(&png_ptr, NULL); WGen_Complain("Png_Create: Failed getting info_ptr"); return NULL; } // what does this do again? if(setjmp(png_jmpbuf(png_ptr))) { png_destroy_write_struct(&png_ptr, &info_ptr); WGen_Complain("Png_Create: Failed on setjmp"); return NULL; } // setup custom data writing procedure png_set_write_fn(png_ptr, NULL, Png_WriteData, NULL); // setup image png_set_IHDR( png_ptr, info_ptr, width, height, bits, PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_DEFAULT); // setup palette palette = (png_colorp)Mem_Alloc((16 * numpal) * png_sizeof(png_color)); // copy dPalette_t data over to png_colorp for(x = 0, j = 0; x < numpal; x++) { for(i = 0; i < 16; i++) { palette[j].red = pal[j].r; palette[j].green = pal[j].g; palette[j].blue = pal[j].b; j++; } } i = 0; // add palette to png png_set_PLTE(png_ptr, info_ptr, palette, (16 * numpal)); // set transparent index if(palette[0].red == 0 && palette[0].green == 0 && palette[0].blue == 0) { char tmp[9]; strncpy(tmp, romWadFile.lump[lump].name, 8); tmp[0] -= (char)0x80; tmp[8] = 0; // Exempt these lumps if(strcmp(tmp, "FIRE") && /*strcmp(tmp, "USLEGAL") && strcmp(tmp, "TITLE") &&*/ strcmp(tmp, "EVIL") && /*strcmp(tmp, "IDCRED1") && strcmp(tmp, "WMSCRED1") &&*/ strcmp(tmp, "SPACE") && strcmp(tmp, "CLOUD") && strcmp(tmp, "FINAL")) png_set_tRNS(png_ptr, info_ptr, (png_bytep)&i, 1, NULL); } // add png info to data png_write_info(png_ptr, info_ptr); // add offset chunk if png is a sprite if(INSPRITELIST(lump)) { for(i = 0; i < spriteExCount; i++) { if(exSpriteLump[i].lumpRef == lump) { int offs[2]; offs[0] = WGen_Swap32(exSpriteLump[i].sprite.offsetx); offs[1] = WGen_Swap32(exSpriteLump[i].sprite.offsety); png_write_chunk(png_ptr, "grAb", (byte*)offs, 8); break; } } } // setup packing if needed png_set_packing(png_ptr); png_set_packswap(png_ptr); // copy data over image = data; row_pointers = (cache*)Mem_Alloc(sizeof(byte*) * height); for(i = 0; i < height; i++) { row_pointers[i] = (cache)Mem_Alloc(width); if(bits == 4) { for(j = 0; j < width; j += 2) { row_pointers[i][j] = *image & 0xf; row_pointers[i][j+1] = *image >> 4; image++; } } else { for(j = 0; j < width; j++) { row_pointers[i][j] = *image; image++; } } png_write_rows(png_ptr, &row_pointers[i], 1); }
bool image_io_png_write(const rct_drawpixelinfo *dpi, const rct_palette *palette, const utf8 *path) { // Get image size int stride = dpi->width + dpi->pitch; // Setup PNG png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (png_ptr == NULL) { return false; } png_infop info_ptr = png_create_info_struct(png_ptr); if (info_ptr == NULL) { png_destroy_write_struct(&png_ptr, (png_infopp)NULL); return false; } png_colorp png_palette = (png_colorp)png_malloc(png_ptr, PNG_MAX_PALETTE_LENGTH * sizeof(png_color)); for (int i = 0; i < 256; i++) { const rct_palette_entry *entry = &palette->entries[i]; png_palette[i].blue = entry->blue; png_palette[i].green = entry->green; png_palette[i].red = entry->red; } png_set_PLTE(png_ptr, info_ptr, png_palette, PNG_MAX_PALETTE_LENGTH); // Open file for writing SDL_RWops *file = SDL_RWFromFile(path, "wb"); if (file == NULL) { png_free(png_ptr, png_palette); png_destroy_write_struct(&png_ptr, (png_infopp)NULL); return false; } png_set_write_fn(png_ptr, file, my_png_write_data, my_png_flush); // Set error handler if (setjmp(png_jmpbuf(png_ptr))) { png_free(png_ptr, png_palette); png_destroy_write_struct(&png_ptr, &info_ptr); SDL_RWclose(file); return false; } // Write header png_set_IHDR( png_ptr, info_ptr, dpi->width, dpi->height, 8, PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT ); png_byte transparentIndex = 0; png_set_tRNS(png_ptr, info_ptr, &transparentIndex, 1, NULL); png_write_info(png_ptr, info_ptr); // Write pixels uint8 *bits = dpi->bits; for (int y = 0; y < dpi->height; y++) { png_write_row(png_ptr, (png_byte *)bits); bits += stride; } // Finish png_write_end(png_ptr, NULL); SDL_RWclose(file); png_free(png_ptr, png_palette); png_destroy_write_struct(&png_ptr, &info_ptr); return true; }
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; }
int R_SaveAsPng(void *d, int width, int height, unsigned int (*gp)(void *, int, int), int bgr, FILE *fp, unsigned int transparent, int res) { png_structp png_ptr; png_infop info_ptr; unsigned int col, palette[256]; png_color pngpalette[256]; png_bytep pscanline; png_bytep scanline = (png_bytep) calloc((size_t)(4*width),sizeof(png_byte)); png_byte trans[256]; png_color_16 trans_values[1]; int i, j, r, ncols, mid, high, low, withpalette, have_alpha; volatile DECLARESHIFTS; /* Have we enough memory?*/ if (scanline == NULL) return 0; if (fp == NULL) { free(scanline); return 0; } /* Create and initialize the png_struct with the desired error handler * functions. If you want to use the default stderr and longjump method, * you can supply NULL for the last three parameters. We also check that * the library version is compatible with the one used at compile time, * in case we are using dynamically linked libraries. REQUIRED. */ png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (png_ptr == NULL) { free(scanline); return 0; } /* Allocate/initialize the image information data. REQUIRED */ info_ptr = png_create_info_struct(png_ptr); if (info_ptr == NULL) { free(scanline); png_destroy_write_struct(&png_ptr, (png_infopp)NULL); return 0; } /* Set error handling. REQUIRED if you aren't supplying your own * error handling functions in the png_create_write_struct() call. */ #if PNG_LIBPNG_VER < 10400 if (setjmp(png_ptr->jmpbuf)) #else if (setjmp(png_jmpbuf(png_ptr))) #endif { /* If we get here, we had a problem writing the file */ free(scanline); png_destroy_write_struct(&png_ptr, &info_ptr); return 0; } png_set_error_fn(png_ptr, NULL, my_png_error, my_png_warning); /* I/O initialization functions is REQUIRED */ png_init_io(png_ptr, fp); /* Have we less than 256 different colors? */ ncols = 0; if(transparent) palette[ncols++] = transparent & 0xFFFFFF; mid = ncols; withpalette = 1; have_alpha = 0; for (i = 0; (i < height) && withpalette ; i++) { for (j = 0; (j < width) && withpalette ; j++) { col = gp(d,i,j); if (GETALPHA(col) < 255) have_alpha = 1; /* binary search the palette: */ low = 0; high = ncols - 1; while (low <= high) { mid = (low + high)/2; if ( col < palette[mid] ) high = mid - 1; else if ( col > palette[mid] ) low = mid + 1; else break; } if (high < low) { /* didn't find colour in palette, insert it: */ if (ncols >= 256) { withpalette = 0; } else { for (r = ncols; r > low; r--) palette[r] = palette[r-1] ; palette[low] = col; ncols ++; } } } } col = gp(d,0,0); //have_alpha &= (transparent == 0); /* Set the image information here. Width and height are up to 2^31, * bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on * the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY, * PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB, * or PNG_COLOR_TYPE_RGB_ALPHA. interlace is either PNG_INTERLACE_NONE or * PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST * currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED */ png_set_IHDR(png_ptr, info_ptr, width, height, 8, withpalette ? PNG_COLOR_TYPE_PALETTE : (have_alpha ? PNG_COLOR_TYPE_RGB_ALPHA : PNG_COLOR_TYPE_RGB), PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); if (withpalette) { for (i = 0; i < ncols ; i++) { col = palette[i]; if(transparent) { trans[i] = (col == transparent) ? 0:255; pngpalette[i].red = GETRED(col); pngpalette[i].green = GETGREEN(col); pngpalette[i].blue = GETBLUE(col); } else { /* PNG needs NON-premultiplied alpha */ int a = GETALPHA(col); trans[i] = a; if(a == 255 || a == 0) { pngpalette[i].red = GETRED(col); pngpalette[i].green = GETGREEN(col); pngpalette[i].blue = GETBLUE(col); } else { pngpalette[i].red = 0.49 + 255.0*GETRED(col)/a; pngpalette[i].green = 0.49 + 255.0*GETGREEN(col)/a; pngpalette[i].blue = 0.49 + 255.0*GETBLUE(col)/a; } } } png_set_PLTE(png_ptr, info_ptr, pngpalette, ncols); if (transparent || have_alpha) png_set_tRNS(png_ptr, info_ptr, trans, ncols, trans_values); } /* Deal with transparency */ if(transparent && !withpalette) { trans_values[0].red = GETRED(transparent); trans_values[0].blue = GETBLUE(transparent); trans_values[0].green = GETGREEN(transparent); png_set_tRNS(png_ptr, info_ptr, trans, ncols, trans_values); } if(res > 0) png_set_pHYs(png_ptr, info_ptr, res/0.0254, res/0.0254, PNG_RESOLUTION_METER); /* Write the file header information. REQUIRED */ png_write_info(png_ptr, info_ptr); /* * Now, write the pixels */ for (i = 0 ; i < height ; i++) { /* Build the scanline */ pscanline = scanline; for (j = 0 ; j < width ; j++) { col = gp(d, i, j); if (withpalette) { /* binary search the palette (the colour must be there): */ low = 0; high = ncols - 1; while (low <= high) { mid = (low + high)/2; if (col < palette[mid]) high = mid - 1; else if (col > palette[mid]) low = mid + 1; else break; } *pscanline++ = mid; } else { if(have_alpha) { /* PNG needs NON-premultiplied alpha */ int a = GETALPHA(col); if(a == 255 || a == 0) { *pscanline++ = GETRED(col) ; *pscanline++ = GETGREEN(col) ; *pscanline++ = GETBLUE(col) ; *pscanline++ = a; } else { *pscanline++ = 0.49 + 255.0*GETRED(col)/a ; *pscanline++ = 0.49 + 255.0*GETGREEN(col)/a ; *pscanline++ = 0.49 + 255.0*GETBLUE(col)/a ; *pscanline++ = a; } } else { *pscanline++ = GETRED(col) ; *pscanline++ = GETGREEN(col) ; *pscanline++ = GETBLUE(col) ; } } } png_write_row(png_ptr, scanline); } /* It is REQUIRED to call this to finish writing the rest of the file */ png_write_end(png_ptr, info_ptr); /* clean up after the write, and free any memory allocated */ free(scanline); png_destroy_write_struct(&png_ptr, &info_ptr); /* that's it */ return 1; }
/* * 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 */ }
/* * Reduce the image type from grayscale(+alpha) or RGB(+alpha) to palette, * if possible. * The parameter reductions indicates the intended reductions. * The function returns the successful reductions. */ png_uint_32 /* PRIVATE */ opng_reduce_to_palette(png_structp png_ptr, png_infop info_ptr, png_uint_32 reductions) { png_uint_32 result; png_bytepp row_ptr; png_bytep sample_ptr, alpha_row; png_uint_32 height, width, channels, i, j; unsigned int color_type, dest_bit_depth; png_color palette[256]; png_byte trans_alpha[256]; int num_palette, num_trans, index; png_color_16p background; unsigned int gray, red, green, blue, alpha; unsigned int prev_gray, prev_red, prev_green, prev_blue, prev_alpha; opng_debug(1, "in opng_reduce_to_palette"); if (info_ptr->bit_depth != 8) return OPNG_REDUCE_NONE; /* nothing is done in this case */ color_type = info_ptr->color_type; OPNG_ASSERT(!(info_ptr->color_type & PNG_COLOR_MASK_PALETTE)); row_ptr = info_ptr->row_pointers; height = info_ptr->height; width = info_ptr->width; channels = info_ptr->channels; alpha_row = (png_bytep)png_malloc(png_ptr, width); /* Analyze the possibility of this reduction. */ num_palette = num_trans = 0; prev_gray = prev_red = prev_green = prev_blue = prev_alpha = 256; for (i = 0; i < height; ++i, ++row_ptr) { sample_ptr = *row_ptr; opng_get_alpha_row(png_ptr, info_ptr, *row_ptr, alpha_row); if (color_type & PNG_COLOR_MASK_COLOR) { for (j = 0; j < width; ++j, sample_ptr += channels) { red = sample_ptr[0]; green = sample_ptr[1]; blue = sample_ptr[2]; alpha = alpha_row[j]; /* Check the cache first. */ if (red != prev_red || green != prev_green || blue != prev_blue || alpha != prev_alpha) { prev_red = red; prev_green = green; prev_blue = blue; prev_alpha = alpha; if (opng_insert_palette_entry(palette, &num_palette, trans_alpha, &num_trans, 256, red, green, blue, alpha, &index) < 0) /* overflow */ { OPNG_ASSERT(num_palette < 0); i = height; /* forced exit from outer loop */ break; } } } } else /* grayscale */ { for (j = 0; j < width; ++j, sample_ptr += channels) { gray = sample_ptr[0]; alpha = alpha_row[j]; /* Check the cache first. */ if (gray != prev_gray || alpha != prev_alpha) { prev_gray = gray; prev_alpha = alpha; if (opng_insert_palette_entry(palette, &num_palette, trans_alpha, &num_trans, 256, gray, gray, gray, alpha, &index) < 0) /* overflow */ { OPNG_ASSERT(num_palette < 0); i = height; /* forced exit from outer loop */ break; } } } } } #if defined(PNG_bKGD_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) if ((num_palette >= 0) && (info_ptr->valid & PNG_INFO_bKGD)) { /* bKGD has an alpha-agnostic palette entry. */ background = &info_ptr->background; if (color_type & PNG_COLOR_MASK_COLOR) { red = background->red; green = background->green; blue = background->blue; } else red = green = blue = background->gray; opng_insert_palette_entry(palette, &num_palette, trans_alpha, &num_trans, 256, red, green, blue, 256, &index); if (index >= 0) background->index = (png_byte)index; } #endif /* Continue only if the uncompressed indexed image (pixels + PLTE + tRNS) * is smaller than the uncompressed RGB(A) image. * Casual overhead (headers, CRCs, etc.) is ignored. * * Compare: * num_pixels * (src_bit_depth * channels - dest_bit_depth) / 8 * vs. * sizeof(PLTE) + sizeof(tRNS) */ if (num_palette >= 0) { OPNG_ASSERT(num_palette > 0 && num_palette <= 256); OPNG_ASSERT(num_trans >= 0 && num_trans <= num_palette); if (num_palette <= 2) dest_bit_depth = 1; else if (num_palette <= 4) dest_bit_depth = 2; else if (num_palette <= 16) dest_bit_depth = 4; else dest_bit_depth = 8; /* Do the comparison in a way that does not cause overflow. */ if (channels * 8 == dest_bit_depth || (3 * num_palette + num_trans) * 8 / (channels * 8 - dest_bit_depth) / width / height >= 1) num_palette = -1; } if (num_palette < 0) /* can't reduce */ { png_free(png_ptr, alpha_row); return OPNG_REDUCE_NONE; } /* Reduce. */ row_ptr = info_ptr->row_pointers; index = -1; prev_red = prev_green = prev_blue = prev_alpha = (unsigned int)(-1); for (i = 0; i < height; ++i, ++row_ptr) { sample_ptr = *row_ptr; opng_get_alpha_row(png_ptr, info_ptr, *row_ptr, alpha_row); if (color_type & PNG_COLOR_MASK_COLOR) { for (j = 0; j < width; ++j, sample_ptr += channels) { red = sample_ptr[0]; green = sample_ptr[1]; blue = sample_ptr[2]; alpha = alpha_row[j]; /* Check the cache first. */ if (red != prev_red || green != prev_green || blue != prev_blue || alpha != prev_alpha) { prev_red = red; prev_green = green; prev_blue = blue; prev_alpha = alpha; if (opng_insert_palette_entry(palette, &num_palette, trans_alpha, &num_trans, 256, red, green, blue, alpha, &index) != 0) index = -1; /* this should not happen */ } OPNG_ASSERT(index >= 0); (*row_ptr)[j] = (png_byte)index; } } else /* grayscale */ { for (j = 0; j < width; ++j, sample_ptr += channels) { gray = sample_ptr[0]; alpha = alpha_row[j]; /* Check the cache first. */ if (gray != prev_gray || alpha != prev_alpha) { prev_gray = gray; prev_alpha = alpha; if (opng_insert_palette_entry(palette, &num_palette, trans_alpha, &num_trans, 256, gray, gray, gray, alpha, &index) != 0) index = -1; /* this should not happen */ } OPNG_ASSERT(index >= 0); (*row_ptr)[j] = (png_byte)index; } } } /* Update the image info. */ png_ptr->rowbytes = info_ptr->rowbytes = 0; png_ptr->color_type = info_ptr->color_type = PNG_COLOR_TYPE_PALETTE; png_ptr->channels = info_ptr->channels = 1; png_ptr->pixel_depth = info_ptr->pixel_depth = 8; png_set_PLTE(png_ptr, info_ptr, palette, num_palette); if (num_trans > 0) png_set_tRNS(png_ptr, info_ptr, trans_alpha, num_trans, NULL); /* bKGD (if present) is already updated. */ png_free(png_ptr, alpha_row); result = OPNG_REDUCE_RGB_TO_PALETTE; if (reductions & OPNG_REDUCE_8_TO_4_2_1) result |= opng_reduce_palette_bits(png_ptr, info_ptr, reductions); return result; }
pngquant_error rwpng_write_image_init(FILE *outfile, write_info *mainprog_ptr) { png_structp png_ptr; /* note: temporary variables! */ png_infop info_ptr; /* could also replace libpng warning-handler (final NULL), but no need: */ png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, mainprog_ptr, rwpng_error_handler, NULL); if (!png_ptr) { return INIT_OUT_OF_MEMORY_ERROR; /* out of memory */ } mainprog_ptr->png_ptr = png_ptr; info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_write_struct(&png_ptr, NULL); return INIT_OUT_OF_MEMORY_ERROR; /* 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 LIBPNG_INIT_ERROR; /* libpng error (via longjmp()) */ } /* make sure outfile is (re)opened in BINARY mode */ png_init_io(png_ptr, outfile); png_set_compression_level(png_ptr, Z_BEST_COMPRESSION); // Palette images generally don't gain anything from filtering png_set_filter(png_ptr, PNG_FILTER_TYPE_BASE, PNG_FILTER_VALUE_NONE); /* set the image parameters appropriately */ int sample_depth; if (mainprog_ptr->num_palette <= 2) sample_depth = 1; else if (mainprog_ptr->num_palette <= 4) sample_depth = 2; else if (mainprog_ptr->num_palette <= 16) sample_depth = 4; else sample_depth = 8; png_set_IHDR(png_ptr, info_ptr, mainprog_ptr->width, mainprog_ptr->height, sample_depth, PNG_COLOR_TYPE_PALETTE, mainprog_ptr->interlaced, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_BASE); /* GRR WARNING: cast of rwpng_colorp to png_colorp could fail in future * major revisions of libpng (but png_ptr/info_ptr will fail, regardless) */ png_set_PLTE(png_ptr, info_ptr, &mainprog_ptr->palette[0], mainprog_ptr->num_palette); if (mainprog_ptr->num_trans > 0) png_set_tRNS(png_ptr, info_ptr, mainprog_ptr->trans, mainprog_ptr->num_trans, NULL); if (mainprog_ptr->gamma > 0.0) png_set_gAMA(png_ptr, info_ptr, mainprog_ptr->gamma); /* 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); /* 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 SUCCESS; }
static int writePNG(QRcode *qrcode, const char *outfile) { static FILE *fp; // avoid clobbering by setjmp. png_structp png_ptr; png_infop info_ptr; png_colorp palette; png_byte alpha_values[2]; unsigned char *row, *p, *q; int x, y, xx, yy, bit; int realwidth; realwidth = (qrcode->width + margin * 2) * size; row = (unsigned char *)malloc((realwidth + 7) / 8); if(row == NULL) { fprintf(stderr, "Failed to allocate memory.\n"); exit(EXIT_FAILURE); } if(outfile[0] == '-' && outfile[1] == '\0') { fp = stdout; } else { fp = fopen(outfile, "wb"); if(fp == NULL) { fprintf(stderr, "Failed to create file: %s\n", outfile); perror(NULL); exit(EXIT_FAILURE); } } png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if(png_ptr == NULL) { fprintf(stderr, "Failed to initialize PNG writer.\n"); exit(EXIT_FAILURE); } info_ptr = png_create_info_struct(png_ptr); if(info_ptr == NULL) { fprintf(stderr, "Failed to initialize PNG write.\n"); exit(EXIT_FAILURE); } if(setjmp(png_jmpbuf(png_ptr))) { png_destroy_write_struct(&png_ptr, &info_ptr); fprintf(stderr, "Failed to write PNG image.\n"); exit(EXIT_FAILURE); } palette = (png_colorp) malloc(sizeof(png_color) * 2); if(palette == NULL) { fprintf(stderr, "Failed to allocate memory.\n"); exit(EXIT_FAILURE); } palette[0].red = fg_color[0]; palette[0].green = fg_color[1]; palette[0].blue = fg_color[2]; palette[1].red = bg_color[0]; palette[1].green = bg_color[1]; palette[1].blue = bg_color[2]; alpha_values[0] = fg_color[3]; alpha_values[1] = bg_color[3]; png_set_PLTE(png_ptr, info_ptr, palette, 2); png_set_tRNS(png_ptr, info_ptr, alpha_values, 2, NULL); png_init_io(png_ptr, fp); png_set_IHDR(png_ptr, info_ptr, realwidth, realwidth, 1, PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); png_set_pHYs(png_ptr, info_ptr, dpi * INCHES_PER_METER, dpi * INCHES_PER_METER, PNG_RESOLUTION_METER); png_write_info(png_ptr, info_ptr); /* top margin */ memset(row, 0xff, (realwidth + 7) / 8); for(y=0; y<margin * size; y++) { png_write_row(png_ptr, row); } /* data */ p = qrcode->data; for(y=0; y<qrcode->width; y++) { bit = 7; memset(row, 0xff, (realwidth + 7) / 8); q = row; q += margin * size / 8; bit = 7 - (margin * size % 8); for(x=0; x<qrcode->width; x++) { for(xx=0; xx<size; xx++) { *q ^= (*p & 1) << bit; bit--; if(bit < 0) { q++; bit = 7; } } p++; } for(yy=0; yy<size; yy++) { png_write_row(png_ptr, row); } } /* bottom margin */ memset(row, 0xff, (realwidth + 7) / 8); for(y=0; y<margin * size; y++) { png_write_row(png_ptr, row); } png_write_end(png_ptr, info_ptr); png_destroy_write_struct(&png_ptr, &info_ptr); fclose(fp); free(row); free(palette); return 0; }
/* * Reduce the image type from grayscale(+alpha) or RGB(+alpha) to palette, * if possible. * The parameter reductions indicates the intended reductions. * The function returns the successful reductions. */ static png_uint_32 opng_reduce_to_palette(png_structp png_ptr, png_infop info_ptr, png_uint_32 reductions) { png_row_info row_info; png_bytep sample_ptr; png_uint_32 height, width; int color_type, interlace_type, compression_type, filter_type; int src_bit_depth; png_color palette[256]; png_byte trans_alpha[256]; int num_trans, index; unsigned gray, red, green, blue, alpha; unsigned prev_red, prev_green, prev_blue, prev_alpha; png_uint_32 j; png_get_IHDR(png_ptr, info_ptr, &width, &height, &src_bit_depth, &color_type, &interlace_type, &compression_type, &filter_type); if (src_bit_depth != 8 || !height || !width) return OPNG_REDUCE_NONE; /* nothing is done in this case */ OPNG_ASSERT(!(color_type & PNG_COLOR_MASK_PALETTE)); png_bytepp row_ptr = png_get_rows(png_ptr, info_ptr); int channels = png_get_channels(png_ptr, info_ptr); png_bytep alpha_row = (png_bytep)png_malloc(png_ptr, width); if (!alpha_row){ exit(1); } row_info.width = width; row_info.rowbytes = 0; /* not used */ row_info.color_type = (png_byte)color_type; row_info.bit_depth = (png_byte)src_bit_depth; row_info.channels = (png_byte)channels; row_info.pixel_depth = 0; /* not used */ /* Analyze the possibility of this reduction. */ int num_palette = num_trans = 0; png_color_16p trans_color = 0; png_get_tRNS(png_ptr, info_ptr, 0, 0, &trans_color); unsigned prev_gray = prev_red = prev_green = prev_blue = prev_alpha = 256; png_uint_32 i; for (i = 0; i < height; ++i, ++row_ptr) { sample_ptr = *row_ptr; opng_get_alpha_row(&row_info, trans_color, *row_ptr, alpha_row); if (color_type & PNG_COLOR_MASK_COLOR) { for (j = 0; j < width; ++j, sample_ptr += channels) { red = sample_ptr[0]; green = sample_ptr[1]; blue = sample_ptr[2]; alpha = alpha_row[j]; /* Check the cache first. */ if (red != prev_red || green != prev_green || blue != prev_blue || alpha != prev_alpha) { prev_red = red; prev_green = green; prev_blue = blue; prev_alpha = alpha; if (opng_insert_palette_entry(palette, &num_palette, trans_alpha, &num_trans, 256, red, green, blue, alpha, &index) < 0) /* overflow */ { OPNG_ASSERT(num_palette < 0); i = height; /* forced exit from outer loop */ break; } } } } else /* grayscale */ { for (j = 0; j < width; ++j, sample_ptr += channels) { gray = sample_ptr[0]; alpha = alpha_row[j]; /* Check the cache first. */ if (gray != prev_gray || alpha != prev_alpha) { prev_gray = gray; prev_alpha = alpha; if (opng_insert_palette_entry(palette, &num_palette, trans_alpha, &num_trans, 256, gray, gray, gray, alpha, &index) < 0) /* overflow */ { OPNG_ASSERT(num_palette < 0); i = height; /* forced exit from outer loop */ break; } } } } } #ifdef PNG_bKGD_SUPPORTED png_color_16p background; if ((num_palette >= 0) && png_get_bKGD(png_ptr, info_ptr, &background)) { /* bKGD has an alpha-agnostic palette entry. */ if (color_type & PNG_COLOR_MASK_COLOR) { red = background->red; green = background->green; blue = background->blue; } else red = green = blue = background->gray; opng_insert_palette_entry(palette, &num_palette, trans_alpha, &num_trans, 256, red, green, blue, 256, &index); if (index >= 0) background->index = (png_byte)index; } #endif /* Continue only if the uncompressed indexed image (pixels + PLTE + tRNS) * is smaller than the uncompressed RGB(A) image. * Casual overhead (headers, CRCs, etc.) is ignored. * * Compare: * num_pixels * (src_bit_depth * channels - dest_bit_depth) / 8 * vs. * sizeof(PLTE) + sizeof(tRNS) */ /* 5/3 times sizeof(PLTE) + sizeof(tRNS) as: 1. Palette is uncompressed additional IDAT data is 2. Headers */ if (num_palette >= 0) { int dest_bit_depth; OPNG_ASSERT(num_palette > 0 && num_palette <= 256); OPNG_ASSERT(num_trans >= 0 && num_trans <= num_palette); if (num_palette <= 2) dest_bit_depth = 1; else if (num_palette <= 4) dest_bit_depth = 2; else if (num_palette <= 16) dest_bit_depth = 4; else dest_bit_depth = 8; /* Do the comparison in a way that does not cause overflow. */ /*if (channels * 8 == dest_bit_depth || (3 * num_palette + num_trans) * 8 / (channels * 8 - dest_bit_depth) / width / height >= 1) */ if (channels * 8 == dest_bit_depth || (12 + (5 * num_palette + num_trans)) * 8 / (channels * 8 - dest_bit_depth) / width / height >= 1) {num_palette = -1;} if ((num_palette && width * height < 12u * num_palette) || width * height < 8000){ num_palette = -1; } } if (num_palette < 0) /* can't reduce */ { png_free(png_ptr, alpha_row); return OPNG_REDUCE_NONE; } /* Reduce. */ row_ptr = png_get_rows(png_ptr, info_ptr); index = -1; prev_red = prev_green = prev_blue = prev_alpha = (unsigned int)(-1); for (i = 0; i < height; ++i, ++row_ptr) { sample_ptr = *row_ptr; opng_get_alpha_row(&row_info, trans_color, *row_ptr, alpha_row); if (color_type & PNG_COLOR_MASK_COLOR) { for (j = 0; j < width; ++j, sample_ptr += channels) { red = sample_ptr[0]; green = sample_ptr[1]; blue = sample_ptr[2]; alpha = alpha_row[j]; /* Check the cache first. */ if (red != prev_red || green != prev_green || blue != prev_blue || alpha != prev_alpha) { prev_red = red; prev_green = green; prev_blue = blue; prev_alpha = alpha; if (opng_insert_palette_entry(palette, &num_palette, trans_alpha, &num_trans, 256, red, green, blue, alpha, &index) != 0) index = -1; /* this should not happen */ } OPNG_ASSERT(index >= 0); (*row_ptr)[j] = (png_byte)index; } } else /* grayscale */ { for (j = 0; j < width; ++j, sample_ptr += channels) { gray = sample_ptr[0]; alpha = alpha_row[j]; /* Check the cache first. */ if (gray != prev_gray || alpha != prev_alpha) { prev_gray = gray; prev_alpha = alpha; if (opng_insert_palette_entry(palette, &num_palette, trans_alpha, &num_trans, 256, gray, gray, gray, alpha, &index) != 0) index = -1; /* this should not happen */ } OPNG_ASSERT(index >= 0); (*row_ptr)[j] = (png_byte)index; } } } /* Update the image information. */ png_set_IHDR(png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_PALETTE, interlace_type, compression_type, filter_type); png_set_PLTE(png_ptr, info_ptr, palette, num_palette); if (num_trans > 0) png_set_tRNS(png_ptr, info_ptr, trans_alpha, num_trans, 0); /* bKGD (if present) is automatically updated. */ png_free(png_ptr, alpha_row); png_uint_32 result = OPNG_REDUCE_RGB_TO_PALETTE; if (reductions & OPNG_REDUCE_8_TO_4_2_1) result |= opng_reduce_palette_bits(png_ptr, info_ptr, reductions); return result; }
void save_as_png(T & file, std::vector<rgb> const& palette, image_data_8 const& image, unsigned width, unsigned height, unsigned color_depth, int compression, int strategy, std::vector<unsigned> const&alpha, bool use_miniz) { if (use_miniz) { MiniZ::PNGWriter writer(compression); // image.width()/height() does not reflect the actual image dimensions; it // refers to the quantized scanlines. writer.writeIHDR(width, height, color_depth); writer.writePLTE(palette); writer.writetRNS(alpha); writer.writeIDAT(image); writer.writeIEND(); writer.toStream(file); return; } png_voidp error_ptr=0; png_structp png_ptr=png_create_write_struct(PNG_LIBPNG_VER_STRING, error_ptr,0, 0); if (!png_ptr) return; // switch on optimization only if supported #if defined(PNG_LIBPNG_VER) && (PNG_LIBPNG_VER >= 10200) && defined(PNG_MMX_CODE_SUPPORTED) png_uint_32 mask, flags; flags = png_get_asm_flags(png_ptr); mask = png_get_asm_flagmask(PNG_SELECT_READ | PNG_SELECT_WRITE); png_set_asm_flags(png_ptr, flags | mask); #endif png_set_filter(png_ptr, PNG_FILTER_TYPE_BASE, PNG_FILTER_NONE); png_infop info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_write_struct(&png_ptr,(png_infopp)0); return; } jmp_buf* jmp_context = (jmp_buf*) png_get_error_ptr(png_ptr); if (jmp_context) { png_destroy_write_struct(&png_ptr, &info_ptr); return; } png_set_write_fn (png_ptr, &file, &write_data<T>, &flush_data<T>); png_set_compression_level(png_ptr, compression); png_set_compression_strategy(png_ptr, strategy); png_set_compression_buffer_size(png_ptr, 32768); png_set_IHDR(png_ptr, info_ptr,width,height,color_depth, PNG_COLOR_TYPE_PALETTE,PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,PNG_FILTER_TYPE_DEFAULT); png_color* pal = const_cast<png_color*>(reinterpret_cast<const png_color*>(&palette[0])); png_set_PLTE(png_ptr, info_ptr, pal, palette.size()); // make transparent lowest indexes, so tRNS is small if (alpha.size()>0) { std::vector<png_byte> trans(alpha.size()); unsigned alphaSize=0;//truncate to nonopaque values for(unsigned i=0; i < alpha.size(); i++) { trans[i]=alpha[i]; if (alpha[i]<255) alphaSize = i+1; } if (alphaSize>0) png_set_tRNS(png_ptr, info_ptr, (png_bytep)&trans[0], alphaSize, 0); } png_write_info(png_ptr, info_ptr); for (unsigned i=0; i<height; i++) { png_write_row(png_ptr,(png_bytep)image.getRow(i)); } png_write_end(png_ptr, info_ptr); png_destroy_write_struct(&png_ptr, &info_ptr); }
bool PngWrite(const rct_drawpixelinfo * dpi, const rct_palette * palette, const utf8 * path) { bool result = false; // Get image size int stride = dpi->width + dpi->pitch; // Setup PNG png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); if (png_ptr == nullptr) { return false; } png_infop info_ptr = png_create_info_struct(png_ptr); if (info_ptr == nullptr) { png_destroy_write_struct(&png_ptr, (png_infopp)nullptr); return false; } png_colorp png_palette = (png_colorp)png_malloc(png_ptr, PNG_MAX_PALETTE_LENGTH * sizeof(png_color)); for (int i = 0; i < 256; i++) { const rct_palette_entry *entry = &palette->entries[i]; png_palette[i].blue = entry->blue; png_palette[i].green = entry->green; png_palette[i].red = entry->red; } png_set_PLTE(png_ptr, info_ptr, png_palette, PNG_MAX_PALETTE_LENGTH); try { // Open file for writing auto fs = FileStream(path, FILE_MODE_WRITE); png_set_write_fn(png_ptr, &fs, PngWriteData, PngFlush); // Set error handler if (setjmp(png_jmpbuf(png_ptr))) { throw Exception("PNG ERROR"); } // Write header png_set_IHDR( png_ptr, info_ptr, dpi->width, dpi->height, 8, PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT ); png_byte transparentIndex = 0; png_set_tRNS(png_ptr, info_ptr, &transparentIndex, 1, nullptr); png_write_info(png_ptr, info_ptr); // Write pixels uint8 * bits = dpi->bits; for (int y = 0; y < dpi->height; y++) { png_write_row(png_ptr, (png_byte *)bits); bits += stride; } // Finish png_write_end(png_ptr, nullptr); result = true; } catch (Exception) { } png_free(png_ptr, png_palette); png_destroy_write_struct(&png_ptr, (png_infopp)nullptr); return result; }
/* Saves an SDL surface to a png file. * TODO: support screen formats. */ int sdl_surface_savepng(SDL_Surface * surf, const char * filename) { png_bytep * rows = NULL; png_colorp pale = NULL; FILE * fout = NULL; png_structp png = NULL; png_infop info = NULL; int colortype; int i = 0; Uint8 * tran = NULL; SDL_PixelFormat * fmt = NULL; SDL_Surface * temp = NULL; if (!surf) { return save_fail("Surface was NULL."); } if (!filename) { return save_fail("Filename was NULL."); } rows = gymalloc(sizeof(png_bytep)*surf->h); if (!rows) { return save_fail("Out of memory."); } for (i = 0; i < surf->h; i++) { rows[i] = ((Uint8 *)surf->pixels) + i*surf->pitch; } /* Create palette and transparency table if needed. */ fmt = surf->format; if (fmt->palette) { pale = malloc(fmt->palette->ncolors * sizeof(png_color)); if (!pale) { savepng_done(rows, fout, png, info, temp, pale, tran); return save_fail("Out of memory in saving palette."); } for (i = 0; i < fmt->palette->ncolors; i++) { pale[i].red = fmt->palette->colors[i].r; pale[i].green = fmt->palette->colors[i].g; pale[i].blue = fmt->palette->colors[i].b; } if (surf->flags & SDL_SRCCOLORKEY) { tran = malloc(fmt->palette->ncolors * sizeof(Uint8)); if(!tran) { savepng_done(rows, fout, png, info, temp, pale, tran); return save_fail("Out of memory in saving palette transparency."); } for (i = 0; i < fmt->palette->ncolors; i++) { tran[i] = (((unsigned)i) == fmt->colorkey) ? 255 : 0; } } } fout = fopen(filename, "wb"); if(!fout) { savepng_done(rows, fout, png, info, temp, pale, tran); return save_fail("Couldn't open file for writing."); } png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!png) { savepng_done(rows, fout, png, info, temp, pale, tran); return save_fail("Couldn't create png_structp"); } info = png_create_info_struct(png); if (!info) { savepng_done(rows, fout, png, info, temp, pale, tran); return save_fail("Couldn't create png_infop"); } /* libpng rudely uses longjump to terminate on errors. This in turns causes warning of pissbli clobered variables. Could it suck more? */ if (setjmp(png->jmpbuf)) { /* This give a warning, but it seems to be unavoidable. */ savepng_done(rows, fout, png, info, temp, pale, tran); return save_fail("Error writing png file."); } /* Set file pointer. */ png_init_io(png, fout); colortype = sdl_surface_pngcolortype(surf); png_set_IHDR(png, info, surf->w, surf->h, 8, colortype, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); /* Write palette if needed. */ if(pale) { png_set_PLTE(png, info, pale, fmt->palette->ncolors); } /* Write transparency table if needed. */ if (tran) { png_set_tRNS(png, info, tran, fmt->palette->ncolors, NULL); } /* Write the image. */ png_write_info(png, info); png_set_packing(png); if(SDL_MUSTLOCK(surf)) { SDL_LockSurface(surf); } png_write_image(png, rows); if(SDL_MUSTLOCK(surf)) { SDL_UnlockSurface(surf); } png_write_end(png, info); /* Clean up. */ savepng_done(rows, fout, png, info, temp, pale, tran); return 0; }
int print_png(FILE *out, SDL_Surface *surf,int compression){ png_structp png_ptr; png_infop info_ptr; SDL_PixelFormat *fmt=NULL; SDL_Surface *tempsurf=NULL; int ret,funky_format,used_alpha; unsigned int i,temp_alpha; png_colorp palette; Uint8 *palette_alpha=NULL; png_byte **row_pointers=NULL; png_ptr=NULL;info_ptr=NULL;palette=NULL;ret=-1; funky_format=0; if(!surf) { goto savedone; /* Nothing to do. */ } row_pointers=(png_byte **)malloc(surf->h * sizeof(png_byte*)); if (!row_pointers) { SDL_SetError("Couldn't allocate memory for rowpointers"); goto savedone; } png_ptr=png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL,NULL,NULL); if (!png_ptr){ SDL_SetError("Couldn't allocate memory for PNG file"); goto savedone; } info_ptr= png_create_info_struct(png_ptr); if (!info_ptr){ SDL_SetError("Couldn't allocate image information for PNG file"); goto savedone; } /* setup custom writer functions */ png_set_write_fn(png_ptr,(voidp)out,png_write_data,NULL); if (setjmp(png_jmpbuf(png_ptr))){ SDL_SetError("Unknown error writing PNG"); goto savedone; } if(compression>Z_BEST_COMPRESSION) compression=Z_BEST_COMPRESSION; if(compression == Z_NO_COMPRESSION) // No compression { png_set_filter(png_ptr,0,PNG_FILTER_NONE); png_set_compression_level(png_ptr,Z_NO_COMPRESSION); } else if(compression<0) // Default compression png_set_compression_level(png_ptr,Z_DEFAULT_COMPRESSION); else png_set_compression_level(png_ptr,compression); fmt=surf->format; if(fmt->BitsPerPixel==8){ /* Paletted */ png_set_IHDR(png_ptr,info_ptr, surf->w,surf->h,8,PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE,PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); palette=(png_colorp) malloc(fmt->palette->ncolors * sizeof(png_color)); if (!palette) { SDL_SetError("Couldn't create memory for palette"); goto savedone; } for (i=0;i<fmt->palette->ncolors;i++) { palette[i].red=fmt->palette->colors[i].r; palette[i].green=fmt->palette->colors[i].g; palette[i].blue=fmt->palette->colors[i].b; } png_set_PLTE(png_ptr,info_ptr,palette,fmt->palette->ncolors); if (surf->flags&SDL_SRCCOLORKEY) { palette_alpha=(Uint8 *)malloc((fmt->colorkey+1)*sizeof(Uint8)); if (!palette_alpha) { SDL_SetError("Couldn't create memory for palette transparency"); goto savedone; } /* FIXME: memset? */ for (i=0;i<(fmt->colorkey+1);i++) { palette_alpha[i]=255; } palette_alpha[fmt->colorkey]=0; png_set_tRNS(png_ptr,info_ptr,palette_alpha,fmt->colorkey+1,NULL); } }else{ /* Truecolor */ if (fmt->Amask) { png_set_IHDR(png_ptr,info_ptr, surf->w,surf->h,8,PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE,PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); } else { png_set_IHDR(png_ptr,info_ptr, surf->w,surf->h,8,PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); } } png_write_info(png_ptr, info_ptr); if (fmt->BitsPerPixel==8) { /* Paletted */ for(i=0;i<surf->h;i++){ row_pointers[i]= ((png_byte*)surf->pixels) + i*surf->pitch; } if(SDL_MUSTLOCK(surf)){ SDL_LockSurface(surf); } png_write_image(png_ptr, row_pointers); if(SDL_MUSTLOCK(surf)){ SDL_UnlockSurface(surf); } }else{ /* Truecolor */ if(fmt->BytesPerPixel==3){ if(fmt->Amask){ /* check for 24 bit with alpha */ funky_format=1; }else{ /* Check for RGB/BGR/GBR/RBG/etc surfaces.*/ #if SDL_BYTEORDER == SDL_BIG_ENDIAN if(fmt->Rmask!=0xFF0000 || fmt->Gmask!=0x00FF00 || fmt->Bmask!=0x0000FF){ #else if(fmt->Rmask!=0x0000FF || fmt->Gmask!=0x00FF00 || fmt->Bmask!=0xFF0000){ #endif funky_format=1; } } }else if (fmt->BytesPerPixel==4){ if (!fmt->Amask) { /* check for 32bit but no alpha */ funky_format=1; }else{ /* Check for ARGB/ABGR/GBAR/RABG/etc surfaces.*/ #if SDL_BYTEORDER == SDL_BIG_ENDIAN if(fmt->Rmask!=0xFF000000 || fmt->Gmask!=0x00FF0000 || fmt->Bmask!=0x0000FF00 || fmt->Amask!=0x000000FF){ #else if(fmt->Rmask!=0x000000FF || fmt->Gmask!=0x0000FF00 || fmt->Bmask!=0x00FF0000 || fmt->Amask!=0xFF000000){ #endif funky_format=1; } } }else{ /* 555 or 565 16 bit color */ funky_format=1; } if (funky_format) { /* Allocate non-funky format, and copy pixeldata in*/ if(fmt->Amask){ #if SDL_BYTEORDER == SDL_BIG_ENDIAN tempsurf = SDL_CreateRGBSurface(SDL_SWSURFACE, surf->w, surf->h, 24, 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff); #else tempsurf = SDL_CreateRGBSurface(SDL_SWSURFACE, surf->w, surf->h, 24, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000); #endif }else{ #if SDL_BYTEORDER == SDL_BIG_ENDIAN tempsurf = SDL_CreateRGBSurface(SDL_SWSURFACE, surf->w, surf->h, 24, 0xff0000, 0x00ff00, 0x0000ff, 0x00000000); #else tempsurf = SDL_CreateRGBSurface(SDL_SWSURFACE, surf->w, surf->h, 24, 0x000000ff, 0x0000ff00, 0x00ff0000, 0x00000000); #endif } if(!tempsurf){ SDL_SetError("Couldn't allocate temp surface"); goto savedone; } if(surf->flags&SDL_SRCALPHA){ temp_alpha=fmt->alpha; used_alpha=1; SDL_SetAlpha(surf,0,255); /* Set for an opaque blit */ }else{ used_alpha=0; } if(SDL_BlitSurface(surf,NULL,tempsurf,NULL)!=0){ SDL_SetError("Couldn't blit surface to temp surface"); SDL_FreeSurface(tempsurf); goto savedone; } if (used_alpha) { SDL_SetAlpha(surf,SDL_SRCALPHA,(Uint8)temp_alpha); /* Restore alpha settings*/ } for(i=0;i<tempsurf->h;i++){ row_pointers[i]= ((png_byte*)tempsurf->pixels) + i*tempsurf->pitch; } if(SDL_MUSTLOCK(tempsurf)){ SDL_LockSurface(tempsurf); } png_write_image(png_ptr, row_pointers); if(SDL_MUSTLOCK(tempsurf)){ SDL_UnlockSurface(tempsurf); } SDL_FreeSurface(tempsurf); } else { for(i=0;i<surf->h;i++){ row_pointers[i]= ((png_byte*)surf->pixels) + i*surf->pitch; } if(SDL_MUSTLOCK(surf)){ SDL_LockSurface(surf); } png_write_image(png_ptr, row_pointers); if(SDL_MUSTLOCK(surf)){ SDL_UnlockSurface(surf); } } } png_write_end(png_ptr, NULL); ret=0; /* got here, so nothing went wrong. YAY! */ savedone: /* clean up and return */ png_destroy_write_struct(&png_ptr,&info_ptr); if (palette) { free(palette); } if (palette_alpha) { free(palette_alpha); } if (row_pointers) { free(row_pointers); } return ret; }