// Copyright (c) 2005-2008 ASCLEPIOS Project, INRIA Sophia-Antipolis (France)
// All rights reserved.
//
// This file is part of the ImageIO Library, and as been adapted for
// CGAL (www.cgal.org).
// You can redistribute it and/or  modify it under the terms of the
// GNU Lesser General Public License as published by the Free Software Foundation;
// either version 3 of the License, or (at your option) any later version.
//
// Licensees holding a valid commercial license may use this file in
// accordance with the commercial license agreement provided with the software.
//
// These files are provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
//
// $URL$
// $Id$
//
//
// Author(s)     :  ASCLEPIOS Project (INRIA Sophia-Antipolis), Laurent Rineau

/*
 * xviris.c - load routine for IRIS 'rgb' format pictures
 *
 * LoadIRIS()
 * WriteIRIS()
 */

#ifdef CGAL_HEADER_ONLY
#define CGAL_INLINE_FUNCTION inline
#else
#define CGAL_INLINE_FUNCTION
#endif

#include <stdio.h>
#include <string.h>
#include <math.h>



/** Magic header for RGB files */
#define IRIS_MAGIC 0732



typedef unsigned char byte;

#define BPPMASK			0x00ff
#define ITYPE_VERBATIM		0x0000
#define ITYPE_RLE		0x0100
#define ISRLE(type)		(((type) & 0xff00) == ITYPE_RLE)
#define ISVERBATIM(type)	(((type) & 0xff00) == ITYPE_VERBATIM)
#define BPP(type)		((type) & BPPMASK)
#define RLE(bpp)		(ITYPE_RLE | (bpp))
#define VERBATIM(bpp)		(ITYPE_VERBATIM | (bpp))






#define TAGLEN	(5)

#define RINTLUM (79)
#define GINTLUM (156)
#define BINTLUM (21)

#define OFFSET_R	3	/* this is byte order dependent */
#define OFFSET_G	2
#define OFFSET_B	1
#define OFFSET_A	0

#define ILUM(r,g,b)     ((int)(RINTLUM*(r)+GINTLUM*(g)+BINTLUM*(b))>>8)
#define CHANOFFSET(z)	(3-(z))	/* this is byte order dependent */

static byte    *getimagedata  (const _image *im, unsigned short, int, int, int);
static void     interleaverow (byte *, byte *, int, int);
static void     expandrow     (byte *, byte *, int);
static void     readtab       (const _image *im, unsigned long *, int);
static void     addimgtag     (byte *, int, int);


static unsigned short  getshort      (const _image *im);
static unsigned long   getlong       (const _image *im);


CGAL_INLINE_FUNCTION
int testIrisHeader(char *magic,const char *) {
  if((((unsigned char *)magic)[0]<<8) + ((unsigned char *)magic)[1]
     == IRIS_MAGIC)
    return 0;
  else 
    return -1;
}

CGAL_INLINE_FUNCTION
PTRIMAGE_FORMAT createIrisFormat() {
  PTRIMAGE_FORMAT f=(PTRIMAGE_FORMAT) ImageIO_alloc(sizeof(IMAGE_FORMAT));

  f->testImageFormat=&testIrisHeader;
  f->readImageHeader=&readIrisImage;
  f->writeImage=0;
  strcpy(f->fileExtension,".rgb");
  strcpy(f->realName,"IRIS");
  return f;
}



/******************************************/
static void interleaverow(byte *lptr, byte *cptr, int z, int n)
{
  lptr += z;
  while(n--) {
    *lptr = *cptr++;
    lptr += 4;
  }
}


/******************************************/
static void expandrow(byte *optr, byte *iptr, int z)
{
  byte pixel, count;

  optr += z;
  while (1) {
    pixel = *iptr++;
    if ( !(count = (pixel & 0x7f)) ) return;
    if (pixel & 0x80) {
      while (count>=8) {
	optr[0*4] = iptr[0];
	optr[1*4] = iptr[1];
	optr[2*4] = iptr[2];
	optr[3*4] = iptr[3];
	optr[4*4] = iptr[4];
	optr[5*4] = iptr[5];
	optr[6*4] = iptr[6];
	optr[7*4] = iptr[7];
	optr += 8*4;
	iptr += 8;
	count = byte(count - 8);
      }
      while(count--) {
	*optr = *iptr++;
	optr+=4;
      }
    }
    else {
      pixel = *iptr++;
      while(count>=8) {
	optr[0*4] = pixel;
	optr[1*4] = pixel;
	optr[2*4] = pixel;
	optr[3*4] = pixel;
	optr[4*4] = pixel;
	optr[5*4] = pixel;
	optr[6*4] = pixel;
	optr[7*4] = pixel;
	optr += 8*4;
	count = byte(count - 8);
      }
      while(count--) {
	*optr = pixel;
	optr+=4;
      }
    }
  }
}

/****************************************************/
static void readtab(const _image *im, unsigned long *tab, int n)
{
  while (n) {
    *tab++ = getlong(im);
    n--;
  }
}


