/******************************************************************************
 * program:     rasimg library                                                *
 * function:    Module for PNG support                                        *
 * modul:       ras_png.cc                                                    *
 * licency:     GPL or LGPL                                                   *
 ******************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "typedfs.h"
#include "raster.h"

#include "imgsupp.h"


#if defined(SupportPNG) && SupportPNG>0

#include <png.h>
#ifndef png_infopp_NULL
 #define png_infopp_NULL	NULL
#endif


#if (PNG_LIBPNG_VER_MAJOR>1) || (PNG_LIBPNG_VER_MAJOR==1 && PNG_LIBPNG_VER_MINOR>=5)
 #define USE_PNG1_5
#endif



#if SupportPNG>=4 || SupportPNG==2
Image LoadPicturePNG(const char *Name)
{
Image Img;
png_byte header[8];			// 8 is the maximum size that can be checked
Raster2DAbstract *Raster = NULL;
APalette *Palette = NULL;
FILE *f;
png_structp png_ptr = NULL;
png_infop info_ptr = NULL;
png_colorp palette;
int num_palette;
png_byte color_type;
int number_of_passes;
int i;

  if((f=fopen(Name,"rb"))==NULL) return(Img);

  fread(header, 1, 8, f);
  if(png_sig_cmp(header, 0, 8)) goto FINISH;

	/* initialize stuff */
  png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
	
  if(!png_ptr) goto FINISH;	// [read_png_file] png_create_read_struct failed

  info_ptr = png_create_info_struct(png_ptr);
  if(!info_ptr) goto FINISH;	// [read_png_file] png_create_info_struct failed

  if(setjmp(png_jmpbuf(png_ptr))) goto FINISH;	// [read_png_file] Error during init_io

  png_init_io(png_ptr, f);
  png_set_sig_bytes(png_ptr, 8);

  png_read_info(png_ptr, info_ptr);

  //color_type = info_ptr->color_type;
  color_type = png_get_color_type(png_ptr,info_ptr);

  number_of_passes = png_set_interlace_handling(png_ptr);
  png_read_update_info(png_ptr, info_ptr);

	/* read file */
  if(setjmp(png_jmpbuf(png_ptr))) goto FINISH;	  // [read_png_file] Error during read_image  

  switch(png_get_channels(png_ptr,info_ptr))
    {
    case 1: if(png_get_rowbytes(png_ptr,info_ptr) > (png_get_image_width(png_ptr,info_ptr)*png_get_bit_depth(png_ptr,info_ptr)+7)/8) goto FINISH;
	    Raster = CreateRaster2D(png_get_image_width(png_ptr,info_ptr),png_get_image_height(png_ptr,info_ptr),png_get_bit_depth(png_ptr,info_ptr));

            png_get_PLTE(png_ptr,info_ptr, &palette, &num_palette);
            if(num_palette>0 && palette!=NULL)
              {
              Palette = BuildPalette(num_palette,8);
	      if(Palette)
                {
                for(i=0;i<num_palette;i++)
		  {
		  Palette->R(i,palette[i].red);
		  Palette->G(i,palette[i].green);
		  Palette->B(i,palette[i].blue);
                  }
		}
              }
	    break;
    case 3: if(png_get_rowbytes(png_ptr,info_ptr) > (3*png_get_image_width(png_ptr,info_ptr)*png_get_bit_depth(png_ptr,info_ptr))/8) goto FINISH;
	    Raster = CreateRaster2DRGB(png_get_image_width(png_ptr,info_ptr),png_get_image_height(png_ptr,info_ptr),png_get_bit_depth(png_ptr,info_ptr));
	    break;
    case 4: if(png_get_rowbytes(png_ptr,info_ptr) > (4*png_get_image_width(png_ptr,info_ptr)*png_get_bit_depth(png_ptr,info_ptr))/8) goto FINISH;
	    Raster = CreateRaster2DRGBA(png_get_image_width(png_ptr,info_ptr),png_get_image_height(png_ptr,info_ptr),png_get_bit_depth(png_ptr,info_ptr));
	    break;	    
    }
  if(Raster==NULL) goto FINISH;

  png_read_image(png_ptr, (png_byte **)Raster->Data2D);


