/*
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	dkbif.c	Bitmap image file API module.
*/


/**	Inside the dkbif module.
*/
#define DKBIF_C		1



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




#line 56 "dkbif.ctr"




/**	Version text and used libraries.
*/
char *dkbif_version_text[] = {
#if DK_HAVE_PNG_H
"libpng       The official PNG reference library",
"             http://www.libpng.org/pub/png/libpng.html",
#endif
#if DK_HAVE_JPEGLIB_H
"jpegsrc      The Independent JPEG Group's free JPEG software",
"             http://www.ijg.org/",
#endif
#if DK_HAVE_PNM_H || DK_HAVE_NETPBM_PNM_H
"netpbm       The image conversion toolkit",
"             http://netpbm.sourceforge.net/",
#endif
#if DK_HAVE_TIFF_H
"libtiff      The TIFF library.",
"             http://www.remotesensing.org/libtiff/",
#endif
NULL
};



char **dkbif_lic_get DK_P0()
{
  return dkbif_version_text;
}



/**	Flag: File names case-insensitive.
*/
static int fncaseins =
#if DK_HAVE_FNCASEINS
1
#else
0
#endif
;



/**	Connect file name suffix to image type.
*/
typedef struct {
	char *s;	/**< File name suffix. */
	int   t;	/**< Image type. */
} image_type;



/**	Suffix for PNG files.
*/
static char str_png[]	= { "png" };

/**	Suffix for JPEG files.
*/
static char str_jpg[]	= { "jpg" };

/**	Suffix for JPEG files.
*/
static char str_jpeg[]	= { "jpeg" };

/**	Suffix for NetPBM files.
*/
static char str_pbm[]	= { "pbm" };

/**	Suffix for NetPBM files.
*/
static char str_ppm[]	= { "ppm" };

/**	Suffix for NetPBM files.
*/
static char str_pgm[]	= { "pgm" };

/**	Suffix for NetPBM files.
*/
static char str_pnm[]	= { "pnm" };

/**	Suffix for TIFF files.
*/
static char str_tif[]	= { "tif" };

/**	Suffix for TIFF files.
*/
static char str_tiff[]	= { "tiff" };



/**	Bits for mask operations.
*/
static unsigned short bits[] = {
  0x0001, 0x0002, 0x0004, 0x0008,
  0x0010, 0x0020, 0x0040, 0x0080,
  0x0100, 0x0200, 0x0400, 0x0800,
  0x1000, 0x2000, 0x4000, 0x8000
};



/**	Image type table.
*/
static image_type image_types[] = {
  { str_png, DKBIF_TYPE_PNG },
  { str_jpg, DKBIF_TYPE_JPG },
  { str_jpeg, DKBIF_TYPE_JPG },
  { str_pbm, DKBIF_TYPE_NETPBM },
  { str_ppm, DKBIF_TYPE_NETPBM },
  { str_pgm, DKBIF_TYPE_NETPBM },
  { str_pnm, DKBIF_TYPE_NETPBM },
  { str_tif, DKBIF_TYPE_TIFF },
  { str_tiff, DKBIF_TYPE_TIFF },
  { NULL, DKBIF_TYPE_UNKNOWN }
};



/**	Get background red value.
	@param	p	Image.
	@return	The background red value.
*/
static
double
getbgr DK_P1(dk_bif_t *,p)
{
  double back = 1.0;
  if(p->cf) {
    back = (p->cf)->mr;
    if((back < 0.0) || (p->m == 2)) {
      back = p->mr;
    }
  }
  return back;
}



/**	Get background green value.
	@param	p	Image.
	@return	The background green value.
*/
static
double
getbgg DK_P1(dk_bif_t *,p)
{
  double back = 1.0;
  
  if(p->cf) {
    
    back = (p->cf)->mg;
    if((back < 0.0) || (p->m == 2)) {
      
      back = p->mg;
    }
  } 
  return back;
}




/**	Get background blue value.
	@param	p	Image.
	@return	The background blue value.
*/
static
double
getbgb DK_P1(dk_bif_t *,p)
{
  double back = 1.0;
  if(p->cf) {
    back = (p->cf)->mb;
    if((back < 0.0) || (p->m == 2)) {
      back = p->mb;
    }
  }
  return back;
}



unsigned short
dkbif_max_for_bpc DK_P1(unsigned short,b)
{
  unsigned short back = 0;
  unsigned short i;
  for(i = 1; i <= b; i++) {
    back |= bits[i-1];
  }
  return back;
}



/**	Get alpha value.
	@param	p	Image.
	@param	x	X position.
	@param	y	Y position.
	@return	The alpha value.
*/
static
unsigned short get_alpha DK_P3(dk_bif_t *,p, unsigned long,x, unsigned long,y)
{
  unsigned short back = 0;
  
  /* ----- additional file types require modifications here ----- */
  switch(p->t) {
    case DKBIF_TYPE_PNG: {
#if DK_HAVE_ZLIB_H	&&	DK_HAVE_PNG_H
      back = dkbifpng_alpha(p, x, y);
#else
#endif
    } break;
    case DKBIF_TYPE_JPG: {
#if DK_HAVE_JPEGLIB_H
      back = dkbifjpg_alpha(p, x, y);
#else
#endif
    } break;
    case DKBIF_TYPE_NETPBM: {
#if DK_HAVE_PNM_H || DK_HAVE_NETPBM_PNM_H
      back = dkbifpbm_alpha(p, x, y);
#else
#endif
    } break;
    case DKBIF_TYPE_TIFF: {
#if DK_HAVE_TIFF_H
      back = dkbiftif_alpha(p, x, y);
#else
#endif
    } break;
  } 
  return back;
}