/*****************************************************/
static void addimgtag(byte *dptr, int xsize, int ysize)
{
  /* this is used to extract image data from core dumps. 
     I doubt this is necessary...  --jhb */

  dptr    = dptr + (xsize * ysize * 4);
  dptr[0] = 0x12;  dptr[1] = 0x34;  dptr[2] = 0x56;  dptr[3] = 0x78;
  dptr += 4;

  dptr[0] = 0x59;  dptr[1] = 0x49;  dptr[2] = 0x33;  dptr[3] = 0x33;
  dptr += 4;

  dptr[0] = 0x69;  dptr[1] = 0x43;  dptr[2] = 0x42;  dptr[3] = 0x22;
  dptr += 4;

  dptr[0] = byte((xsize>>24)&0xff);
  dptr[1] = byte((xsize>>16)&0xff);
  dptr[2] = byte((xsize>> 8)&0xff);
  dptr[3] = byte((xsize    )&0xff);
  dptr += 4;

  dptr[0] = byte((ysize>>24)&0xff);
  dptr[1] = byte((ysize>>16)&0xff);
  dptr[2] = byte((ysize>> 8)&0xff);
  dptr[3] = byte((ysize    )&0xff);
}

/* byte order independent read/write of shorts and longs. */
/*****************************************************/
static unsigned short getshort( const _image *im)
{
  byte buf[2];
  ImageIO_read( im, buf, (size_t) 2);
  return (unsigned short)((buf[0]<<8)+(buf[1]<<0));
}

/*****************************************************/
static unsigned long getlong( const _image *im )
{
  byte buf[4];
  ImageIO_read( im, buf, (size_t) 4);
  return (((unsigned long) buf[0])<<24) + (((unsigned long) buf[1])<<16)
       + (((unsigned long) buf[2])<<8) + buf[3];
}

/*****************************************************/
CGAL_INLINE_FUNCTION
int readIrisImage( const char *, _image *im ) {
  byte   *rawdata, *rptr;
  byte   *pic824,  *bptr, *iptr;
  std::size_t i, j, size;
  unsigned short imagic, type;
  unsigned int xsize, ysize, zsize;


  /* read header information from file */
  imagic = getshort( im );
  type   = getshort( im );
  getshort( im );
  xsize  = getshort( im );
  ysize  = getshort( im );
  zsize  = getshort( im );

  if ( ImageIO_error(im) ) return( 0 );

  if (imagic != IRIS_MAGIC) return( 0 );

  rawdata = getimagedata(im, type, xsize, ysize, zsize);
  if (!rawdata) return( 0 );

  if ( ImageIO_error(im) ) return( 0 );   /* probably truncated file */


  /*  t1=texture_alloc();
  (void) strcpy(t1->name,fname);
  t1->type=IRIS;
  
  t1->nrows = ysize;
  t1->ncols = xsize;
    
  t1->image = create_int_array(t1->nrows,t1->ncols);*/

  /* got the raw image data.  Convert to an XV image (1,3 bytes / pix) */
  if (zsize < 3) 
    {  /* grayscale */
      im->xdim = xsize;
      im->ydim = ysize;
      im->zdim = 1;
      im->vdim = 1;
      im->wdim = 1;
      im->wordKind = WK_FIXED;
      im->sign = SGN_UNSIGNED;
      im->data = ImageIO_alloc(xsize * ysize);

      pic824 = (byte *) ImageIO_alloc((size_t) xsize * ysize);
      if (!pic824) exit(-1);
      
      /* copy plane 3 from rawdata into pic824, inverting pic vertically */
      for (i = 0, bptr = pic824; i < ysize; i++)
	{
	  rptr = rawdata + 3 + ((ysize - 1) - i) * (xsize * 4);
	  for (j = 0; j < xsize; j++, bptr++, rptr += 4)
	    *bptr = *rptr;
	}
      
      
      size = im->xdim * im->ydim;
      for (bptr = pic824, iptr = (unsigned char *) im->data,
	     i = 0; i < size; ++i, ++iptr, ++bptr)
	{
	  *iptr = *bptr;
	}
      
      ImageIO_free(pic824);
    
  }

  else {  /* truecolor */
    im->xdim = xsize;
    im->ydim = ysize;
    im->zdim = zsize / 3;
    im->vdim = 4;
    im->wdim = 1;
    im->wordKind = WK_FIXED;
    im->sign = SGN_UNSIGNED;
    im->data = ImageIO_alloc(xsize * ysize * im->zdim * 4);

    pic824 = (byte *) ImageIO_alloc((size_t) xsize * ysize * 3);
    if (!pic824) 
      exit(1);
    
    /* copy plane 3 from rawdata into pic824, inverting pic vertically */
    for (i = 0, bptr = pic824; i< ysize; i++)
      {
	rptr = rawdata + ((ysize - 1) - i) * (xsize * 4);

	for (j=0; j< xsize; j++, rptr += 4) {
	  *bptr++ = rptr[3];
	  *bptr++ = rptr[2];
	  *bptr++ = rptr[1];
	}
      }
    
    
    size = im->xdim * im->ydim;
    for (bptr = pic824, iptr = (unsigned char *) im->data, i = 0;
	 i < size; ++i, iptr += 4, bptr += 3)
      {
	if ( _getEndianness() == END_LITTLE ) {
	  iptr[0] = 0xFF;
	  iptr[1] = bptr[2];
	  iptr[2] = bptr[1];
	  iptr[3] = bptr[0];
	}
	else {
	  iptr[0] = bptr[0];
	  iptr[1] = bptr[1];
	  iptr[2] = bptr[2];
	  iptr[3] = 0xFF;
	}
      }
      ImageIO_free(pic824);
  }

  ImageIO_free(rawdata);

  return 1;
}     




