/************************************************************
**
** Application: Snapper
**
** Title:       c.jpegc
**
*************************************************************/

/*
*
* Copyright (c) 2017, David Pilling and Chris Johnson
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the copyright holder nor the names of their
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/

/* Include files */

#include "h.includes"

#include <setjmp.h>



/* from TaskLib */

#include "TaskLib:h.wimpt"
#include "TaskLib:h.flex"
#include "TaskLib:h.transform"
#include "TaskLib:h.fsx"
#include "TaskLib:h.task"
#include "TaskLib:h.poll"
#include "TaskLib:h.alloc"
#include "TaskLib:h.trans"
#include "TaskLib:h.etc"

#include "jpeg9d:jpeglib.h"

/* from application */

#include "h.constants"
#include "h.str"
#include "h.im"
#include "h.jpegc"
#include "h.config"
#include "h.convert"








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;


/************************************************************/

static void my_error_exit(j_common_ptr cinfo)
{
 os_error     * err;
 my_error_ptr   myerr;
 char           string[256];

 myerr=(my_error_ptr)cinfo->err;

 (*cinfo->err->format_message) (cinfo,string);

 err=generror(0,string);

 longjmp(myerr->setjmp_buffer,(int)err);
}


/************************************************************/

static void internalerror(j_common_ptr cinfo,os_error * err)
{
 my_error_ptr   myerr;

 myerr=(my_error_ptr)cinfo->err;

 longjmp(myerr->setjmp_buffer,(int)err);
}






/************************************************************/