/**	Get red value.
	@param	p	Image.
	@param	x	X position.
	@param	y	Y position.
	@return	The red value.
*/
static
unsigned short get_red DK_P3(dk_bif_t *,p, unsigned long,x, unsigned long,y)
{
  unsigned short back = 0;
  int ec = 0;
  double value, avalue;
  /* ----- additional file types require modifications here ----- */
  switch(p->t) {
    case DKBIF_TYPE_PNG: {
#if DK_HAVE_ZLIB_H	&&	DK_HAVE_PNG_H
      back = dkbifpng_red(p, x, y);
#else
#endif
    } break;
    case DKBIF_TYPE_JPG: {
#if DK_HAVE_JPEGLIB_H
      back = dkbifjpg_red(p, x, y);
#else
#endif
    } break;
    case DKBIF_TYPE_NETPBM: {
#if DK_HAVE_PNM_H || DK_HAVE_NETPBM_PNM_H
      back = dkbifpbm_red(p, x, y);
#else
#endif
    } break;
    case DKBIF_TYPE_TIFF: {
#if DK_HAVE_TIFF_H
      back = dkbiftif_red(p, x, y);
#else
#endif
    } break;
  }
  switch((p->cf)->ch) {
    case 4: case 2: {
      if(p->m) {
        value = dkma_ul_to_double((unsigned long)back);
	avalue = dkma_ul_to_double(get_alpha(p,x,y));
	value = dkma_div_double_ok(value, (p->cf)->vmax, &ec);
	avalue = dkma_div_double_ok(avalue, (p->cf)->vmax, &ec);
	/* value = value * avalue + (1.0 - avalue) * p->mr; */
	value = dkma_add_double_ok(
	  dkma_mul_double_ok(value, avalue, &ec),
	  dkma_mul_double_ok(
	    dkma_sub_double_ok(1.0, avalue, &ec), getbgr(p), &ec
	  ),
	  &ec
	);
	value = dkma_mul_double_ok(value, (p->cf)->vmax, &ec);
	value = dkma_rint(value);
	back = (unsigned short)dkma_double_to_ul_ok(value, &ec);
	if(ec) { p->ec = DK_ERR_MATH_OOR; }
      }
    } break;
  }
  return back;
}



/**	Get green value.
	@param	p	Image.
	@param	x	X position.
	@param	y	Y position.
	@return	The green value.
*/
static
unsigned short get_green DK_P3(dk_bif_t *,p, unsigned long,x, unsigned long,y)
{
  unsigned short back = 0;
  int ec = 0;
  double value, avalue;
  
  /* ----- additional file types require modifications here ----- */
  switch(p->t) {
    case DKBIF_TYPE_PNG: {
#if DK_HAVE_ZLIB_H	&&	DK_HAVE_PNG_H
      back = dkbifpng_green(p, x, y);
#else
#endif
    } break;
    case DKBIF_TYPE_JPG: {
#if DK_HAVE_JPEGLIB_H
      back = dkbifjpg_green(p, x, y);
#else
#endif
    } break;
    case DKBIF_TYPE_NETPBM: {
#if DK_HAVE_PNM_H || DK_HAVE_NETPBM_PNM_H
      back = dkbifpbm_green(p, x, y);
#else
#endif
    } break;
    case DKBIF_TYPE_TIFF: {
#if DK_HAVE_TIFF_H
      back = dkbiftif_green(p, x, y);
#else
#endif
    } break;
  }
  switch((p->cf)->ch) {
    case 4: case 2: {
      if(p->m) {
        
        value = dkma_ul_to_double((unsigned long)back);
	
	avalue = dkma_ul_to_double(get_alpha(p,x,y));
	
	value = dkma_div_double_ok(value, (p->cf)->vmax, &ec);
	
	avalue = dkma_div_double_ok(avalue, (p->cf)->vmax, &ec);
	
	/* value = value * avalue + (1.0 - avalue) * p->mg; */
	value = dkma_add_double_ok(
	  dkma_mul_double_ok(value, avalue, &ec),
	  dkma_mul_double_ok(
	    dkma_sub_double_ok(1.0, avalue, &ec), getbgg(p), &ec
	  ),
	  &ec
	);
	value = dkma_mul_double_ok(value, (p->cf)->vmax, &ec);
	value = dkma_rint(value);
	back = (unsigned short)dkma_double_to_ul_ok(value, &ec);
	if(ec) { p->ec = DK_ERR_MATH_OOR; }
      }
    } break;
  } 
  return back;
}