/****************************************************/
static byte *getimagedata(const _image *im, unsigned short type, int xsize, int ysize,
			  int zsize)
{
  /* read in a B/W RGB or RGBA iris image file and return a 
     pointer to an array of 4-byte pixels, arranged ABGR, NULL on error */

  byte   *base, *lptr;
  byte   *verdat;
  int     y, z, tablen;
  int     bpp, rle, badorder;
  int     rlebuflen;
  byte *rledat;
  unsigned long *starttab, *lengthtab, cur;


  rle     = ISRLE(type);
  bpp     = BPP(type);

  if (bpp != 1) {
    return (byte *) NULL;
  }

  if (rle) {

    rlebuflen = 2 * xsize + 10;
    tablen    = ysize * zsize;
    starttab  = (unsigned long *) ImageIO_alloc((size_t) tablen * sizeof(long));
    lengthtab = (unsigned long *) ImageIO_alloc((size_t) tablen * sizeof(long));
    rledat    = (byte *)   ImageIO_alloc((size_t) rlebuflen);

    if (!starttab || !lengthtab || !rledat) 
      exit(1);

    ImageIO_seek( im, 512L, SEEK_SET );
    readtab(im, starttab,  tablen);
    readtab(im, lengthtab, tablen);

    if ( ImageIO_error(im) ) {
      ImageIO_free(starttab);  ImageIO_free(lengthtab);  ImageIO_free(rledat);
      return (byte *) NULL;
    }


    /* check data order */
    cur = 0;
    badorder = 0;
    for (y=0; y<ysize && !badorder; y++) {
      for (z=0; z<zsize && !badorder; z++) {
	if (starttab[y+z*ysize] < cur) badorder = 1;
	else cur = starttab[y+z*ysize];
      }
    }

    ImageIO_seek( im, (long) (512 + 2*tablen*4), 0);

    cur = 512 + 2*tablen*4;

    base = (byte *) ImageIO_alloc((size_t) (xsize*ysize+TAGLEN) * 4);
    if (!base) 
      exit(1);

    addimgtag(base,xsize,ysize);

    if (badorder) {
      for (z=0; z<zsize; z++) {
	lptr = base;
	for (y=0; y<ysize; y++) {
	  if (cur != starttab[y+z*ysize]) {
	    ImageIO_seek( im, (long) starttab[y+z*ysize], 0);
	    cur = starttab[y+z*ysize];
	  }

	  if (lengthtab[y+z*ysize]>(unsigned long)rlebuflen) {
	    ImageIO_free(starttab); ImageIO_free(lengthtab); ImageIO_free(rledat); ImageIO_free(base);
	    return (byte *) NULL;
	  }
	  ImageIO_read(im, rledat, (size_t) lengthtab[y+z*ysize]);
	  cur += lengthtab[y+z*ysize];
	  expandrow(lptr,rledat,3-z);
	  lptr += (xsize * 4);
	}
      }
    }
    else {
      lptr = base;
      for (y=0; y<ysize; y++) {
	for (z=0; z<zsize; z++) {
	  if (cur != starttab[y+z*ysize]) {
	    ImageIO_seek( im, (long) starttab[y+z*ysize], 0);
	    cur = starttab[y+z*ysize];
	  }
	  
	  ImageIO_read(im, rledat, (size_t) lengthtab[y+z*ysize]);
	  cur += lengthtab[y+z*ysize];
	  expandrow(lptr,rledat,3-z);
	}
	lptr += (xsize * 4);
      }
    }

    ImageIO_free(starttab);
    ImageIO_free(lengthtab);
    ImageIO_free(rledat);
    return base;
  }      /* end of RLE case */

  else {  /* not RLE */
    verdat = (byte *) ImageIO_alloc((size_t) xsize);
    base   = (byte *) ImageIO_alloc((size_t) (xsize*ysize+TAGLEN) * 4);
    if (!base || !verdat) 
      exit(1);

    addimgtag(base,xsize,ysize);

    ImageIO_seek( im, 512L,0);

    for (z=0; z<zsize; z++) {
      lptr = base;
      for (y=0; y<ysize; y++) {
	ImageIO_read(im, verdat, (size_t) xsize);
	interleaverow(lptr,verdat,3-z,xsize);
	lptr += (xsize * 4);
      }
    }

    ImageIO_free(verdat);
    return base;
  }
}
