FINISH:
  fclose(f);
	/* Clean up after the read, and free any memory allocated - REQUIRED */
  if(png_ptr)
    png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);

  Img.AttachRaster(Raster);
  Img.AttachPalette(Palette);  

  return(Img);  /* And we're done! */
}
#endif


#if SupportPNG>=3

int SavePicturePNG(const char *Name, const Image &Img)
{
png_colorp palette = NULL;

  if(Img.Raster==NULL) return(-10);

	/* create file */
  FILE *fp = fopen(Name, "wb");
  if(!fp) return -1;
  //	abort_("[write_png_file] File %s could not be opened for writing", file_name);

  /* 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_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);

  if(png_ptr == NULL)
  {
     fclose(fp);
     return -2;
  }

	/* Allocate/initialize the image information data.  REQUIRED */
  png_infop info_ptr = png_create_info_struct(png_ptr);
  if(info_ptr == NULL)
  {
    fclose(fp);
    png_destroy_write_struct(&png_ptr,  png_infopp_NULL);
    return -3;
  }

   /* Set error handling.  REQUIRED if you aren't supplying your own
    * error handling functions in the png_create_write_struct() call. */
  if(setjmp(png_jmpbuf(png_ptr)))
  {	/* If we get here, we had a problem writing the file */
    fclose(fp);
    png_destroy_write_struct(&png_ptr, &info_ptr);
    return -4;
  }
  
	/* Set up the output control if you are using standard C streams */
  png_init_io(png_ptr, fp);

   /* Optional significant bit (sBIT) chunk */
  png_color_8 sig_bit;

   /* 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 */
  IMAGE_TYPE Ityp = Img.ImageType();
  switch(Ityp)
  {
    case ImageTrueColor:
              png_set_IHDR(png_ptr, info_ptr, Img.Raster->Size1D, Img.Raster->Size2D,
		     Img.Raster->GetPlanes()/3, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
		     PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
		   /* Otherwise, if we are dealing with a color image then */
              sig_bit.blue = 
                  sig_bit.green = 
	          sig_bit.red = Img.Raster->GetPlanes() / 3;
              png_set_sBIT(png_ptr, info_ptr, &sig_bit);
              break;
    case ImageGray:
              png_set_IHDR(png_ptr, info_ptr, Img.Raster->Size1D, Img.Raster->Size2D,
		     Img.Raster->GetPlanes(), PNG_COLOR_TYPE_GRAY, PNG_INTERLACE_NONE,
		     PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
			/* If we are dealing with a grayscale image then */
	      sig_bit.gray = Img.Raster->GetPlanes();
              png_set_sBIT(png_ptr, info_ptr, &sig_bit);
              break;
    case ImagePalette:
              png_set_IHDR(png_ptr, info_ptr, Img.Raster->Size1D, Img.Raster->Size2D,
		     Img.Raster->GetPlanes(), PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE,
		     PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
		   /* Set the palette if there is one.  REQUIRED for indexed-color images */
	      palette = (png_colorp)png_malloc(png_ptr, PNG_MAX_PALETTE_LENGTH*png_sizeof(png_color));
		   /* ... Set palette colors ... */
	      png_set_PLTE(png_ptr, info_ptr, palette, PNG_MAX_PALETTE_LENGTH);
		  /* 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;

    default:  fclose(fp);
              png_destroy_write_struct(&png_ptr, &info_ptr);              
              return -5;
  }

	/* Write the file header information.  REQUIRED */
  png_write_info(png_ptr, info_ptr);

	/* write bytes */
  if(setjmp(png_jmpbuf(png_ptr)))
  {
    //abort_("[write_png_file] Error during writing bytes");
    fclose(fp);
    png_destroy_write_struct(&png_ptr,  png_infopp_NULL);
    return -6;
  }

  png_write_image(png_ptr, (png_bytepp)Img.Raster->Data2D);

	/* end write */
  if(setjmp(png_jmpbuf(png_ptr)))
  {
    //	abort_("[write_png_file] Error during end of write");
    fclose(fp);
    png_destroy_write_struct(&png_ptr,  png_infopp_NULL);
    return -7;
  }

  png_write_end(png_ptr, NULL);

  fclose(fp);
  return 0;
}
#endif


#endif