/**	Get blue value.
	@param	p	Image.
	@param	x	X position.
	@param	y	Y position.
	@return	The blue value.
*/
static
unsigned short get_blue DK_P3(dk_bif_t *,p, unsigned long,x, unsigned long,y)
{
  unsigned short back = 0;
  int ec = 0;
  double value, avalue;
  
  /* ----- additional file types require modifications here ----- */
  switch(p->t) {
    case DKBIF_TYPE_PNG: {
#if DK_HAVE_ZLIB_H	&&	DK_HAVE_PNG_H
      back = dkbifpng_blue(p, x, y);
#else
#endif
    } break;
    case DKBIF_TYPE_JPG: {
#if DK_HAVE_JPEGLIB_H
      back = dkbifjpg_blue(p, x, y);
#else
#endif
    } break;
    case DKBIF_TYPE_NETPBM: {
#if DK_HAVE_PNM_H || DK_HAVE_NETPBM_PNM_H
      back = dkbifpbm_blue(p, x, y);
#else
#endif
    } break;
    case DKBIF_TYPE_TIFF: {
#if DK_HAVE_TIFF_H
      back = dkbiftif_blue(p, x, y);
#else
#endif
    } break;
  } 
  switch((p->cf)->ch) {
    case 4: case 2: {
      
      if(p->m) {	
        value = dkma_ul_to_double((unsigned long)back);
	avalue = dkma_ul_to_double(get_alpha(p,x,y));
	value = dkma_div_double_ok(value, (p->cf)->vmax, &ec);
	avalue = dkma_div_double_ok(avalue, (p->cf)->vmax, &ec);
	/* value = value * avalue + (1.0 - avalue) * p->mb; */
	value = dkma_add_double_ok(
	  dkma_mul_double_ok(value, avalue, &ec),
	  dkma_mul_double_ok(
	    dkma_sub_double_ok(1.0, avalue, &ec), getbgb(p), &ec
	  ),
	  &ec
	);
	value = dkma_mul_double_ok(value, (p->cf)->vmax, &ec);
	value = dkma_rint(value);
	back = (unsigned short)dkma_double_to_ul_ok(value, &ec);
	if(ec) { p->ec = DK_ERR_MATH_OOR; }
      }
    } break;
  } 
  return back;
}



/**	Get gray value.
	@param	p	Image.
	@param	x	X position.
	@param	y	Y position.
	@return	The gray value.
*/
static
unsigned short get_gray DK_P3(dk_bif_t *,p, unsigned long,x, unsigned long,y)
{
  unsigned short back = 0;
  int ec = 0;
  double value, avalue, bgg;
  /* ----- additional file types require modifications here ----- */
  switch(p->t) {
    case DKBIF_TYPE_PNG: {
#if DK_HAVE_ZLIB_H	&&	DK_HAVE_PNG_H
      back = dkbifpng_gray(p, x, y);
#else
#endif
    } break;
    case DKBIF_TYPE_JPG: {
#if DK_HAVE_JPEGLIB_H
      back = dkbifjpg_gray(p, x, y);
#else
#endif
    } break;
    case DKBIF_TYPE_NETPBM: {
#if DK_HAVE_PNM_H || DK_HAVE_NETPBM_PNM_H
      back = dkbifpbm_gray(p, x, y);
#else
#endif
    } break;
    case DKBIF_TYPE_TIFF: {
#if DK_HAVE_TIFF_H
      back = dkbiftif_gray(p, x, y);
#else
#endif
    } break;
  }
  switch((p->cf)->ch) {
    case 2: {
      if(p->m) {
        value = dkma_ul_to_double((unsigned long)back);
	avalue = dkma_ul_to_double(get_alpha(p,x,y));
	value = dkma_div_double_ok(value, (p->cf)->vmax, &ec);
	avalue = dkma_div_double_ok(avalue, (p->cf)->vmax, &ec);
	bgg = 0.3 * getbgr(p) + 0.59 * getbgg(p) + 0.11 * getbgb(p);
	/* value = value * avalue + (1.0 - avalue) * bgg; */
	value = dkma_add_double_ok(
	  dkma_mul_double_ok(value, avalue, &ec),
	  dkma_mul_double_ok(dkma_sub_double_ok(1.0, avalue, &ec), bgg, &ec),
	  &ec
	);
	value = dkma_mul_double_ok(value, (p->cf)->vmax, &ec);
	value = dkma_rint(value);
	back = (unsigned short)dkma_double_to_ul_ok(value, &ec);
	if(ec) { p->ec = DK_ERR_MATH_OOR; }
      }
    } break;
  }
  return back;
}



/**	Correct unsigned short value.
	@param	omax	Factor	(output maximum).
	@param	imax	Divisor (input maximum).
	@param	v	Value to correct.
	@param	ec	Pointer to error code variable.
	@return	Corrected value.
*/
static
unsigned short correct_unsigned_short DK_P4(\
  double,omax, double,imax, unsigned short,v, int *,ec)
{
  unsigned short back = 0;
  back = (unsigned short)dkma_double_to_ul_ok(
    dkma_rint(
      dkma_mul_double_ok(
        dkma_div_double_ok(
	  dkma_ul_to_double((unsigned long)v),
	  imax,
	  ec
	),
	omax,
	ec
      )
    ),
    ec
  );
  return back;
}



