/*
Copyright (c) 2000-2010, Dirk Krause
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 
  opyright 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 Dirk Krause nor the names of
  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 OWNER OR CONTRIBUTORS 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.
*/



/**	@file	dkbiftif.c	TIFF module for dkbif API.
*/



/**	Inside the dkbiftif module.
*/
#define DKBIFTIF_C	1

#include "dkbifi.h"
#include "dkbif.h"




#line 54 "dkbiftif.ctr"




#if DK_HAVE_TIFF_H



/**	Initialize TIFF frame.
	@param	p	Image.
	@param	f	Frame.
*/
static
void
init_tif_frame DK_P2(dk_bif_t *,p, dk_bif_frame_t *,f)
{
  f->w = 0UL;
  f->h = 0UL;
  f->ch = 4;
  f->bpc = 8;
  f->vmask = 255;
  f->vmax = 255.0;
  f->xdpi = -1.0;
  f->ydpi = -1.0;
  f->mr = 1.0;
  f->mg = 1.0;
  f->mb = 1.0;
  (f->d).tif.w = 0;
  (f->d).tif.h = 0;
  (f->d).tif.bpc = 8;
  (f->d).tif.spp = 4;
  (f->d).tif.raster = NULL;
  (f->d).tif.xres = -1.0;
  (f->d).tif.yres = -1.0;
  (f->d).tif.rescm = 0x00;
}



/**	Transfer file contents.
	@param	fn	Destination file name.
	@param	fin	Input file.
	@return	1 on success, 0 on error.
*/
static
int
transfer_file_contents DK_P2(char *,fn, FILE *,fin)
{
  int back = 0, cc = 1;
  FILE *fout;
  char buffer[512];
  size_t br, bu;
  
  fout = dksf_fopen(fn, "wb");
  if(fout) {
    cc = 1; back = 1;
    while(cc) {
      br = fread((void *)buffer, 1, sizeof(buffer), fin);
      if(br > 0) {
        bu = fwrite((void *)buffer, 1, br, fout);
	if(bu != br) {
	  /* Problem */
	  back = 0;
	}
      } else {
        cc = 0;
      }
    }
    fclose(fout); fout = NULL;
  }
  
  return back;
}



/**	Bit patterns for test of significant bits.
*/
static unsigned char bit_masks[] = {
  0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80
};



/**	Find insignificant bits in uc.
	@param	uc	Byte to analyze.
	@return	Number of insignificant bits in \a uc.
*/
#if VERSION_BEFORE_20100626
static
unsigned short
insignificant_bits DK_P1(unsigned char, uc)
{
  int i;
  unsigned short back = 7;
  if(uc) {
    back= 0;
    for(i = 0; i < 8; i++) {
      if(uc & bit_masks[i]) {
        back++;
      } else {
        i = 8;
      }
    }
  }
  if(back > 7) { back = 7; }
  return back;
}
#else
static
unsigned short
insignificant_bits DK_P1(unsigned char, uc)
{
  int i;
  unsigned short back = 7;
  if(uc) {
    for(i = 0; i < 8; i++) {
      if(uc & bit_masks[i]) {
        back = 7 - i;   
      }
    }
  }
  if(back > 7) { back = 7; }
  return back;
}
#endif


