/***********************************************************************/
/*                                                                     */
/* Project      : GRAV                                                 */
/* Current Rel. : 3.1                                                  */
/* Creator      : Michael Knigge                                       */
/* Creation Date: 05/09/95                                             */
/* Description  : Reading PING-Images                                  */
/*                                                                     */
/* Functions    : grav_read_png_file()                                 */
/*                                                                     */
/***********************************************************************/

#include <stdio.h>
#include <fcntl.h>
#include <malloc.h>
#include <ncurses.h>
#include <setjmp.h>
#include "standard.h"
#include "gbm.h"
#include "defines.h"
#include "png.h"
#include "error.h"
#include "readpng.h"


char *grav_read_png_file(int *error_id, int flag, int index, DIRTAB dirtab[], GBM *gbm, GBMFT *gbmft, GBMRGB gbmrgb[])
{
   int           pass;
   int           number_passes;
   int           lines;
   int           bitmap_width;
   long          bitmap_size;

   char          *bitmap;
   char          *work_bitmap;
   static char   *ft = "Portable Network Graphics\0";  
   
   FILE          *png_file;

   png_color_16  default_background = {0, 0, 0, 0, 0};
   png_struct    *png_ptr;
   png_info      *info_ptr;


   /* 
    * Open the PING-Image
    */
   png_file = fopen(dirtab[index].filename, "rb");
   if (!png_file)
   {
      *error_id = GRAV_ERROR_OPEN;
      return(NULL);
   }

   /* 
    * Allocate the necessary structures 
    */
   bitmap_size = 0;
   png_ptr     = malloc(sizeof (png_struct));
   info_ptr    = malloc(sizeof (png_info));

   /* 
    * set error handling of PNGLIB (if the if() gets TRUE, we had a
    * problem reading the PING-Image
    */
   if (setjmp(png_ptr->jmpbuf))
   {
      if (bitmap_size != 0)
         free(bitmap);

      free(png_ptr);
      free(info_ptr);
      fclose(png_file);
      *error_id = GRAV_ERROR_PNGLIB;
      return(NULL);
   }

   /* 
    * Initialize the required structures 
    */
   png_read_init(png_ptr);
   png_info_init(info_ptr);

   /* 
    * set up the input control (read from file) and read file information
    */
   png_init_io(png_ptr, png_file);
   png_read_info(png_ptr, info_ptr);

   gbmft->long_name = ft;
   gbm->w           = info_ptr->width;
   gbm->h           = info_ptr->height;

   if (info_ptr->bit_depth == 16)
      gbm->bpp      = 24;
   else
      gbm->bpp      = info_ptr->bit_depth;
      
   if (flag == READ_HEADER_ONLY)
   {
      *error_id        = GRAV_NO_ERROR;
      free(png_ptr);
      free(info_ptr);
      fclose(png_file);
      return(NULL);
   }
   

   /*
    * Set the backgroundcolor if needed....
    */
   if (info_ptr->valid & PNG_INFO_bKGD)
      png_set_background(png_ptr, &info_ptr->background);
   else
      png_set_background(png_ptr, &default_background);


   /*
    * Tell pnglib to expand images with < 4 bits per pixel. We can't use
    * pnglib to expand images with 8 bpp to RGB because of a bug.
    * So i use my own funcion to do this later...
    */
   if (info_ptr->bit_depth < 4)
   {
      png_set_packing(png_ptr);
      gbm->bpp = 8;
   }

   
   /*
    * Tell pnglib to return BGR data instead of RGB because SVGALIB
    * expects BGR data...
    */
   if (info_ptr->color_type == PNG_COLOR_TYPE_RGB ||
       info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
   {
       png_set_bgr(png_ptr);
   }


   /* 
    * turn on interlace handling 
    */
   if (info_ptr->interlace_type)
      number_passes = png_set_interlace_handling(png_ptr);
   else
      number_passes = 1;
   
   
   /* 
    * allocate the memory to hold the image using the fields
    * of png_info. And we need to initialize the bitmap.
    */
   bitmap_width = grav_get_bitmap_width(gbm);
   bitmap_size  = gbm->h * (((gbm->w * gbm->bpp + 31) / 32) * 4);

   if ((bitmap = malloc((int) bitmap_size)) == NULL)
   {
      free(png_ptr);
      free(info_ptr);
      fclose(png_file);
      *error_id = GRAV_ERROR_ALLOC;
      return(NULL);
   }
                     
   memset(bitmap, 0, bitmap_size);

   /*
    * Now get the image row by row...
    */
   for (pass=0; pass<number_passes; pass++)
   {
      work_bitmap = bitmap + bitmap_size;
      
      for (lines=0; lines<info_ptr->height; lines++)
      {
         png_read_row(png_ptr, work_bitmap, NULL);
         work_bitmap = work_bitmap - bitmap_width;
      }
   }
 
   /*
    * store the palette in GBMRGB if needed
    */
   if (gbm->bpp != 24)
      grav_store_png_palette(info_ptr->num_palette, info_ptr->palette, gbmrgb);

   /* 
    * read the rest of the file, getting any additional chunks in info_ptr 
    */
   png_read_end(png_ptr, info_ptr);

   /* 
    * clean up after the read, and free any memory allocated 
    */
   png_read_destroy(png_ptr, info_ptr);

   /* 
    * free allocated memory for the structures 
    */
   free(png_ptr);
   free(info_ptr);

   /* 
    * close the PING-Image file 
    */
   fclose(png_file);
   *error_id = GRAV_NO_ERROR;

   return(bitmap);
}

int grav_store_png_palette(int max_pal, png_color work_palette[], GBMRGB gbmrgb[])
{
   int local;
   
   for(local=0; local<max_pal; local++)
   {
      gbmrgb[local].r = work_palette[local].red;
      gbmrgb[local].g = work_palette[local].green;
      gbmrgb[local].b = work_palette[local].blue;  
   }
   return(TRUE);
}