int
dkbif_get_type DK_P1(char *,infname)
{
  char *s = NULL;
  image_type *ptr;
  int back = DKBIF_TYPE_UNKNOWN;
  if(infname) {
    s = dksf_get_file_type_dot(infname);
    if(s) {
      s++;
    } else {
      s = infname;
    }
  }
  if(s) {
    ptr = image_types;
    while((back == DKBIF_TYPE_UNKNOWN) && (ptr->s)) {
      if(fncaseins) {
        if(dkstr_casecmp(ptr->s, s)) {
          ptr++;
        } else {
          back = ptr->t;
        }
      } else {
        if(strcmp(ptr->s, s)) {
          ptr++;
        } else {
          back = ptr->t;
        }
      }
    }
  }
  return back;
}



/**	Release frame.
	@param	b	Image.
	@param	f	Frame to release.
*/
static
void
frame_release DK_P2(dk_bif_t *,b, dk_bif_frame_t *,f)
{
  
  if(f) {
    /* ----- additional file types require modifications here ----- */
    switch(b->t) {
      case DKBIF_TYPE_PNG: {
#if DK_HAVE_ZLIB_H	&&	DK_HAVE_PNG_H
        dkbifpng_frame_release(b, f);
#else
#endif
      } break;
      case DKBIF_TYPE_JPG: {
#if DK_HAVE_JPEGLIB_H
        dkbifjpg_frame_release(b, f);
#else
#endif
      } break;
      case DKBIF_TYPE_NETPBM: {
#if DK_HAVE_PNM_H || DK_HAVE_NETPBM_PNM_H
        dkbifpbm_frame_release(b, f);
#else
#endif
      } break;
      case DKBIF_TYPE_TIFF: {
#if DK_HAVE_TIFF_H
       dkbiftif_frame_release(b, f);
#else
#endif
      } break;
    }
  } 
}



/*
	Create empty frame dataset.
*/
dk_bif_frame_t *
dkbif_frame_new DK_P2(dk_bif_t *,b, unsigned long,n)
{
  dk_bif_frame_t *back = NULL;
  
  back = dk_new(dk_bif_frame_t,1);
  if(back) {
    back->n = n;
    if(dksto_add(b->f, (void *)back)) {
      back->w = 0UL; back->h = 0UL; back->bpc = 0; back->ch = 0;
      back->xdpi = -1.0; back->ydpi = -1.0;
      back->vmax = 1.0;
      back->mr = back->mg = back->mb = -1.0;
    } else {
      frame_release(b, back); back = NULL;
    }
  }
  if(!(back)) {
    b->ec = DK_ERR_NOMEM;
  }
  
  return back;
}



/**	Compare two frames.
	@param	l	Left frame.
	@param	r	Right frame.
	@param	cr	Comparison criteria (0=frame/frame, 1=frame/number).
	@return	Comparison result.
*/
static
int
frame_compare DK_P3(void *,l, void *,r, int,cr)
{
  int back = 0;
  dk_bif_frame_t *pl, *pr;
  
  pl = (dk_bif_frame_t *)l; pr = (dk_bif_frame_t *)r;
  if(l) {
    if(r) {
      switch(cr) {
        case 1: {
	  if(pl->n > (*((unsigned long *)r))) {
	    back = 1;
	  } else {
	    if(pl->n < (*((unsigned long *)r))) {
	      back = -1;
	    }
	  }
	} break;
	default: {
	  if(pl->n > pr->n) {
	    back = 1;
	  } else {
	    if(pl->n < pr->n) {
	      back = -1;
	    }
	  }
	} break;
      }
    } else {
      back = 1;
    }
  } else {
    if(r) {
      back = -1;
    }
  } 
  return back;
}


void
dkbif_close DK_P1(dk_bif_ptr,p)
{
  dk_bif_t *ptr;
  dk_bif_frame_t	*fptr;
  
  ptr = (dk_bif_t *)p;
  if(ptr) {
    if(ptr->f) {
      if(ptr->fi) {
        dksto_it_reset(ptr->fi);
	while((fptr = (dk_bif_frame_t *)dksto_it_next(ptr->fi)) != NULL) {
	  frame_release(ptr, fptr);
	}
	dksto_it_close(ptr->fi); ptr->fi = NULL;
      }
      dksto_close(ptr->f); ptr->f = NULL;
    }
    /* ----- additional file types require modifications here ----- */
    switch(ptr->t) {
      case DKBIF_TYPE_PNG: {
#if DK_HAVE_ZLIB_H	&&	DK_HAVE_PNG_H
        dkbifpng_release(ptr);
#else
#endif
      } break;
      case DKBIF_TYPE_JPG: {
#if DK_HAVE_JPEGLIB_H
        dkbifjpg_release(ptr);
#else
#endif
      } break;
      case DKBIF_TYPE_NETPBM: {
#if DK_HAVE_PNM_H || DK_HAVE_NETPBM_PNM_H
        dkbifpbm_release(ptr);
#else
#endif
      } break;
      case DKBIF_TYPE_TIFF: {
#if DK_HAVE_TIFF_H
        dkbiftif_release(ptr);
#else
#endif
      } break;
    }
    dk_delete(ptr);
  } 
}



