#include <stdio.h>
#include <setjmp.h>

#include <allegro.h>
#include "jpeglib.h"
#include "jpeg.h"



BITMAP *load_jpeg(char *filename, RGB *pal)
{
jpeg_decode_param jpeg;
jpeg.color_type=CT_RGB;
jpeg.scale_factor=1;
jpeg.decoding_speed=DS_SLOW;
jpeg.first_color=0;
jpeg.nbr_colors=256;
return load_jpeg_ex(filename, pal, &jpeg);
}


/* We provide a loader error manager, since */
/* we have to deal with corrupted files :<  */

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;

/*
 * Here's the routine that will replace the standard error_exit method:
 */

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;

#if 0
/* we do not print the error message since this is not safe */
/* when using a graphic mode */
/* if you want it anyway, modify #if 0 */
(*cinfo->err->output_message) (cinfo);
#endif
/* Return control to the setjmp point */
longjmp(myerr->setjmp_buffer, 1);
}


BITMAP *load_jpeg_ex(char *filename, RGB *pal,jpeg_decode_param *jpeg)
{
struct my_error_mgr jerr;
struct jpeg_decompress_struct cinfo;
PACKFILE *f;		/* source file */
unsigned char *ptr_in, *ptr_out;    /* pointers which goes thru the buffer
                              to convert 24bits -> nbits */
int i;      /* # of the line processed */
unsigned int j;      /* # number of the pixel processed */

unsigned char *buffer=NULL; /* temporary buffer 24 bits mode */
BITMAP *bmp=NULL;


if ((f=pack_fopen(filename,"rb"))==NULL)
   {
   return NULL;
   }

cinfo.err = jpeg_std_error(&jerr.pub);
jerr.pub.error_exit = my_error_exit;
/* 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 (bmp!=NULL)
        destroy_bitmap(bmp);
   jpeg_destroy_decompress(&cinfo);
   pack_fclose(f);
   if (buffer!=NULL)
        free(buffer);
   return NULL; 
   }

jpeg_create_decompress(&cinfo);
jpeg_stdio_src(&cinfo, f);
jpeg_read_header(&cinfo, TRUE);


/* let us force grayscale output, if the user asked for it */
if (jpeg->color_type==CT_GRAYSCALE)
   cinfo.out_color_space=JCS_GRAYSCALE;
else
/* but do not override the color options of the file */
    if (cinfo.out_color_space!=JCS_GRAYSCALE)
       cinfo.out_color_space = JCS_RGB;

cinfo.scale_num=1;
if (jpeg->scale_factor!=1)
   {
   switch (jpeg->scale_factor)
          {
          case 1:
          case 2:
          case 4:
          case 8:
               cinfo.scale_denom=jpeg->scale_factor;
               break;
          default:
                  cinfo.scale_denom=1;
                  break;
          }
   }

if (jpeg->decoding_speed==DS_FAST)
   {
   cinfo.dct_method=JDCT_FASTEST;
   cinfo.do_fancy_upsampling=FALSE;
   cinfo.do_block_smoothing=FALSE;
   }

if ((_color_depth==8) && (cinfo.out_color_space==JCS_RGB))
   {
   if ((jpeg->nbr_colors>256) || (jpeg->first_color>255) || (jpeg->first_color+jpeg->nbr_colors>256))
      {
      jpeg_destroy_decompress(&cinfo);
      pack_fclose(f);
      return NULL;
      }
   cinfo.quantize_colors = TRUE;
   cinfo.desired_number_of_colors=jpeg->nbr_colors;
   }


jpeg_start_decompress(&cinfo);

if ( (bmp= create_bitmap(cinfo.output_width,cinfo.output_height))==NULL)
   {
   jpeg_destroy_decompress(&cinfo);
   pack_fclose(f);
   return NULL;
   }

if (_color_depth!=8)
   {
   if ( (buffer=malloc(3*cinfo.output_width))==NULL)
      {
      destroy_bitmap(bmp);
      jpeg_destroy_decompress(&cinfo);
      pack_fclose(f);
      return NULL;
      }
   }



/* let us decode, according to the _color_depth */
i=0; /* number of the line which is processed != cinfo.output_scanline */
if ((cinfo.out_color_space==JCS_GRAYSCALE) || (cinfo.num_components==1))
   {
   switch (_color_depth)
          {
          case 8:
               while (cinfo.output_scanline < cinfo.output_height)
                  {
                  jpeg_read_scanlines(&cinfo,&(bmp->line[i]), 1);
                  i++;
                  }
               break;
          case 15:
          case 16:
               while (cinfo.output_scanline < cinfo.output_height)
                     {
                     jpeg_read_scanlines(&cinfo, &(buffer),1);
                     for (ptr_in=buffer,ptr_out=bmp->line[i],j=0;
                         j<cinfo.output_width;j++)
                         {
                         *((short *)ptr_out)= makecol(*ptr_in,*ptr_in,*ptr_in);
                         ptr_out+=2;
                         ptr_in++;
                         }
                     i++;
                     }
               break;
          case 24:
               while (cinfo.output_scanline < cinfo.output_height)
                  {

                  jpeg_read_scanlines(&cinfo,&(buffer), 1);

                  /* invert the r and the b value */
                  for (ptr_in=buffer,ptr_out=bmp->line[i],j=0;
                      j<cinfo.output_width;j++)
                      {
                      ptr_out[0]=*ptr_in;
                      ptr_out[1]=*ptr_in;
                      ptr_out[2]=*ptr_in;
                      ptr_out+=3;
                      ptr_in++;
                      }
                  i++;
                  }
               break;
          case 32:
            while (cinfo.output_scanline < cinfo.output_height)
                  {
                  jpeg_read_scanlines(&cinfo,&(buffer), 1);
                  /* invert the r and b value */
                  for (ptr_in=buffer,ptr_out=bmp->line[i],j=0;
                      j<cinfo.output_width;j++)
                      {
                      ptr_out[0]=*ptr_in;
                      ptr_out[1]=*ptr_in;
                      ptr_out[2]=*ptr_in;
                      ptr_out[3]=0;
                      ptr_out+=4;
                      ptr_in++;
                      }
                  i++;
                  }
          }
   }
else
    {
    switch (_color_depth)
       {
       case 8:
            while (cinfo.output_scanline < cinfo.output_height)
                  {
                  jpeg_read_scanlines(&cinfo,&(bmp->line[i]), 1);
                  i++;
                  }
            break;
       case 15:
            while (cinfo.output_scanline < cinfo.output_height)
                  {
                  jpeg_read_scanlines(&cinfo,&(buffer), 1);
                  for (ptr_in=buffer,ptr_out=bmp->line[i],j=0;
                      j<cinfo.output_width;j++)
                      {
                      *((short *)ptr_out)= makecol15(ptr_in[0],ptr_in[1],ptr_in[2]);
                      ptr_out+=2;
                      ptr_in+=3;
                      }
                  i++;
                  }
            break;
       case 16:
            while (cinfo.output_scanline < cinfo.output_height)
                  {
                  jpeg_read_scanlines(&cinfo,&(buffer), 1);
                  for (ptr_in=buffer,ptr_out=bmp->line[i],j=0;
                      j<cinfo.output_width;j++)
                      {
                      *((short *)ptr_out)=makecol16(ptr_in[0],ptr_in[1],ptr_in[2]);
                      ptr_out+=2;
                      ptr_in+=3;
                      }
                  i++;
                  }
            break;
       case 24:
            while (cinfo.output_scanline < cinfo.output_height)
                  {
                  jpeg_read_scanlines(&cinfo,&(buffer), 1);
                  /* invert the r and the b value */
                  for (ptr_in=buffer,ptr_out=bmp->line[i],j=0;
                      j<cinfo.output_width;j++)
                      {
                      ptr_out[0]=ptr_in[2];
                      ptr_out[1]=ptr_in[1];
                      ptr_out[2]=ptr_in[0];
                      ptr_out+=3;
                      ptr_in+=3;
                      }
                  i++;
                  }
            break;
       case 32:
            while (cinfo.output_scanline < cinfo.output_height)
                  {
                  jpeg_read_scanlines(&cinfo,&(buffer), 1);
                  for (ptr_in=buffer,ptr_out=bmp->line[i],j=0;
                      j<cinfo.output_width;j++)
                      {
                      ptr_out[0]=ptr_in[2];
                      ptr_out[1]=ptr_in[1];
                      ptr_out[2]=ptr_in[0];
                      ptr_out[3]=0;
                      ptr_out+=4;
                      ptr_in+=3;
                      }
                  i++;
                  }
            break;
       }
    }

/* Step 7: Finish decompression */
 
/* now get the palette */
if (_color_depth==8)
   {
   if (cinfo.out_color_space==JCS_RGB)
      {
      for (i=0,j=jpeg->first_color;i<(int)jpeg->nbr_colors;i++,j++)
          {
          pal[j].r=((int)cinfo.colormap[0][i]*63)/255;
          pal[j].g=((int)cinfo.colormap[1][i]*63)/255;
          pal[j].b=((int)cinfo.colormap[2][i]*63)/255;
          }
      /* now we have to change the colors inside the image */
      if (jpeg->first_color!=0)
         {
         for (i=0;i<bmp->h;i++)
             for (ptr_out=bmp->line[i],j=0;j<(unsigned int)bmp->w;j++,ptr_out++)
                 *ptr_out+=(int)jpeg->first_color;
         }
      }
   else
       {
       for (i=0;i<256;i++)
          {
          pal[i].r=pal[i].g=pal[i].b=(((int)i)*63)/255;
          }
       }
   }
else
    {
    free(buffer);
    }
jpeg_finish_decompress(&cinfo);

/* Step 8: Release JPEG decompression object */
/* This is an important step since it will release a good deal of memory. */
jpeg_destroy_decompress(&cinfo);

pack_fclose(f);

/* And we're done! */
return bmp;
}