static os_error * get_input_row(j_compress_ptr cinfo,JSAMPARRAY pixel_row,
                                                        filestr * loadfile)
{
 os_error * err;
 JSAMPROW    ptr;
 int       * idata;
 int         xpix;
 int         bpp;
 int         shift;
 int         word;
 int         mask;
 int         sbyte;
 int         x;
 char      * p;
 imagestr * saveimage;

  saveimage = ((loadfile->frames[0].xim)->sim[IM]);

 ptr=pixel_row[0];

 err=imfind1r(saveimage,cinfo->next_scanline,&idata);
 if(err) return(err);

 xpix=saveimage->xpix;
 bpp=saveimage->bpp;


 if(saveimage->bpp<=8)
 {
  word=0;   /* compiler */
  shift=32;

  if(bpp==1) mask=0x1;
  else
  if(bpp==2) mask=0x3;
  else
  if(bpp==4) mask=0xF;
  else       mask=0xFF;

  if(cinfo->input_components==1)
  {
   for(x=0;x<xpix;x++)
   {
    if(shift==32)
    {
     word=*idata++;
     shift=0;
    }
    sbyte=(word>>shift)&mask;
    shift+=bpp;

    sbyte=saveimage->ipal.word[sbyte];

    *ptr++=(JSAMPLE)((sbyte>>8)  & 0xFF); /* red */
   }
  }
  else
  {
   for(x=0;x<xpix;x++)
   {
    if(shift==32)
    {
     word=*idata++;
     shift=0;
    }
    sbyte=(word>>shift)&mask;
    shift+=bpp;

    sbyte=saveimage->ipal.word[sbyte];

    *ptr++=(JSAMPLE)((sbyte>>8)  & 0xFF); /* red */
    *ptr++=(JSAMPLE)((sbyte>>16) & 0xFF); /* green */
    *ptr++=(JSAMPLE)((sbyte>>24) & 0xFF); /* blue */
   }
  }
 }
 else
 if(saveimage->bpp==16)
 {
        switch (sp_info.type)
        {
          case 5:
            if (loadfile->frames[0].spcolorder == 0)
            {
              for(x=0;x<xpix;x++)
              {
               word=*idata++;

               *ptr++=(JSAMPLE)((word<<3)&0xF8); /* red */
               *ptr++=(JSAMPLE)((word>>2)&0xF8); /* green */
               *ptr++=(JSAMPLE)((word>>7)&0xF8); /* blue */

               x++;
               if(x>=xpix) break;

               *ptr++=(JSAMPLE)((word>>13)&0xF8); /* red */
               *ptr++=(JSAMPLE)((word>>18)&0xF8); /* green */
               *ptr++=(JSAMPLE)((word>>23)&0xF8); /* blue */
              }
            }
            else
            {
              for(x=0;x<xpix;x++)
              {
               word=*idata++;

               *ptr++=(JSAMPLE)((word>>7)&0xF8); /* red */
               *ptr++=(JSAMPLE)((word>>2)&0xF8); /* green */
               *ptr++=(JSAMPLE)((word<<3)&0xF8); /* blue */

               x++;
               if(x>=xpix) break;

               *ptr++=(JSAMPLE)((word>>23)&0xF8); /* red */
               *ptr++=(JSAMPLE)((word>>18)&0xF8); /* green */
               *ptr++=(JSAMPLE)((word>>13)&0xF8); /* blue */
              }
            }
            break;

          case 10:
            /* 64k colour mode */
            if (loadfile->frames[0].spcolorder == 0)
            {
              for(x=0;x<xpix;x++)
              {
               word=*idata++;

               *ptr++=(JSAMPLE)((word<<3)&0xF8); /* red */
               *ptr++=(JSAMPLE)((word>>3)&0xFC); /* green */
               *ptr++=(JSAMPLE)((word>>8)&0xF8); /* blue */

               x++;
               if(x>=xpix) break;

               *ptr++=(JSAMPLE)((word>>13)&0xF8); /* red */
               *ptr++=(JSAMPLE)((word>>19)&0xFC); /* green */
               *ptr++=(JSAMPLE)((word>>24)&0xF8); /* blue */
              }
            }
            else
            {
              for(x=0;x<xpix;x++)
              {
               word=*idata++;

               *ptr++=(JSAMPLE)((word>>8)&0xF8); /* red */
               *ptr++=(JSAMPLE)((word>>3)&0xFC); /* green */
               *ptr++=(JSAMPLE)((word<<3)&0xF8); /* blue */

               x++;
               if(x>=xpix) break;

               *ptr++=(JSAMPLE)((word>>24)&0xF8); /* red */
               *ptr++=(JSAMPLE)((word>>19)&0xFC); /* green */
               *ptr++=(JSAMPLE)((word>>13)&0xF8); /* blue */
              }
            }
            break;

          case 16:
            /* New colour mode - 4096 colours */
            /* assuming 16bits are 0000bbbbggggrrrr */
            if (loadfile->frames[0].spcolorder == 0)
            {
              for(x=0;x<xpix;x++)
              {
               word=*idata++;

               *ptr++=(JSAMPLE)((word<<4)&0xF0); /* red */
               *ptr++=(JSAMPLE)((word>>0)&0xF0); /* green */
               *ptr++=(JSAMPLE)((word>>4)&0xF0); /* blue */

               x++;
               if(x>=xpix) break;

               *ptr++=(JSAMPLE)((word>>12)&0xF0); /* red */
               *ptr++=(JSAMPLE)((word>>16)&0xF0); /* green */
               *ptr++=(JSAMPLE)((word>>20)&0xF0); /* blue */
              }
            }
            else
            {
              for(x=0;x<xpix;x++)
              {
               word=*idata++;

               *ptr++=(JSAMPLE)((word>>4)&0xF0); /* red */
               *ptr++=(JSAMPLE)((word>>0)&0xF0); /* green */
               *ptr++=(JSAMPLE)((word<<4)&0xF0); /* blue */

               x++;
               if(x>=xpix) break;

               *ptr++=(JSAMPLE)((word>>20)&0xF0); /* red */
               *ptr++=(JSAMPLE)((word>>16)&0xF0); /* green */
               *ptr++=(JSAMPLE)((word>>12)&0xF0); /* blue */
              }
            }
            break;
        }

 }
 else
 if(saveimage->bpp==24)
 {
  p=(char*)idata;

  if (loadfile->frames[0].spcolorder == 0)
  {
    for(x=0;x<xpix;x++)
    {
     *ptr++=(JSAMPLE)*p++; /* red */
     *ptr++=(JSAMPLE)*p++; /* green */
     *ptr++=(JSAMPLE)*p++; /* blue */
    }
  }
  else
  {
    /* need to swap r and b */
    for (x = 0; x < xpix; x++)
    {
     *ptr++=(JSAMPLE)*(p+2); /* red */
     *ptr++=(JSAMPLE)*(p+1); /* green */
     *ptr++=(JSAMPLE)*p; /* blue */
     p += 3;
    }
  }
 }
 else
 if(saveimage->bpp==32)
 {
  p=(char*)idata;

  if(saveimage->ipal.palclass==CL_CMYK)
  {
   for(x=0;x<xpix;x++)
   {
    *ptr++=(JSAMPLE)*p++;
    *ptr++=(JSAMPLE)*p++;
    *ptr++=(JSAMPLE)*p++;
    *ptr++=(JSAMPLE)*p++;
   }
  }
  else
  {
    if (loadfile->frames[0].spcolorder == 0)
    {
     for(x=0;x<xpix;x++)
     {
      *ptr++=(JSAMPLE)*p++; /* red */
      *ptr++=(JSAMPLE)*p++; /* green */
      *ptr++=(JSAMPLE)*p++; /* blue */
      p++;
     }
    }
    else
    {
    /* need to swap r and b */
    for (x = 0; x < xpix; x++)
    {
     *ptr++=(JSAMPLE)*(p+2); /* red */
     *ptr++=(JSAMPLE)*(p+1); /* green */
     *ptr++=(JSAMPLE)*p; /* blue */
     p += 4;
    }
    }
  }
 }

 return(err);
}