dk_bif_ptr
dkbif_open DK_P0()
{
  int ok = 0;
  dk_bif_t *back = NULL;
  
  back = dk_new(dk_bif_t,1);
  if(back) {
    back->nof = 0UL;
    back->f = NULL; back->fi = NULL;
    back->ec = 0; back->t = DKBIF_TYPE_UNKNOWN;
    back->w_all = 0UL; back->h_all = 0UL;
    back->cf = NULL;
    back->vmax = 1.0; back->obpc = 0;
    back->mtl = 0.000001;
    back->mr = 1.0; back->mg = 1.0; back->mb = 1.0;
    back->tmpfilename = NULL;
    back->whs = 0x00;
    back->f = dksto_open(0);
    if(back->f) {
      dksto_set_comp(back->f, frame_compare, 0);
      back->fi = dksto_it_open(back->f);
      if(back->fi) {
        ok = 1;
      }
    }
    if(!ok) {
      dkbif_close(back); back = NULL;
    }
  } 
  return ((dk_bif_ptr)back);
}



/*
	Read header information from file.
	tp ... DKBIF_TYPE...
	fn ... file name, used if tp==DKBIF_TYPE_UNKNOWN
*/
int
dkbif_read_header DK_P5(dk_bif_ptr,p, FILE *,f, char *,fn, int,tp, char *,tmpfilename)
{
  int back = 0;
  dk_bif_t *ptr = NULL;
  char *sptr;
  
  if((p) && (f)) {		
    ptr = (dk_bif_t *)p;
    ptr->tmpfilename = tmpfilename;
    /* ----- additional file types require modifications here ----- */
    ptr->t = tp;
    if(ptr->t == DKBIF_TYPE_UNKNOWN) {	
      if(fn) {
        sptr = dksf_get_file_type_dot(fn);
	if(sptr) {
	  sptr++;
	} else {
	  sptr = fn;
	}
	ptr->t = dkbif_get_type(sptr);
      }
    } 
    switch(ptr->t) {
      case DKBIF_TYPE_PNG: {		
#if DK_HAVE_ZLIB_H	&&	DK_HAVE_PNG_H
        back = dkbifpng_header(p, f);
#else
#endif
      } break;
      case DKBIF_TYPE_JPG: {		
#if DK_HAVE_JPEGLIB_H
        back = dkbifjpg_header(p, f);
#else
#endif
      } break;
      case DKBIF_TYPE_NETPBM: {		
#if DK_HAVE_PNM_H || DK_HAVE_NETPBM_PNM_H
        back = dkbifpbm_header(p, f);
#else
#endif
      } break;
      case DKBIF_TYPE_TIFF: {		
#if DK_HAVE_TIFF_H
        back = dkbiftif_header(p, f);
#else
#endif
      } break;
      default: {
        ptr->ec = DK_ERR_SYNTAX;
      } break;
    }
  } 
  return back;
}



int
dkbif_read_data DK_P2(dk_bif_ptr,p, FILE *,f)
{
  int back = 0;
  dk_bif_t *ptr = NULL;
  dk_bif_frame_t	*fptr;
  
  if((p) && (f)) {
    ptr = (dk_bif_t *)p;	
    /* ----- additional file types require modifications here ----- */
    switch(ptr->t) {
      case DKBIF_TYPE_PNG: {
#if DK_HAVE_ZLIB_H	&&	DK_HAVE_PNG_H
	
        back = dkbifpng_data(p, f);
#else
#endif
      } break;
      case DKBIF_TYPE_JPG: {
#if DK_HAVE_JPEGLIB_H
        back = dkbifjpg_data(p, f);
#else
#endif
      } break;
      case DKBIF_TYPE_NETPBM: {
#if DK_HAVE_PNM_H || DK_HAVE_NETPBM_PNM_H
        back = dkbifpbm_data(p, f);
#else
#endif
      } break;
      case DKBIF_TYPE_TIFF: {
#if DK_HAVE_TIFF_H
        back = dkbiftif_data(p, f);
#else
#endif
      } break;
    }
    /* calculate vmax values */
    dksto_it_reset(ptr->fi);
    while((fptr = dksto_it_next(ptr->fi)) != NULL) {
      fptr->vmax =
      dkma_ul_to_double((unsigned long)dkbif_max_for_bpc(fptr->bpc));
    }
  } else {	
  }
  
  return back;
}



/*
	Return number of frames in file.
*/
unsigned long
dkbif_number_of_frames DK_P1(dk_bif_ptr,p)
{
  unsigned long back = 0UL;
  dk_bif_t *ptr = NULL;
  
  ptr =(dk_bif_t *)p;
  if(ptr) {
    back = ptr->nof;
  } 
  return back;
}



