/***********************************************************************/
/*                                                                     */
/* Project      : GRAV                                                 */
/* Current Rel. : 3.1                                                  */
/* Creator      : Michael Knigge                                       */
/* Creation Date: 02/17/95                                             */
/* Description  : Interface to the JPEGLIB V5.0 (thanx to IGJ!!!)      */
/*                                                                     */
/* Functions    : grav_read_jpg_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 "error.h"
#include "readjpg.h"
#include "jpeglib.h"


struct my_error_mgr {
   struct jpeg_error_mgr pub;    /* "public" fields      */
   jmp_buf setjmp_buffer;        /* for return to caller */
};
    
typedef struct my_error_mgr * my_error_ptr;


METHODDEF void
my_error_exit (j_common_ptr cinfo)
{
   /* 
    * cinfo->err really points to a my_error_mgr struct,
    * so coerce pointer 
    */
    my_error_ptr myerr = (my_error_ptr) cinfo->err;
    
    /* 
     * Return control to the setjmp point
     */
    longjmp(myerr->setjmp_buffer, 1);
}

/* 
 * Because we want no warning messages, we need this "dummy-function" here...
 */
METHODDEF void
grav_jpeg_warning(j_common_ptr cinfo)
{
}
              

char *grav_read_jpg_file(int *error_id, int flag, int index, DIRTAB dirtab[], GBM *gbm, GBMFT *gbmft, GBMRGB gbmrgb[])
{
   struct jpeg_decompress_struct cinfo;
   struct my_error_mgr jerr;
   long   bitmap_size;
   int    bitmap_width;
   int    row_stride;    
   int    local;
   int    tmp;

   unsigned char *bitmap;
   unsigned char *work_bitmap;
   unsigned char *temp_ptr;
   static char   *ft = "Joint Photographic Expert Group\0";
   FILE          *jpg_file;   
   JSAMPARRAY    buffer;      

   
   /*
    * this is the interface to the JPEGLIB v5.0 written by the IJG
    * (thanx for that GREAT Lib, guys!). because JPEGLIB can return
    * 24bit or 8bit data, gbm->bpp must be preset with 24 or 8....
    *
    * If the JPEG-Image is a greyscale-image, we force loading a
    * 8bpp image...
    *
    * first we have to open the file for reading only.
    */

   if ((jpg_file = fopen(dirtab[index].filename, "rb")) == NULL )
   {
      *error_id = GRAV_ERROR_OPEN;
      return(NULL);
   }

   /* 
    * Step 1: allocate and initialize JPEG decompression object.
    * We set up the normal JPEG error routines, then override error_exit. 
    */
  
   bitmap_size             = 0;
   cinfo.err               = jpeg_std_error(&jerr.pub);
   jerr.pub.error_exit     = my_error_exit;
   jerr.pub.output_message = grav_jpeg_warning;
        

   /* 
    * Establish the setjmp return context for my_error_exit to use.
    */
   
   if (setjmp(jerr.setjmp_buffer))
   {
   /* 
    * If we get here, the JPEG code has signaled an error.
    * We need to clean up the JPEG object, close the input file, and return.
    */
       if (bitmap_size != 0)
          free(bitmap);
       jpeg_destroy_decompress(&cinfo);
       fclose(jpg_file);
       *error_id = GRAV_ERROR_JPEGLIB;
       return(NULL);
   }

   /* 
    * Now we can initialize the JPEG decompression object. 
    */
   
   jpeg_create_decompress(&cinfo);
                                    
   /* 
    * Step 2: specify data source (eg, a file) 
    */
  
   jpeg_stdio_src(&cinfo, jpg_file);
  
   /* 
    * Step 3: read file parameters with jpeg_read_header()
    */
  
   (void) jpeg_read_header(&cinfo, TRUE);

   /* 
    * Step 4: set parameters for decompression 
    */
  
   /* In this example, we don't need to change any of the defaults set by
    * jpeg_read_header(), so we do nothing here.
    */

   /*
    * check if we need a 8bit bitmap returned or a 24bit bitmap...
    */
    
    if (gbm->bpp == 8)
    {
       cinfo.quantize_colors          = TRUE;
       cinfo.desired_number_of_colors = 256;
       cinfo.two_pass_quantize        = TRUE;
    }

   /*
    * check if the current JPEG is a grayscale-image
    */
   if(cinfo.jpeg_color_space == JCS_GRAYSCALE)
   {
      cinfo.out_color_space          = JCS_GRAYSCALE;
      cinfo.desired_number_of_colors = 256;
      cinfo.quantize_colors          = FALSE;
      cinfo.two_pass_quantize        = FALSE;
      gbm->bpp                       = 8;
      
      for(local=0; local<256; local++)
      {
         gbmrgb[local].r = local;
         gbmrgb[local].g = local;
         gbmrgb[local].b = local;
      }
   }
                    
   /* 
    * Step 5: Start decompressor
    */
  
   jpeg_start_decompress(&cinfo);

   /*
    * if we only want to get a fileinfo, we have to destroy the
    * JPEG decompression object and go back....
    */

   gbm->w   = cinfo.output_width;
   gbm->h   = cinfo.output_height;
  
   if (flag == READ_HEADER_ONLY)
   {
      jpeg_destroy_decompress(&cinfo);
      fclose(jpg_file);
      gbmft->long_name = ft;
      *error_id        = GRAV_NO_ERROR;
      return(NULL);
   }

  /*
   * store the palette in GBMRGB.....
   */
   if(cinfo.jpeg_color_space != JCS_GRAYSCALE)   
   {
      for (local=0; local<cinfo.actual_number_of_colors; local++)
      {
         gbmrgb[local].r = cinfo.colormap[0][local];
         gbmrgb[local].g = cinfo.colormap[1][local];
         gbmrgb[local].b = cinfo.colormap[2][local];            
      }
   }
   
   /* 
    * JSAMPLEs per row in output buffer
    */
   
   row_stride = cinfo.output_width * cinfo.output_components;
   
   /* 
    * Make a one-row-high sample array that will go away 
    * when done with image
    */
      
   buffer = (*cinfo.mem->alloc_sarray)
            ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);
                                          
    
   /*
    * calculate size of bitmap and allocate memory and initialize....
    */
    
   bitmap_size  = gbm->h * (((gbm->w * gbm->bpp + 31) / 32) * 4);

   if ((bitmap = malloc((int) bitmap_size)) == NULL)
   {
      jpeg_destroy_decompress(&cinfo);
      fclose(jpg_file);
      *error_id = GRAV_ERROR_ALLOC;
      return(NULL);
   }
                     
   memset(bitmap, 0, bitmap_size);

   /*
    * now we will do a loop until all scanlines of the JPEG-File 
    * are returned from JPGELIB....
    */
    
   bitmap_width = grav_get_bitmap_width(gbm);
   work_bitmap  = bitmap + bitmap_size - bitmap_width;

   while (cinfo.output_scanline < cinfo.output_height)
   {
      (void) jpeg_read_scanlines(&cinfo, &work_bitmap, 1);
      temp_ptr = work_bitmap;

      if (gbm->bpp == 24)
      {
         for(local=0; local<cinfo.output_width; local++)
         {
            tmp            = work_bitmap[0];
            work_bitmap[0] = work_bitmap[2];
            work_bitmap[2] = tmp;
            work_bitmap    = work_bitmap + 3;
         }
      }
      work_bitmap = temp_ptr - bitmap_width;
   }
      

   /*
    * now we have finished....
    */

   jpeg_finish_decompress(&cinfo);
   jpeg_destroy_decompress(&cinfo);

   fclose(jpg_file);
    
   *error_id = GRAV_NO_ERROR;
   return(bitmap);
}

              
