/* Copyright (c) 2010 MJ Rutter 
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License version 2
 * as published by the Free Software Foundation.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street,
 * Fifth Floor, Boston, MA  02110-1301, USA.
 */ 

#ifdef USE_LIBPNG
#include <stdlib.h>  /* malloc */
#include <png.h>
#include "bmp2eps.h"

void pngread(FILE *in, struct bitmap *image){
  int debug,depth,ncols,ntrans,png_colour_type,i,j;
  unsigned width,height,row_bytes;
  static unsigned char index[3];
  png_structp png_ptr;
  png_infop info_ptr;
  png_bytep *row_ptrs,trans_colours;
  png_color_16p trans_val;

  debug=image->flags&DEBUG_MASK;

  if (debug) fprintf(stderr,"PNG file detected\n");

  /* Initialise libpng data structures */

  png_ptr = png_create_read_struct
    (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
  if (!png_ptr){
    fprintf(stderr,"Memory allocation error in readpng\n");
    exit(1);
  }

  info_ptr = png_create_info_struct(png_ptr);
  if (!info_ptr){
    fprintf(stderr,"Memory allocation error in readpng\n");
    exit(1);
  }

  if (setjmp(png_jmpbuf(png_ptr))){
    fprintf(stderr,"Unexpected error in libpng. Exiting.\n");
    exit(1);
  }

  png_init_io(png_ptr, in);
  png_set_sig_bytes(png_ptr, 3); /* Three bytes consumed before this routine
                                    was called */

  /* Let libpng read header, and deal with outcome */

  png_read_info(png_ptr, info_ptr);

  width=png_get_image_width(png_ptr,info_ptr);
  height=png_get_image_height(png_ptr,info_ptr);
  depth=png_get_bit_depth(png_ptr,info_ptr);
  png_colour_type=png_get_color_type(png_ptr,info_ptr);

  if (debug) fprintf(stderr,"Size %dx%d\n",width,height);
  if (debug>1) fprintf(stderr,"Depth %d\n",depth);

  if (depth==16){
    if (debug) fprintf(stderr,"Reducing 16 bit per components to 8 bits\n");
    png_set_strip_16(png_ptr);
    depth=8;
  }

  if (png_colour_type & PNG_COLOR_MASK_ALPHA){
    if (debug>1) fprintf(stderr,"PNG uses transparency ");
    if (image->flags&NO_TRANS) {
      png_set_strip_alpha(png_ptr);
      if (debug>1) fprintf(stderr,"which we will ignore.\n");
    }
    else {
      png_color_16p backgndp;
      if (png_get_bKGD(png_ptr,info_ptr,&backgndp)){
        if (debug>1) fprintf(stderr,"and defines a background colour\n");
        png_set_background(png_ptr,backgndp,PNG_BACKGROUND_GAMMA_FILE,1,1.0);
      }
      else{
        png_color_16 background;
        if (debug>1) fprintf(stderr,"but no background colour defined\n");
  /* Set background to white */
        background.red=background.green=background.blue=background.gray=0xffff;
        png_set_background(png_ptr,&background,PNG_BACKGROUND_GAMMA_FILE,0,1.0);
      }
    }
  }

  if ((png_colour_type==PNG_COLOR_TYPE_GRAY)||
      (png_colour_type==PNG_COLOR_TYPE_GRAY_ALPHA)) image->colour=GREY;
  if ((png_colour_type==PNG_COLOR_TYPE_RGB)||
      (png_colour_type==PNG_COLOR_TYPE_RGB_ALPHA)) image->colour=RGB;

  if (png_colour_type==PNG_COLOR_TYPE_PALETTE){
    png_colorp palette;
    image->type=PALETTE;
    image->colour=PALETTE;
    i=png_get_PLTE(png_ptr,info_ptr,&palette,&ncols);
    if (!i){
      fprintf(stderr,"readpng: paletted data, but no palette found! Abort\n");
      exit(1);
    }
    image->pal_len=ncols;
    image->palette=malloc(ncols*3);
    if (!image->palette){fprintf(stderr,"Malloc error\n");exit(1);}
    for(i=0;i<ncols;i++){
      *(image->palette+3*i)=palette[i].red;
      *(image->palette+3*i+1)=palette[i].green;
      *(image->palette+3*i+2)=palette[i].blue;
    }
    if (debug>1) fprintf(stderr,"Palette of %d colours in PNG\n",ncols);
  }
  else image->type=BMP;

  /* Update png info to reflect any requested conversions
   * (e.g. 16 bit to 8 bit or Alpha to non-alpha) */

/*
 fprintf(stderr,"Debug: PNG has %d channels\n",png_get_channels(png_ptr,info_ptr));
*/

  png_read_update_info(png_ptr, info_ptr);

  /* Now rowbytes will reflect what we will get, not what we had originally */

  row_bytes=png_get_rowbytes(png_ptr, info_ptr);

  /* Allocate block of memory for uncompressed image */

  if (!(image->data=malloc(row_bytes*height))){
    fprintf(stderr,"Malloc error on raw in pngread\n");
    exit(1);
  }

  /* libpng requires a sepate pointer to each row of image */

  if (!(row_ptrs=malloc(height*sizeof(png_bytep)))){
    fprintf(stderr,"Malloc error in pngread\n");
    exit(1);
  }

  for(i=0;i<height;i++) row_ptrs[i]=image->data+row_bytes*i;

  /* get uncompressed image */

  png_read_image(png_ptr, row_ptrs);

  /* free everything we don't need */

  free(row_ptrs);

  png_read_end(png_ptr,NULL);

  if (image->dpi_x==-1){
    i=png_get_x_pixels_per_meter(png_ptr,info_ptr);
    j=png_get_y_pixels_per_meter(png_ptr,info_ptr);
    if ((i)&&(j)){
      image->dpi_x=i*0.0254; /* The _pixels_per_inch calls appear to */
      image->dpi_y=j*0.0254; /* be absent from some libpngs */
      if (debug>1) fprintf(stderr,"DPI: %dx%d\n",image->dpi_x,image->dpi_y);
    }
  }

  /* wonder about transparency */

  if (png_get_valid(png_ptr,info_ptr,PNG_INFO_tRNS)){
    fprintf(stderr,"PNG has transparency\n");
    png_get_tRNS(png_ptr, info_ptr, &trans_colours, &ntrans, &trans_val);
/*    if (ntrans==1){ */
      fprintf(stderr,"PNG has transparency\n");
      if (png_colour_type==PNG_COLOR_TYPE_PALETTE){
        /* We can cope with just one, fully transparent, entry */
        j=0;
        for(i=0;i<ntrans;i++)
          if(trans_colours[i]==0)j++;
          else if(trans_colours[i]!=255)j+=10;
        if(j!=1) fprintf(stderr,"Not a single fully transparent entry. Ignoring\n");
        else{
          for(i=0;i<ntrans&&trans_colours[i]==255;i++);
          image->trans=index;
          *image->trans=i;
        }
      }else{
        image->trans=index;
        *image->trans=trans_val->gray;
        if (image->colour==RGB){
          image->trans[0]=trans_val->red;
          image->trans[1]=trans_val->green;
          image->trans[2]=trans_val->blue;
        }
      }
 /*   } */
  }
  png_destroy_read_struct(&png_ptr, &info_ptr,(png_infopp)NULL);

  /* make sure we have all necessary info */

  image->data_len=row_bytes*height;
  image->height=height;
  image->width=width;
  image->depth=depth;
  if (image->colour==RGB) image->depth*=3;
  
}
#endif
