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

/*
*
* 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"

/* from TaskLib */

#include "Tasklib:h.wimpt"
#include "Tasklib:h.flex"
#include "Tasklib:h.transform"
#include "Tasklib:h.etc"
#include "Tasklib:h.bf"
#include "Tasklib:h.xmath"
#include "Tasklib:h.alloc"

#include "pnglib:h.png"

/* from application */

#include "xdeb.h"
#include "constants.h"
#include "h.str"
#include "h.config"
#include "h.convert"
#include "h.im"
#include "h.pngim"


static int interlace;




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

static void user_read_data(png_structp png_ptr, png_bytep data,
                                                        png_size_t length)
{
 buffer * bf = (buffer*)png_get_io_ptr(png_ptr);

 bf_read(bf, data, (int)length);
}



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

static void user_write_data(png_structp png_ptr, png_bytep data,
                                                         png_size_t length)
{
 buffer * bf = (buffer*)png_get_io_ptr(png_ptr);

 bf_write(bf, data, (int)length);
}



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

static void user_flush_data(png_structp png_ptr)
{
 USE(png_ptr);
}



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

static void user_error(png_structp png_ptr, png_const_charp error_msg)
{
 os_error * err;

// dprintf(0,"user_error %d",error_msg);

 err = generror(0x600, "PNG:%s", error_msg);
 longjmp(png_ptr->jmpbuf, (int)err);  /* return control to outer routine */
}




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

static void user_warning(png_structp png_ptr, png_const_charp warning_msg)
{

 USE(png_ptr);
 USE(warning_msg);
}




#define BIGBUFFSIZE 0x20000



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