/************************************************************/

#define ABUFFSIZE 0x10000

static os_error * write_JPEG_file(char * filename, filestr * loadfile, int quality)
{
 os_error                    * err;
 struct jpeg_compress_struct   cinfo;
 struct my_error_mgr           jerr;
 FILE                        * outfile;
 JSAMPROW                      row_pointer[1];
 char                        * buffer1;
 imagestr * image;

 err=NULL;

 buffer1=NULL;

  image = ((loadfile->frames[0].xim)->sim[IM]);


 memset(row_pointer,0,sizeof(row_pointer));


 if((outfile=fopen(filename,"wb"))==NULL)
 {
  err=generror(0,"Can't open %s\n",filename);
  return(err);
 }


 if(salloc((flex_ptr)&buffer1,ABUFFSIZE)==NULL)
 {
  if(buffer1) setvbuf(outfile,buffer1,_IOFBF,ABUFFSIZE);
 }


 cinfo.err=jpeg_std_error(&jerr.pub);
 jerr.pub.error_exit=my_error_exit;


 if((err=(os_error*)setjmp(jerr.setjmp_buffer))!=NULL)
 {
  jpeg_destroy_compress(&cinfo);
  fclose(outfile);
  fs_delete(filename);
  sfree((flex_ptr)&buffer1);
  sfree((flex_ptr)&row_pointer[0]);
  return(err);
 }


 jpeg_create_compress(&cinfo);

 jpeg_stdio_dest(&cinfo, outfile);

 cinfo.image_width=image->xpix;
 cinfo.image_height=image->ypix;

 if(image->ipal.palclass==CL_CMYK)
 {
  cinfo.input_components=4;
  cinfo.in_color_space=JCS_CMYK;
  err=salloc((flex_ptr)&row_pointer[0],4*image->xpix);
 }
 else
 if(image->bpp>8 || !imgrey(image))
 {
  cinfo.input_components=3;          /* or 1 for grayscale */
  cinfo.in_color_space=JCS_RGB;      /* or CS_GRAYSCALE for grayscale */
  err=salloc((flex_ptr)&row_pointer[0],3*image->xpix);
 }
 else
 {
  cinfo.input_components=1;
  cinfo.in_color_space=JCS_GRAYSCALE;
  err=salloc((flex_ptr)&row_pointer[0],image->xpix);
 }

 if(err) internalerror((j_common_ptr)&cinfo,err);

 jpeg_set_defaults(&cinfo);

 cinfo.density_unit=1;
 cinfo.X_density=image->xdpi;
 cinfo.Y_density=image->ydpi;

 jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);

 jpeg_start_compress(&cinfo, TRUE);

 while(cinfo.next_scanline<cinfo.image_height)
 {
  err=get_input_row(&cinfo,row_pointer, loadfile);
  if(err) internalerror((j_common_ptr)&cinfo,err);
  jpeg_write_scanlines(&cinfo,row_pointer,1);
 }

 jpeg_finish_compress(&cinfo);

 fclose(outfile);

 sfree((flex_ptr)&buffer1);
 sfree((flex_ptr)&row_pointer[0]);

 jpeg_destroy_compress(&cinfo);

 fs_settype (filename, JPEG);

 return(err);
}




/************************************************************/

os_error * savejpeg (char *fname, filestr * loadfile, int quality )
{
////  imagestr * saveimage;

////  saveimage = ((loadfile->frames[0].xim)->sim[IM]);

  return (write_JPEG_file(fname, loadfile, quality));
}






/*************  End of c.jpegc  ***********************/