/**	Analyze TIFF whether it is gray or color.
	@param	fr	Frame to anaylze.
*/
static
void tif_analyze DK_P1(dk_bif_frame_t *,fr)
{
  unsigned char used_flags[256], alpha_used, is_gray, have_insig_bits;
  unsigned short insig_bits, ni;
  uint32 *uiptr;
  uint32 ui, r, g, b, alpha, x, y;
  
  alpha_used = 0; is_gray = 1;
  uiptr = (fr->d).tif.raster;
  for(x = 0; x < 256; x++) { used_flags[x] = (unsigned char)0x00; }
  for(y = 0; y < (fr->d).tif.h; y++) {
    for(x = 0; x < (fr->d).tif.w; x++) {
      ui = *(uiptr++);
      alpha = ((ui >> 24) & 0x000000FFUL);
      b     = ((ui >> 16) & 0x000000FFUL);
      g     = ((ui >>  8) & 0x000000FFUL);
      r     = (ui & 0x000000FFUL);
      if(r != g) {
        is_gray = 0;
      } else {
        if(r != b) is_gray = 0;
      }
      if(alpha < 255UL) { alpha_used = 1; }
      used_flags[r] = 0x01;
      used_flags[g] = 0x01;
      used_flags[b] = 0x01;
      used_flags[alpha] = 0x01;
    }
  }
  insig_bits = 0; have_insig_bits = 0x00;
  for(x = 0; x < 256; x++) {
    if(used_flags[x]) {
      ni = insignificant_bits((unsigned char)x);
      if(have_insig_bits) {
        if(ni < insig_bits) insig_bits = ni;
      } else {
        insig_bits = ni; have_insig_bits = 0x01;
      }
    }
  }
  if(is_gray) {
    if(alpha_used) {
      (fr->d).tif.spp = 2;	
    } else {
      (fr->d).tif.spp = 1;	
    }
  } else {
    if(alpha_used) {
      (fr->d).tif.spp = 4;	
    } else {
      (fr->d).tif.spp = 3;	
    }
  }
#if VERSION_BEFORE_20100626
  /*	Zweifel, ob die bits per pixel so einfach umgesetzt
  	werden koennen.
	Wenn die Maximalwerte RGB z.B. 0x0F, 0x0F, 0x0F (dunkles Grau)
	sind und man verringert bpc auf 4, so entspricht dieser Wert
	auf einmal Weiss.
  */
  (fr->d).tif.bpc = 8 - insig_bits;
#endif
  
}