/*
	Select a frame.
*/
int
dkbif_set_frame DK_P2(dk_bif_ptr,ptr, unsigned long,fno)
{
  int back = 0; unsigned long ul;
  dk_bif_t *p = NULL;
  
  if(ptr) {
    p = (dk_bif_t *)ptr;
    ul = fno;
    if((p->f) && (p->fi)) {
      p->cf = dksto_it_find_like(p->fi, (void *)(&ul), 1);
      if(p->cf) {
        back = 1;
      }
    }
    if(!back) {
      p->ec = DK_ERR_NO_SUCH_OBJECT;
    }
  } 
  return back;
}



/*
	Return number of channels
	1 = gray
	2 = gray + alpha
	3 = RGB
	4 = RGB + alpha
*/
int
dkbif_get_channels DK_P1(dk_bif_ptr,ptr)
{
  int back = 0;
  dk_bif_t *p = NULL;
  
  if(ptr) {
    p = (dk_bif_t *)ptr;
    if(p->cf) {
      back = (p->cf)->ch;
    }
  }
  
  return back;
}



/*
	Return number of bits per component.
*/
unsigned short
dkbif_get_bits_per_component DK_P1(dk_bif_ptr,ptr)
{
  unsigned short back = 0;
  dk_bif_t *p = NULL;
  
  if(ptr) {
    p = (dk_bif_t *)ptr;
    if(p->cf) {
      back = (p->cf)->bpc;
    }
  } 
  return back;
}



/*
	Set bits per component for output operations.
*/
int
dkbif_set_bits_per_component DK_P2(dk_bif_ptr,ptr, unsigned short,bpc)
{
  int back = 0;
  dk_bif_t *p = NULL;
  
  if(ptr) {
    p = (dk_bif_t *)ptr;
    p->obpc = bpc;
    p->vmax = dkma_ul_to_double((unsigned long)dkbif_max_for_bpc(p->obpc));
  }
  
  return back;
}



/*
	Allow/deny mixing of pixels against background
	f = 0 ... no mixing
	f = 1 ... mixing against background color from bg chunk
	f = 2 ... mixing, always use background as specified
*/
int
dkbif_set_mixing DK_P5(dk_bif_ptr,ptr,int,f,double,r,double,g,double,b)
{
  int back = 0;
  dk_bif_t *p = NULL;
  
  if(ptr) {
    p = (dk_bif_t *)ptr;
    p->m = f;
    p->mr = r; p->mg = g; p->mb = b;
    back = 0;
  }
  
  return back;
}



/*
	Set trigger level for conversion of alpha channel
	to image masks.
*/
int
dkbif_set_mask_trigger_level DK_P2(dk_bif_ptr,ptr, double,tl)
{
  int back = 0;
  dk_bif_t *p = NULL;
  
  if(ptr) {
    p = (dk_bif_t *)ptr;
    p->mtl = tl;
    back = 1;
  }
  
  return back;
}



unsigned short
dkbif_get_red DK_P3(dk_bif_ptr,ptr, unsigned long,x, unsigned long,y)
{
  unsigned short back = 0;
  dk_bif_t *p = NULL;
  int ec = 0;
  
  if(ptr) {
    p = (dk_bif_t *)ptr;
    if(p->cf) {
    if((x < (p->cf)->w) && (y < (p->cf)->h)) {
      switch((p->cf)->ch) {
        case 1: case 2: {
	  back = get_gray(p, x, y);
	} break;
	default: {
	  back = get_red(p, x, y);
	} break;
      }
      if(p->obpc) {
        if(p->obpc != (p->cf)->bpc) {
	  back = correct_unsigned_short(p->vmax, (p->cf)->vmax, back, &ec);
	  if(ec) { p->ec = DK_ERR_MATH_OOR; }
	}
      }
    }
    }
  }
  
  return back;
}



unsigned short
dkbif_get_green DK_P3(dk_bif_ptr,ptr, unsigned long,x, unsigned long,y)
{
  unsigned short back = 0;
  dk_bif_t *p = NULL;
  int ec = 0;
  
  if(ptr) {
    p = (dk_bif_t *)ptr;
    if(p->cf) {
    if((x < (p->cf)->w) && (y < (p->cf)->h)) {
      switch((p->cf)->ch) {
        case 1: case 2: {
	  back = get_gray(p, x, y);
	} break;
	default: {
	  back = get_green(p, x, y);
	} break;
      }
      if(p->obpc) {
        if(p->obpc != (p->cf)->bpc) {
	  back = correct_unsigned_short(p->vmax, (p->cf)->vmax, back, &ec);
	  if(ec) { p->ec = DK_ERR_MATH_OOR; }
	}
      }
    }
    }
  }
  
  return back;
}


unsigned short
dkbif_get_blue DK_P3(dk_bif_ptr,ptr, unsigned long,x, unsigned long,y)
{
  unsigned short back = 0;
  dk_bif_t *p = NULL;
  int ec = 0;
  
  if(ptr) {
    p = (dk_bif_t *)ptr;
    if(p->cf) {
    if((x < (p->cf)->w) && (y < (p->cf)->h)) {
      switch((p->cf)->ch) {
        case 1: case 2: {
	  back = get_gray(p, x, y);
	} break;
	default: {
	  back = get_blue(p, x, y);
	} break;
      }
      if(p->obpc) {
        if(p->obpc != (p->cf)->bpc) {
	  back = correct_unsigned_short(p->vmax, (p->cf)->vmax, back, &ec);
	  if(ec) { p->ec = DK_ERR_MATH_OOR; }
	}
      }
    }
    }
  }
  
  return back;
}


