Esempio n. 1
0
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;
}
Esempio n. 2
0
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);
    }
}
Esempio n. 4
0
/** 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;
}
Esempio n. 5
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);
}
Esempio n. 7
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);
}
Esempio n. 8
0
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;
}
Esempio n. 9
0
        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);
        }
Esempio n. 10
0
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;
}
Esempio n. 11
0
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)");
  }
}
Esempio n. 12
0
bool wxPNGHandler::SaveFile( wxImage *image, wxOutputStream& stream, bool verbose )
{
    wxPNGInfoStruct wxinfo;

    wxinfo.verbose = verbose;
    wxinfo.stream.out = &stream;

    png_structp png_ptr = png_create_write_struct
                          (
                            PNG_LIBPNG_VER_STRING,
                            NULL,
                            wx_PNG_error,
                            wx_PNG_warning
                          );
    if (!png_ptr)
    {
        if (verbose)
        {
           wxLogError(_("Couldn't save PNG image."));
        }
        return false;
    }

    png_infop info_ptr = png_create_info_struct(png_ptr);
    if (info_ptr == NULL)
    {
        png_destroy_write_struct( &png_ptr, (png_infopp)NULL );
        if (verbose)
        {
           wxLogError(_("Couldn't save PNG image."));
        }
        return false;
    }

    if (setjmp(wxinfo.jmpbuf))
    {
        png_destroy_write_struct( &png_ptr, (png_infopp)NULL );
        if (verbose)
        {
           wxLogError(_("Couldn't save PNG image."));
        }
        return false;
    }

    // NB: please see the comment near wxPNGInfoStruct declaration for
    //     explanation why this line is mandatory
    png_set_write_fn( png_ptr, &wxinfo, wx_PNG_stream_writer, NULL);

    const int iHeight = image->GetHeight();
    const int iWidth = image->GetWidth();

    const bool bHasPngFormatOption
        = image->HasOption(wxIMAGE_OPTION_PNG_FORMAT);

    int iColorType = bHasPngFormatOption
                            ? image->GetOptionInt(wxIMAGE_OPTION_PNG_FORMAT)
                            : wxPNG_TYPE_COLOUR;

    bool bHasAlpha = image->HasAlpha();
    bool bHasMask = image->HasMask();

    bool bUsePalette = iColorType == wxPNG_TYPE_PALETTE
#if wxUSE_PALETTE
        || (!bHasPngFormatOption && image->HasPalette() )
#endif
    ;

    png_color_8 mask = { 0, 0, 0, 0, 0 };

    if (bHasMask)
    {
        mask.red   = image->GetMaskRed();
        mask.green = image->GetMaskGreen();
        mask.blue  = image->GetMaskBlue();
    }

    PaletteMap palette;

    if (bUsePalette)
    {
        png_color png_rgb  [PNG_MAX_PALETTE_LENGTH];
        png_byte  png_trans[PNG_MAX_PALETTE_LENGTH];

        const unsigned char *pColors = image->GetData();
        const unsigned char* pAlpha  = image->GetAlpha();

        if (bHasMask && !pAlpha)
        {
            // Mask must be first
            PaletteAdd(&palette, mask);
        }

        for (int y = 0; y < iHeight; y++)
        {
            for (int x = 0; x < iWidth; x++)
            {
                png_color_8 rgba;

                rgba.red   = *pColors++;
                rgba.green = *pColors++;
                rgba.blue  = *pColors++;
                rgba.gray  = 0;
                rgba.alpha = (pAlpha && !bHasMask) ? *pAlpha++ : 0;

                // save in our palette
                long index = PaletteAdd(&palette, rgba);

                if (index < PNG_MAX_PALETTE_LENGTH)
                {
                    // save in libpng's palette
                    png_rgb[index].red   = rgba.red;
                    png_rgb[index].green = rgba.green;
                    png_rgb[index].blue  = rgba.blue;
                    png_trans[index]     = rgba.alpha;
                }
                else
                {
                    bUsePalette = false;
                    break;
                }
            }
        }

        if (bUsePalette)
        {
            png_set_PLTE(png_ptr, info_ptr, png_rgb, palette.size());

            if (bHasMask && !pAlpha)
            {
                wxASSERT(PaletteFind(palette, mask) == 0);
                png_trans[0] = 0;
                png_set_tRNS(png_ptr, info_ptr, png_trans, 1, NULL);
            }
            else if (pAlpha && !bHasMask)
            {
                png_set_tRNS(png_ptr, info_ptr, png_trans, palette.size(), NULL);
            }
        }
    }

    /*
    If saving palettised was requested but it was decided we can't use a
    palette then reset the colour type to RGB.
    */
    if (!bUsePalette && iColorType == wxPNG_TYPE_PALETTE)
    {
        iColorType = wxPNG_TYPE_COLOUR;
    }

    bool bUseAlpha = !bUsePalette && (bHasAlpha || bHasMask);

    int iPngColorType;

    if (bUsePalette)
    {
        iPngColorType = PNG_COLOR_TYPE_PALETTE;
        iColorType = wxPNG_TYPE_PALETTE;
    }
    else if ( iColorType==wxPNG_TYPE_COLOUR )
    {
        iPngColorType = bUseAlpha ? PNG_COLOR_TYPE_RGB_ALPHA
                                  : PNG_COLOR_TYPE_RGB;
    }
    else
    {
        iPngColorType = bUseAlpha ? PNG_COLOR_TYPE_GRAY_ALPHA
                                  : PNG_COLOR_TYPE_GRAY;
    }

    if (image->HasOption(wxIMAGE_OPTION_PNG_FILTER))
        png_set_filter( png_ptr, PNG_FILTER_TYPE_BASE, image->GetOptionInt(wxIMAGE_OPTION_PNG_FILTER) );

    if (image->HasOption(wxIMAGE_OPTION_PNG_COMPRESSION_LEVEL))
        png_set_compression_level( png_ptr, image->GetOptionInt(wxIMAGE_OPTION_PNG_COMPRESSION_LEVEL) );

    if (image->HasOption(wxIMAGE_OPTION_PNG_COMPRESSION_MEM_LEVEL))
        png_set_compression_mem_level( png_ptr, image->GetOptionInt(wxIMAGE_OPTION_PNG_COMPRESSION_MEM_LEVEL) );

    if (image->HasOption(wxIMAGE_OPTION_PNG_COMPRESSION_STRATEGY))
        png_set_compression_strategy( png_ptr, image->GetOptionInt(wxIMAGE_OPTION_PNG_COMPRESSION_STRATEGY) );

    if (image->HasOption(wxIMAGE_OPTION_PNG_COMPRESSION_BUFFER_SIZE))
        png_set_compression_buffer_size( png_ptr, image->GetOptionInt(wxIMAGE_OPTION_PNG_COMPRESSION_BUFFER_SIZE) );

    int iBitDepth = !bUsePalette && image->HasOption(wxIMAGE_OPTION_PNG_BITDEPTH)
                            ? image->GetOptionInt(wxIMAGE_OPTION_PNG_BITDEPTH)
                            : 8;

    png_set_IHDR( png_ptr, info_ptr, image->GetWidth(), image->GetHeight(),
                  iBitDepth, iPngColorType,
                  PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
                  PNG_FILTER_TYPE_BASE);

    int iElements;
    png_color_8 sig_bit;

    if ( iPngColorType & PNG_COLOR_MASK_COLOR )
    {
        sig_bit.red =
        sig_bit.green =
        sig_bit.blue = (png_byte)iBitDepth;
        iElements = 3;
    }
    else // grey
    {
        sig_bit.gray = (png_byte)iBitDepth;
        iElements = 1;
    }

    if ( bUseAlpha )
    {
        sig_bit.alpha = (png_byte)iBitDepth;
        iElements++;
    }

    if ( iBitDepth == 16 )
        iElements *= 2;

    // save the image resolution if we have it
    int resX, resY;
    switch ( GetResolutionFromOptions(*image, &resX, &resY) )
    {
        case wxIMAGE_RESOLUTION_INCHES:
            {
                const double INCHES_IN_METER = 10000.0 / 254;
                resX = int(resX * INCHES_IN_METER);
                resY = int(resY * INCHES_IN_METER);
            }
            break;

        case wxIMAGE_RESOLUTION_CM:
            resX *= 100;
            resY *= 100;
            break;

        case wxIMAGE_RESOLUTION_NONE:
            break;

        default:
            wxFAIL_MSG( wxT("unsupported image resolution units") );
    }

    if ( resX && resY )
        png_set_pHYs( png_ptr, info_ptr, resX, resY, PNG_RESOLUTION_METER );

    png_set_sBIT( png_ptr, info_ptr, &sig_bit );
    png_write_info( png_ptr, info_ptr );
    png_set_shift( png_ptr, &sig_bit );
    png_set_packing( png_ptr );

    unsigned char *
        data = (unsigned char *)malloc( image->GetWidth() * iElements );
    if ( !data )
    {
        png_destroy_write_struct( &png_ptr, (png_infopp)NULL );
        return false;
    }

    const unsigned char *
        pAlpha = (const unsigned char *)(bHasAlpha ? image->GetAlpha() : NULL);

    const unsigned char *pColors = image->GetData();

    for (int y = 0; y != iHeight; ++y)
    {
        unsigned char *pData = data;
        for (int x = 0; x != iWidth; x++)
        {
            png_color_8 clr;
            clr.red   = *pColors++;
            clr.green = *pColors++;
            clr.blue  = *pColors++;
            clr.gray  = 0;
            clr.alpha = (bUsePalette && pAlpha) ? *pAlpha++ : 0; // use with wxPNG_TYPE_PALETTE only

            switch ( iColorType )
            {
                default:
                    wxFAIL_MSG( wxT("unknown wxPNG_TYPE_XXX") );
                    // fall through

                case wxPNG_TYPE_COLOUR:
                    *pData++ = clr.red;
                    if ( iBitDepth == 16 )
                        *pData++ = 0;
                    *pData++ = clr.green;
                    if ( iBitDepth == 16 )
                        *pData++ = 0;
                    *pData++ = clr.blue;
                    if ( iBitDepth == 16 )
                        *pData++ = 0;
                    break;

                case wxPNG_TYPE_GREY:
                    {
                        // where do these coefficients come from? maybe we
                        // should have image options for them as well?
                        unsigned uiColor =
                            (unsigned) (76.544*(unsigned)clr.red +
                                        150.272*(unsigned)clr.green +
                                        36.864*(unsigned)clr.blue);

                        *pData++ = (unsigned char)((uiColor >> 8) & 0xFF);
                        if ( iBitDepth == 16 )
                            *pData++ = (unsigned char)(uiColor & 0xFF);
                    }
                    break;

                case wxPNG_TYPE_GREY_RED:
                    *pData++ = clr.red;
                    if ( iBitDepth == 16 )
                        *pData++ = 0;
                    break;

                case wxPNG_TYPE_PALETTE:
                    *pData++ = (unsigned char) PaletteFind(palette, clr);
                    break;
            }

            if ( bUseAlpha )
            {
                unsigned char uchAlpha = 255;
                if ( bHasAlpha )
                    uchAlpha = *pAlpha++;

                if ( bHasMask )
                {
                    if ( (clr.red == mask.red)
                            && (clr.green == mask.green)
                                && (clr.blue == mask.blue) )
                        uchAlpha = 0;
                }

                *pData++ = uchAlpha;
                if ( iBitDepth == 16 )
                    *pData++ = 0;
            }
        }

        png_bytep row_ptr = data;
        png_write_rows( png_ptr, &row_ptr, 1 );
    }

    free(data);
    png_write_end( png_ptr, info_ptr );
    png_destroy_write_struct( &png_ptr, (png_infopp)&info_ptr );

    return true;
}
/*
 * 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 */
}
Esempio n. 14
0
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);
}
Esempio n. 15
0
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;
}
Esempio n. 16
0
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;
}
Esempio n. 17
0
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 {
Esempio n. 18
0
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);
	}
Esempio n. 19
0
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;
}
Esempio n. 20
0
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, &params ) ) {
			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;
	}
Esempio n. 21
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;
}
Esempio n. 22
0
/*
 * 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 */
}
Esempio n. 23
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.
 */
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;
}
Esempio n. 24
0
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;
}
Esempio n. 25
0
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;
}
Esempio n. 27
0
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);
}
Esempio n. 28
0
    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;
    }
Esempio n. 29
0
File: save.c Progetto: beoran/eruta
/* 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;
}
Esempio n. 30
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;
}