int
dkbiftif_header DK_P2(dk_bif_t *,p, FILE *,f)
{
  int back = 0; float testresval;
  unsigned long next_frame;
  uint16 testresunit;
  dk_bif_frame_t *fr;
  double dpi_epsilon = DPI_EPSILON;
  
  if((p) && (f)) {
    (p->d).tif.tiff = NULL;
    if(transfer_file_contents(p->tmpfilename, f)) {
      (p->d).tif.tiff = TIFFOpen(p->tmpfilename, "r");
      if((p->d).tif.tiff) {
        back = 1; next_frame = 0UL;
	do {
	  uint32 w, h; uint16 cps, spp;
	  w = h = 0; cps = spp = 0;
	  fr = dkbif_frame_new(p, next_frame++);
	  if(fr) {
	    
	    p->nof = next_frame;
	    init_tif_frame(p, fr);
	    if(TIFFGetField((p->d).tif.tiff, TIFFTAG_IMAGEWIDTH, &w) == 1) {
	      (fr->d).tif.w = w;
	      if(TIFFGetField((p->d).tif.tiff, TIFFTAG_IMAGELENGTH, &h) == 1) {
	        (fr->d).tif.h = h;
		testresval = 0.0;
		if(TIFFGetField((p->d).tif.tiff, TIFFTAG_XRESOLUTION, &testresval) == 1) {
		  (fr->d).tif.xres = testresval;
		}
		testresval = 0.0;
		if(TIFFGetField((p->d).tif.tiff, TIFFTAG_YRESOLUTION, &testresval) == 1) {
		  (fr->d).tif.yres = testresval;
		}
		testresunit = 0;
		if(TIFFGetField((p->d).tif.tiff,TIFFTAG_RESOLUTIONUNIT,&testresunit) == 1){
		  if(testresunit == RESUNIT_CENTIMETER) {
		    (fr->d).tif.xres = 2.54 * (fr->d).tif.xres;
		    (fr->d).tif.yres = 2.54 * (fr->d).tif.yres;
		    (fr->d).tif.rescm = 0x01;
		  } else {
		    if(testresunit == RESUNIT_NONE) {
		      (fr->d).tif.xres = (fr->d).tif.yres = -1.0;
		    }
		  }
		}
		if(!(p->whs)) {
		  int me = 0;
		  unsigned long image_size;
		  size_t is;
		  image_size = dkma_mul_ulong_ok(
		    dkma_mul_ulong_ok(
		      (unsigned long)w,
		      (unsigned long)h,
		      &me
		    ),
		    sizeof(uint32),
		    &me
		  );
		  is = (size_t)image_size;
		  if((unsigned long)is == image_size) {
		    if(me) {
		      back = 0; /* image too large */
		    } else {
		      (fr->d).tif.raster = (uint32 *)_TIFFmalloc(is);
		      if((fr->d).tif.raster) {
		        if(
			  TIFFReadRGBAImage(
			    (p->d).tif.tiff, w, h, (fr->d).tif.raster, 0
			  )
			)
			{
			  /* always analyze image data (-> bpc, ch) */
			  tif_analyze(fr);
			} else {
			  back = 0; /* error while reading */
			}

		      } else {
		        back = 0; /* memory */
		      }
		    }
		  } else {
		    back = 0; /* image too large */
		  }
		}
		fr->w = (unsigned long)((fr->d).tif.w);
		fr->h = (unsigned long)((fr->d).tif.h);
		fr->ch = (int)((fr->d).tif.spp);
		fr->bpc = (unsigned short)((fr->d).tif.bpc);
		fr->vmask = dkbif_max_for_bpc(fr->bpc);
		fr->vmax = dkma_ul_to_double(fr->vmask);
		if((fr->d).tif.xres > 0.0) {
		  fr->xdpi = (fr->d).tif.xres;
		  if((fr->d).tif.yres > 0.0) {
		    fr->ydpi = (fr->d).tif.yres;
		  } else {
		    fr->ydpi = (fr->d).tif.xres;
		  }
		} else {
		  if((fr->d).tif.yres > 0.0) {
		    fr->xdpi = fr->ydpi = (fr->d).tif.yres;
		  }
	        }
		if((fr->d).tif.rescm) {
		  dpi_epsilon = 1.67;
		}
                if(fabs(fr->xdpi - 72.0) < DPI_EPSILON) {
		  if(fabs(fr->ydpi - 72.0) < DPI_EPSILON) {
		    fr->xdpi = fr->ydpi = -1.0;
		  }
		}
	      } else {
	        back = 0; /* ERROR: height not found */
	      }
	    } else {
	      back = 0;	/* ERROR: width not found */
	    }
	  } else {
	    back = 0;	/* ERROR: Memory */
	    p->ec = DK_ERR_NOMEM;
	  }
	} while(TIFFReadDirectory((p->d).tif.tiff));
	
      } else {
        /* Failed to read file */
	p->ec = DKBIF_ERR_FAILED_TO_PROCESS_INPUT;
      }
    } else {
      /* failed to create temporary file */
      p->ec = DKBIF_ERR_FAILED_TO_CREATE_TEMPORARY_FILE;
    }
  }
  
  return back;
}



int
dkbiftif_data DK_P2(dk_bif_t *,p, FILE *,f)
{
  int back = 0;
  
  if((p) && (f)) {
    back = 1;
  }
  
  return back;
}