unsigned short
dkbif_get_gray DK_P3(dk_bif_ptr,ptr, unsigned long,x, unsigned long,y)
{
  unsigned short back = 0, r = 0, g = 0, b = 0;
  dk_bif_t *p = NULL;
  int ec = 0;
  
  if(ptr) {
    p = (dk_bif_t *)ptr;
    if(p->cf) {
    if((x < (p->cf)->w) && (y < (p->cf)->h)) {
      switch((p->cf)->ch) {
        case 1: case 2: {
	  back = get_gray(p, x, y);
	} break;
	default: {
	  r = get_red(p, x, y);
	  g = get_green(p, x, y);
	  b = get_blue(p, x, y);
	  if(p->fntsc) {
	    back = (unsigned short)(
	      (
	        dkma_add_ulong_ok(
		  dkma_add_ulong_ok(
		    dkma_mul_ulong_ok(54UL,(unsigned long)r,&ec),
		    dkma_mul_ulong_ok(183UL,(unsigned long)g,&ec),
		    &ec
		  ),
		  dkma_mul_ulong_ok(19UL,(unsigned long)b,&ec),
		  &ec
	        ) >> 8
	      ) & 0xFFFF
	    );
	  } else {
	    back = (unsigned short)dkma_double_to_ul_ok(
	      dkma_rint(
	        dkma_add_double_ok(
		  dkma_add_double_ok(
		    dkma_mul_double_ok(
		      0.3,dkma_ul_to_double((unsigned long)r),&ec
		    ),
		    dkma_mul_double_ok(
		      0.59,dkma_ul_to_double((unsigned long)g),&ec
		    ),
		    &ec
		  ),
		  dkma_mul_double_ok(
		    0.11,dkma_ul_to_double((unsigned long)g),&ec
		  ),
		  &ec
		)
	      ),
	      &ec
	    );
	  }
	  if(ec) { p->ec = DK_ERR_MATH_OOR; }
	} break;
      }
      if(p->obpc) {
        if(p->obpc != (p->cf)->bpc) {
	  back = correct_unsigned_short(p->vmax, (p->cf)->vmax, back, &ec);
	  if(ec) { p->ec = DK_ERR_MATH_OOR; }
	}
      }
    }
    }
  }
  
  return back;
}



unsigned short
dkbif_get_alpha DK_P3(dk_bif_ptr,ptr, unsigned long,x, unsigned long,y)
{
  unsigned short back = 0;
  dk_bif_t *p = NULL;
  int ec = 0;
  
  if(ptr) {
    p = (dk_bif_t *)ptr;
    if(p->cf) {
    if((x < (p->cf)->w) && (y < (p->cf)->h)) {
      back = get_alpha(p, x, y);
      if(p->obpc) {
        if(p->obpc != (p->cf)->bpc) {
	  back = correct_unsigned_short(p->vmax, (p->cf)->vmax, back, &ec);
	  if(ec) { p->ec = DK_ERR_MATH_OOR; }
	}
      }
    }
    }
  }
  
  return back;
}


unsigned short
dkbif_get_mask DK_P3(dk_bif_ptr,ptr, unsigned long,x, unsigned long,y)
{
  int back = 1;
  dk_bif_t *p = NULL;
  int ec = 0;
  double v;
  
  if(ptr) {
    p = (dk_bif_t *)ptr;
    if(p->cf) {
    if((x < (p->cf)->w) && (y < (p->cf)->h)) {
      switch((p->cf)->ch) {
        case 2: case 4: {
	  v = dkma_div_double_ok(
	    dkma_ul_to_double((unsigned long)get_alpha(p,x,y)),
	    (p->cf)->vmax,
	    &ec
	  );
	  if(v > p->mtl) {
	    back = 0;
	  } else {
	    back = (p->cf)->vmask;
	  }
	} break;
      }
    }
    }
    if(ec) { p->ec = DK_ERR_MATH_OOR; }
  }
  
  return back;
}



void
dkbif_allow_fast_ntsc DK_P2(dk_bif_ptr,ptr, int,f)
{
  dk_bif_t *p;
  p = (dk_bif_t *)ptr;
  if(p) {
    p->fntsc = (f ? 0x01 : 0x00);
  }
}



unsigned long dkbif_get_width DK_P1(dk_bif_ptr,ptr)
{
  unsigned long back = 0UL;
  dk_bif_t *p;
  p = (dk_bif_t *)ptr;
  if(p) {
    if(p->cf) {
      back = (p->cf)->w;
    }
  }
  return back;
}



unsigned long dkbif_get_height DK_P1(dk_bif_ptr,ptr)
{
  unsigned long back = 0UL;
  dk_bif_t *p;
  p = (dk_bif_t *)ptr;
  if(p) {
    if(p->cf) {
      back = (p->cf)->h;
    }
  }
  return back;
}