os_error * savepng(filestr * file,int fn,char * name,int type)
{
 os_error  * err;
 png_struct  png_ptr;
 png_info    info_ptr;
 imagestr  * saveimage;
 int         x;
 int         y;
 int         pass;
 int         height;
 int       * idata;
 int         i;
 int         number_passes;
 int         bpp;
 int         xpix;
 char      * p;
 char      * q;
 char      * temp;
 int         wwidth;
 int         owidth;
 int         ch;
 buffer      bf;


 err=NULL;


 memset(&info_ptr,0,sizeof(png_info));
 temp=NULL;

 saveimage=((file->frames[fn].xim)->sim[IM]);
 bpp=saveimage->bpp;
 xpix=saveimage->xpix;
 wwidth=((bpp*xpix+31) & ~0x1F)>>3;
 owidth=(bpp==16)?(2*wwidth):wwidth;
 err=bf_open(name,'w',BIGBUFFSIZE,&bf);
 if(!err)
 {
  /* set error handling */

  png_set_error_fn(&png_ptr,NULL,user_error,user_warning);

  if((err=(os_error*)setjmp(png_ptr.jmpbuf))!=NULL)
  {
   sfree((flex_ptr)&temp);
   png_write_destroy(&png_ptr);
   if(info_ptr.palette) sfree((flex_ptr)&info_ptr.palette);
  }
  else
  {
   /* initialize the structures */

   png_info_init(&info_ptr);

   png_write_init(&png_ptr);

   png_set_write_fn(&png_ptr,(voidp)&bf,user_write_data,user_flush_data);


   /* set the file information here */

   info_ptr.width=saveimage->xpix;
   info_ptr.height=height=saveimage->ypix;
   info_ptr.bit_depth=saveimage->bpp;
   if(info_ptr.bit_depth>8) info_ptr.bit_depth=8;
   info_ptr.color_type=
                    saveimage->bpp>8?PNG_COLOR_TYPE_RGB:PNG_COLOR_TYPE_PALETTE;
   info_ptr.interlace_type=interlace;


   /* set the palette if there is one */

   if(saveimage->bpp<=8)
   {
    info_ptr.valid|=PNG_INFO_PLTE;
    err=salloc((flex_ptr)&info_ptr.palette, 256*sizeof(png_color));

    if(!err)
    {
     info_ptr.num_palette=saveimage->ipal.ncolours;

     for(i=0;i<saveimage->ipal.ncolours;i++)
     {
      info_ptr.palette[i].red=(saveimage->ipal.word[i]>>8) & 0xFF;
      info_ptr.palette[i].green=(saveimage->ipal.word[i]>>16) & 0xFF;
      info_ptr.palette[i].blue=(saveimage->ipal.word[i]>>24) & 0xFF;
     }
    }
   }


   info_ptr.valid|=PNG_INFO_pHYs;
   info_ptr.x_pixels_per_unit=scale(saveimage->xdpi,10000,254);
   info_ptr.y_pixels_per_unit=scale(saveimage->ydpi,10000,254);
   info_ptr.phys_unit_type=PNG_RESOLUTION_METER;

   /* write the file information */
   png_write_info(&png_ptr, &info_ptr);

   /* turn on interlace handling if you are not using png_write_image() */
   if(interlace) number_passes=png_set_interlace_handling(&png_ptr);
   else          number_passes=1;


   if(!err) err=salloc((flex_ptr)&temp,owidth);
   if(!err)
   {
    for(pass=0;pass<number_passes;pass++)
    {
     for(y=0;y<height;y++)
     {
      err=imfind1r(saveimage,y,&idata);
      if(err) break;

      if(bpp==32)
      {
       q=temp;

        if (file->frames[0].spcolorder == 0)
        {
         for(x=0;x<xpix;x++)
         {
          ch=*idata++;
          *q++=ch;
          *q++=ch>>8;
          *q++=ch>>16;
         }
        }
        else
        {
         for(x=0;x<xpix;x++)
         {
          ch=*idata++;
          *q++=ch>>16;
          *q++=ch>>8;
          *q++=ch;
         }
        }
      }
      else
      if(bpp==16)
      {
        switch (sp_info.type)
        {
          case 5:
            /* 32k colour mode */
            q=temp;
            if (file->frames[0].spcolorder == 0)
            {
              for(x=0;x<xpix;x++)
              {
               ch=*idata++;

               *q++=(ch<<3)&0xF8;
               *q++=(ch>>2)&0xF8;
               *q++=(ch>>7)&0xF8;

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

               *q++=(ch>>13)&0xF8;
               *q++=(ch>>18)&0xF8;
               *q++=(ch>>23)&0xF8;
              }
            }
            else
            {
              for(x=0;x<xpix;x++)
              {
               ch=*idata++;

               *q++=(ch>>7)&0xF8;
               *q++=(ch>>2)&0xF8;
               *q++=(ch<<3)&0xF8;

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

               *q++=(ch>>23)&0xF8;
               *q++=(ch>>18)&0xF8;
               *q++=(ch>>13)&0xF8;
              }
            }
            break;

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

               *q++=(ch<<3)&0xF8;
               *q++=(ch>>3)&0xFC;
               *q++=(ch>>8)&0xF8;

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

               *q++=(ch>>13)&0xF8;
               *q++=(ch>>19)&0xFC;
               *q++=(ch>>24)&0xF8;
              }
            }
            else
            {
              for(x=0;x<xpix;x++)
              {
               ch=*idata++;

               *q++=(ch>>8)&0xF8;
               *q++=(ch>>3)&0xFC;
               *q++=(ch<<3)&0xF8;

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

               *q++=(ch>>24)&0xF8;
               *q++=(ch>>19)&0xFC;
               *q++=(ch>>13)&0xF8;
              }
            }
            break;

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

               *q++=(ch<<4)&0xF0;
               *q++=(ch)&0xF0;
               *q++=(ch>>4)&0xF0;

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

               *q++=(ch>>12)&0xF0;
               *q++=(ch>>16)&0xF0;
               *q++=(ch>>20)&0xF0;
              }
            }
            else
            {
              for(x=0;x<xpix;x++)
              {
               ch=*idata++;

               *q++=(ch>>4)&0xF0;
               *q++=(ch)&0xF0;
               *q++=(ch<<4)&0xF0;

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

               *q++=(ch>>20)&0xF0;
               *q++=(ch>>16)&0xF0;
               *q++=(ch>>12)&0xF0;
              }
            }
            break;
        }
      }
      else
      if(bpp==8)
      {
       memcpy(temp,idata,wwidth);
      }
      else
      if(bpp==24)
      {
        if (file->frames[0].spcolorder == 0)
        {
          memcpy(temp,idata,wwidth);
        }
        else
        {
          q=temp;
          p=(char*)idata;
          for(x=0;x<xpix;x++)
          {
            *q++ = *(p+2);
            *q++ = *(p+1);
            *q++ = *(p+0);
            p += 3;
          }
        }
      }
      else
      if(bpp==1)
      {
       q=temp;
       p=(char*)idata;
       for(i=0;i<wwidth;i++) *q++=bitrev[*p++];
      }
      else
      if(bpp==2)
      {
       q=temp;
       p=(char*)idata;
       for(i=0;i<wwidth;i++) *q++=dbitrev[*p++];
      }
      else
      if(bpp==4)
      {
       q=temp;
       p=(char*)idata;
       for(i=0;i<wwidth;i++) *q++=nybrev[*p++];
      }

      png_write_rows(&png_ptr,(png_byte**)&temp,1);
     }
    }

    sfree((flex_ptr)&temp);
   }

   /* write the rest of the file */

   png_write_end(&png_ptr,&info_ptr);

   /* clean up after the write, and free any memory allocated */
   png_write_destroy(&png_ptr);

   /* if you malloced the palette, free it here */
   if(info_ptr.palette) sfree((flex_ptr)&info_ptr.palette);
  }
  bf_closec(&bf,err,name,type);
 }
 return(err);
}







/*************  End of c.pngim  ***********************/