/**	Get one component from a TIFF image.
	@param	p	Image.
	@param	x	X position.
	@param	y	Y position.
	@param	cr	0=red,1=green,2=blue,3=alpha
*/
static
unsigned short get_component_at DK_P4(\
  dk_bif_t *,p, unsigned long,x, unsigned long,y, int,cr\
)
{
  int me = 0;
  uint32 value, r, g, b;
  unsigned short back = 0;
  unsigned long ypos;
  
  if(p) {				
    if(p->cf) {				
      if(((p->cf)->d).tif.raster) {	
        if(x < ((p->cf)->d).tif.w) {	
	  if(y < ((p->cf)->d).tif.h) {	
	    ypos = ((p->cf)->d).tif.h - y - 1UL;
	    value = (((p->cf)->d).tif.raster)[((p->cf)->d).tif.w * ypos + x];
	    
	    switch(cr) {
	      case 4: {
	        switch((p->cf)->ch) {
		  case 1: case 2: {
		    value = value & 0x000000FFUL;
		  } break;
		  default: {
		    r = value & 0x000000FFUL;
		    g = ((value >>  8) & 0x000000FFUL);
		    b = ((value >> 16) & 0x000000FFUL);
		    if(p->fntsc) {
		      value = (dkma_add_ulong_ok(
		        dkma_add_ulong_ok(
			  dkma_mul_ulong_ok(54UL, r, &me),
			  dkma_mul_ulong_ok(183UL, g, &me),
			  &me
			),
			dkma_mul_ulong_ok(19UL, b, &me),
		        &me
		      ) >> 8) & 0x000000FFUL;
		    } else {
		      /* 0.3 * r + 0.59 * g + 0.11 * b */
		      value = dkma_double_to_ul_ok(
		        dkma_rint(
			  dkma_add_double_ok(
			    dkma_add_double_ok(
			      dkma_mul_double_ok(0.3,r,&me),
			      dkma_mul_double_ok(0.59,g,&me),
			      &me
			    ),
			    dkma_mul_double_ok(0.11, b, &me),
			    &me
			  )
			),
			&me
		      );
		    }
		  } break;
		}
	      } break;
	      default: {
		switch(cr) {
		  case 0: { value = value & 0x000000FFUL; } break;
		  case 1: { value = ((value >>  8) & 0x000000FFUL); } break;
		  case 2: { value = ((value >> 16) & 0x000000FFUL); } break;
		  case 3: { value = ((value >> 24) & 0x000000FFUL); } break;
		}
	      } break;
	    }
	    
	    back = (unsigned short)value;
	    
	    if((p->cf)->bpc < 8) {
	      
	      back = back >> (8 - (p->cf)->bpc);
	      back = (back & (p->cf)->vmask);
	    }
	    
	  } else {			
	  }
	} else {			
	}
      } else {				
      }
    } else {				
    }
  } else {				
  } 
  return back;
}



unsigned short
dkbiftif_red DK_P3(dk_bif_t *,p, unsigned long,x, unsigned long,y)
{
  unsigned short back = 0;
  
  back = get_component_at(p, x, y, 0);
  
  return back;
}



unsigned short
dkbiftif_green DK_P3(dk_bif_t *,p, unsigned long,x, unsigned long,y)
{
  unsigned short back = 0;
  
  back = get_component_at(p, x, y, 1);
  
  return back;
}



unsigned short
dkbiftif_blue DK_P3(dk_bif_t *,p, unsigned long,x, unsigned long,y)
{
  unsigned short back = 0;
  
  back = get_component_at(p, x, y, 2);
  
  return back;
}



unsigned short
dkbiftif_gray DK_P3(dk_bif_t *,p, unsigned long,x, unsigned long,y)
{
  unsigned short back = 0;
  
  back = get_component_at(p, x, y, 4);
  
  return back;
}



unsigned short
dkbiftif_alpha DK_P3(dk_bif_t *,p, unsigned long,x, unsigned long,y)
{
  unsigned short back = 0;
  
  back = get_component_at(p, x, y, 3);
  
  return back;
}



void
dkbiftif_frame_release DK_P2(dk_bif_t *,p, dk_bif_frame_t *,f)
{
  
  if(f) {
    if((f->d).tif.raster) {
      _TIFFfree((f->d).tif.raster);
      (f->d).tif.raster = NULL;
    }
  }
  
}



void
dkbiftif_release DK_P1(dk_bif_t *,p)
{
  
  if(p) {
    if((p->d).tif.tiff) {
      TIFFClose((p->d).tif.tiff); (p->d).tif.tiff = NULL;
    }
    if(p->tmpfilename) {
      dksf_remove_file(p->tmpfilename);
    }
  }
  
}



#endif