double
dkbif_get_real_width DK_P1(dk_bif_ptr,ptr)
{
  double back = 0.0;
  unsigned long ul;
  dk_bif_t *p;
  int ec = 0;
  p = (dk_bif_t *)ptr;
  if(p) {
    if(p->cf) {
      ul = (p->cf)->w;
      back = dkma_ul_to_double(ul);
      if((p->cf)->xdpi > 0.0) {
	back = dkma_mul_double_ok(
	  dkma_div_double_ok(back, (p->cf)->xdpi, &ec),
	  72.0,
	  &ec
	);
	if(ec) {
	  p->ec = DK_ERR_MATH_OOR;
	}
      }
    }
  }
  return back;
}



double
dkbif_get_real_height DK_P1(dk_bif_ptr,ptr)
{
  double back = 0.0;
  unsigned long ul;
  dk_bif_t *p;
  int ec = 0;
  p = (dk_bif_t *)ptr;
  if(p) {
    if(p->cf) {
      ul = (p->cf)->h;
      back = dkma_ul_to_double(ul);
      if((p->cf)->ydpi > 0.0) {
	back = dkma_mul_double_ok(
	  dkma_div_double_ok(back, (p->cf)->ydpi, &ec),
	  72.0,
	  &ec
	);
	if(ec) {
	  p->ec = DK_ERR_MATH_OOR;
	}
      }
    }
  }
  return back;
}



int
dkbif_get_must_scale DK_P1(dk_bif_ptr,ptr)
{
  int back = 0;
  dk_bif_t *p;
  
  p = (dk_bif_t *)ptr;
  if(p) {
    if(p->cf) {
      if((p->cf)->ydpi > 0.0) {
        back = 1;
      }
      if((p->cf)->xdpi > 0.0) {
        back = 1;
      }
    }
  } 
  return back;
}



double
dkbif_get_sf_x DK_P1(dk_bif_ptr,ptr)
{
  double back = -1.0;
  int me = 0;
  dk_bif_t *p;
  p = (dk_bif_t *)ptr;
  if(p) {
    if(p->cf) {
      if((p->cf)->xdpi > 0.0) {
        back = dkma_div_double_ok(72.0, (p->cf)->xdpi, &me);
      }
    }
  }
  return back;
}



double
dkbif_get_sf_y DK_P1(dk_bif_ptr,ptr)
{
  double back = -1.0;
  int me = 0;
  dk_bif_t *p;
  p = (dk_bif_t *)ptr;
  if(p) {
    if(p->cf) {
      if((p->cf)->ydpi > 0.0) {
        back = dkma_div_double_ok(72.0, (p->cf)->ydpi, &me);
      }
    }
  }
  return back;
}



void
dkbif_set_width_height_sufficient DK_P2(dk_bif_ptr,ptr, int,flagvalue)
{
  dk_bif_t *p;
  if(ptr) {
    p = (dk_bif_t *)ptr;
    p->whs = (flagvalue ? 0x01 : 0x00);
  }
}



int
dkbif_can_handle DK_P1(int,t)
{
  int back = 0;
  switch(t) {
    case DKBIF_TYPE_PNG: {
#if DK_HAVE_PNG_H
	back = 1;
#endif
    } break;
    case DKBIF_TYPE_JPG: {
#if DK_HAVE_JPEGLIB_H
	back = 1;
#endif
    } break;
    case DKBIF_TYPE_NETPBM: {
#if DK_HAVE_PNM_H || DK_HAVE_NETPBM_PNM_H
	back = 1;
#endif
    } break;
    case DKBIF_TYPE_TIFF: {
#if DK_HAVE_TIFF_H
	back = 1;
#endif
    } break;
  }
  return back;
}



int
dkbif_can_handle_name DK_P1(char *,filename)
{
  int back = 0; int t = -1;
  t = dkbif_get_type(filename);
  back = dkbif_can_handle(t);
  return back;
}



int
dkbif_get_predictor DK_P1(dk_bif_ptr,p)
{
  int back = DKBIF_PREDICTOR_NONE;
  dk_bif_t *ptr;
  ptr = (dk_bif_t *)p;
  if(ptr) {
    switch(ptr->t) {
      case DKBIF_TYPE_PNG: {
#if DK_HAVE_PNG_H
        int ft = 0;
	ft = png_get_filter_type((ptr->d).png.pp, (ptr->d).png.pi);
#endif
      } break;
      case DKBIF_TYPE_TIFF: {
#if DK_HAVE_TIFF_H
	uint16 res;
	if(TIFFGetField((ptr->d).tif.tiff, TIFFTAG_PREDICTOR, &res) == 1) {
#ifdef PREDICTOR_HORIZONTAL
	  if(res == PREDICTOR_HORIZONTAL) {
#endif
	    back = DKBIF_PREDICTOR_PIXEL_SUB;
#ifdef PREDICTOR_HORIZONTAL
	  }
#endif
	}
#endif
      } break;
    }
  }
  return back;
}




