/******************************************************************************
 *
 * Project:  MapServer
 * Purpose:  msDrawRasterLayer(): generic raster layer drawing, including
 *           implementation of non-GDAL raster layer renderers.
 * Author:   Steve Lime 
 *           Frank Warmerdam, warmerdam@pobox.com
 ******************************************************************************
 * Copyright (c) 1996-2004 Regents of the University of Minnesota.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 ******************************************************************************
 *
 * $Log: mapraster.c,v $
 * Revision 1.123  2004/11/15 18:55:49  frank
 * added "experimental" drawSDE support.
 *
 * Revision 1.122  2004/11/05 03:08:23  frank
 * Cleanup layer level leaks under various error conditions.
 * http://mapserver.gis.umn.edu/bugs/show_bug.cgi?id=713
 *
 * Revision 1.121  2004/10/21 04:30:56  frank
 * Added standardized headers.  Added MS_CVSID().
 *
 * Revision 1.120  2004/07/29 18:16:44  sdlime
 * Fixed an error with eppl7 code where row/col access to the GD pixels was reversed.
 *
 * Revision 1.119  2004/07/23 12:57:18  frank
 * improved header purpose text
 *
 * Revision 1.118  2004/06/15 16:08:52  dan
 * Fixed problem with tiled raster layers if there is no tile in the current
 * view (bug 729).
 *
 * Revision 1.117  2004/06/01 14:33:43  frank
 * Fixed bug 698 - filename from tileindex freed before use.
 *
 * Revision 1.116  2004/05/28 18:34:34  frank
 * removed drawERD() ... now handled by GDAL
 *
 * Revision 1.115  2004/05/28 05:12:34  sdlime
 * Fixed (I believe) bug 660, a memory leak in msDrawRasterLow()...
 *
 * Revision 1.114  2004/04/16 20:19:39  dan
 * Added try_addimage_if_notfound to msGetSymbolIndex() (bug 612)
 *
 * Revision 1.113  2004/04/06 18:49:37  frank
 * add function comment blocks to split things up a bit
 *
 * Revision 1.112  2004/04/06 06:44:59  sdlime
 * Working version of layer-based tiling for raster data. The layer does not have to be a shapefile.
 *
 * Revision 1.111  2004/03/31 14:44:48  frank
 * Added my standard headers so I can see the log messages.
 *
 */

#include <assert.h>
#include "map.h"
#include "mapresample.h"
#include "mapthread.h"

MS_CVSID("$Id: mapraster.c,v 1.123 2004/11/15 18:55:49 frank Exp $")

extern int msyyparse();
extern int msyylex();
extern char *msyytext;

extern int msyyresult; // result of parsing, true/false
extern int msyystate;
extern char *msyystring;

#ifdef USE_TIFF
#include <tiffio.h>
#include <tiff.h>
#endif

#ifdef USE_GDAL
#include "gdal.h"
#include "cpl_string.h"
#endif

#ifdef USE_EPPL
#include "epplib.h"
#endif

#ifdef USE_JPEG
#include "jpeglib.h"
#endif


#define MAXCOLORS 256
#define BUFLEN 1024
#define HDRLEN 8

#define	CVT(x) ((x) >> 8) /* converts to 8-bit color value */

#define NUMGRAYS 16

// FIX: need WBMP sig
unsigned char PNGsig[8] = {137, 80, 78, 71, 13, 10, 26, 10}; // 89 50 4E 47 0D 0A 1A 0A hex
unsigned char JPEGsig[3] = {255, 216, 255}; // FF D8 FF hex

/************************************************************************/
/*                             msGetClass()                             */
/************************************************************************/

int msGetClass(layerObj *layer, colorObj *color)
{
  int i;
  char *tmpstr1=NULL;
  char tmpstr2[12]; // holds either a single color index or something like 'rrr ggg bbb'
  
  if((layer->numclasses == 1) && !(layer->class[0].expression.string)) /* no need to do lookup */
    return(0);

  if(!color) return(-1);

  for(i=0; i<layer->numclasses; i++) {
    if (layer->class[i].expression.string == NULL) /* Empty expression - always matches */
      return(i);
    switch(layer->class[i].expression.type) {
    case(MS_STRING):
      sprintf(tmpstr2, "%d %d %d", color->red, color->green, color->blue);
      if(strcmp(layer->class[i].expression.string, tmpstr2) == 0) return(i); // matched
      sprintf(tmpstr2, "%d", color->pen);
      if(strcmp(layer->class[i].expression.string, tmpstr2) == 0) return(i); // matched
      break;
    case(MS_REGEX):
      if(!layer->class[i].expression.compiled) {
	if(regcomp(&(layer->class[i].expression.regex), layer->class[i].expression.string, REG_EXTENDED|REG_NOSUB) != 0) { // compile the expression 
	  msSetError(MS_REGEXERR, "Invalid regular expression.", "msGetClass()");
	  return(-1);
	}
	layer->class[i].expression.compiled = MS_TRUE;
      }

      sprintf(tmpstr2, "%d %d %d", color->red, color->green, color->blue);
      if(regexec(&(layer->class[i].expression.regex), tmpstr2, 0, NULL, 0) == 0) return(i); // got a match
      sprintf(tmpstr2, "%d", color->pen);
      if(regexec(&(layer->class[i].expression.regex), tmpstr2, 0, NULL, 0) == 0) return(i); // got a match
      break;
    case(MS_EXPRESSION):
      tmpstr1 = strdup(layer->class[i].expression.string);

      sprintf(tmpstr2, "%d", color->red);
      tmpstr1 = gsub(tmpstr1, "[red]", tmpstr2);
      sprintf(tmpstr2, "%d", color->green);
      tmpstr1 = gsub(tmpstr1, "[green]", tmpstr2);
      sprintf(tmpstr2, "%d", color->blue);
      tmpstr1 = gsub(tmpstr1, "[blue]", tmpstr2);

      sprintf(tmpstr2, "%d", color->pen);
      tmpstr1 = gsub(tmpstr1, "[pixel]", tmpstr2);

      msyystate = 4; msyystring = tmpstr1;
      if(msyyparse() != 0) return(-1); // error parsing the expression

      free(tmpstr1);

      if(msyyresult) return(i); // got a match	
    }
  }

  return(-1); /* not found */
}

/************************************************************************/
/*                          msGetClass_Float()                          */
/*                                                                      */
/*      Returns the class based on classification of a floating         */
/*      pixel value.                                                    */
/************************************************************************/

int msGetClass_Float(layerObj *layer, float fValue)
{
    int i;
    char *tmpstr1=NULL;
    char tmpstr2[100];
  
    if((layer->numclasses == 1) && !(layer->class[0].expression.string)) /* no need to do lookup */
        return(0);

    for(i=0; i<layer->numclasses; i++) {
        if (layer->class[i].expression.string == NULL) /* Empty expression - always matches */
            return(i);

        switch(layer->class[i].expression.type) {
          case(MS_STRING):
            sprintf(tmpstr2, "%18g", fValue );
            if(strcmp(layer->class[i].expression.string, tmpstr2) == 0) return(i); // matched
            break;

          case(MS_REGEX):
            if(!layer->class[i].expression.compiled) {
                if(regcomp(&(layer->class[i].expression.regex), layer->class[i].expression.string, REG_EXTENDED|REG_NOSUB) != 0) { // compile the expression 
                    msSetError(MS_REGEXERR, "Invalid regular expression.", "msGetClass()");
                    return(-1);
                }
                layer->class[i].expression.compiled = MS_TRUE;
            }

            sprintf(tmpstr2, "%18g", fValue );
            if(regexec(&(layer->class[i].expression.regex), tmpstr2, 0, NULL, 0) == 0) return(i); // got a match
            break;

          case(MS_EXPRESSION):
            tmpstr1 = strdup(layer->class[i].expression.string);

            sprintf(tmpstr2, "%18g", fValue);
            tmpstr1 = gsub(tmpstr1, "[pixel]", tmpstr2);

            msyystate = 4; msyystring = tmpstr1;
            if(msyyparse() != 0) return(-1); // error parsing the expression

            free(tmpstr1);

            if(msyyresult) return(i); // got a match	
        }
    }

    return(-1); /* not found */
}

/************************************************************************/
/*                            msAddColorGD()                            */
/*                                                                      */
/*      Function to add a color to an existing color map.  It first     */
/*      looks for an exact match, then tries to add it to the end of    */
/*      the existing color map, and if all else fails it finds the      */
/*      closest color.                                                  */
/************************************************************************/

int msAddColorGD(mapObj *map, gdImagePtr img, int cmt, int r, int g, int b)
{
  int c; 
  int ct = -1;
  int op = -1;
  long rd, gd, bd, dist;
  long mindist = 3*255*255;  /* init to max poss dist */

  if( gdImageTrueColor( img ) )
      return gdTrueColor( r, g, b );
  
  /*
  ** We want to avoid using a color that matches a transparent background
  ** color exactly.  If this is the case, we will permute the value slightly.
  ** When perterbing greyscale images we try to keep them greyscale, otherwise
  ** we just perterb the red component.
  */
  if( map->outputformat && map->outputformat->transparent 
      && map->imagecolor.red == r 
      && map->imagecolor.green == g 
      && map->imagecolor.blue == b )
  {
      if( r == 0 && g == 0 && b == 0 )
      {
          r = g = b = 1;
      }
      else if( r == g && r == b )
      {
          r = g = b = r-1;
      }
      else if( r == 0 )
      {
          r = 1;
      }
      else
      {
          r = r-1;
      }
  }

  /* 
  ** Find the nearest color in the color table.  If we get an exact match
  ** return it right away.
  */
  for (c = 0; c < img->colorsTotal; c++) {

    if (img->open[c]) {
      op = c; /* Save open slot */
      continue; /* Color not in use */
    }
    
    /* don't try to use the transparent color */
    if (map->outputformat && map->outputformat->transparent 
        && img->red  [c] == map->imagecolor.red
        && img->green[c] == map->imagecolor.green
        && img->blue [c] == map->imagecolor.blue )
        continue;

    rd = (long)(img->red  [c] - r);
    gd = (long)(img->green[c] - g);
    bd = (long)(img->blue [c] - b);
/* -------------------------------------------------------------------- */
/*      special case for grey colors (r=g=b). we will try to find       */
/*      either the nearest grey or a color that is almost grey.         */
/* -------------------------------------------------------------------- */
    if (r == g && r == b)
    {
        if (img->red == img->green && img->red ==  img->blue)
          dist = rd*rd;
        else
          dist = rd * rd + gd * gd + bd * bd;
    }
    else
      dist = rd * rd + gd * gd + bd * bd;

    if (dist < mindist) {
      if (dist == 0) {
	return c; /* Return exact match color */
      }
      mindist = dist;
      ct = c;
    }
  }

  /* no exact match, is the closest within our "color match threshold"? */
  if( mindist <= cmt*cmt )
      return ct;

  /* no exact match.  If there are no open colors we return the closest
     color found.  */
  if (op == -1) {
    op = img->colorsTotal;
    if (op == gdMaxColors) { /* No room for more colors */
      return ct; /* Return closest available color */
    }
    img->colorsTotal++;
  }

  /* allocate a new exact match */
  img->red  [op] = r;
  img->green[op] = g;
  img->blue [op] = b;
  img->open [op] = 0;

  return op; /* Return newly allocated color */  
}

/************************************************************************/
/*                           readWorldFile()                            */
/*                                                                      */
/*      Function to read georeferencing information for an image        */
/*      from an ESRI world file.                                        */
/************************************************************************/

static int readWorldFile(char *filename, double *ulx, double *uly, double *cx, double *cy) {
  FILE *stream;
  char *wld_filename;
  int i=0;
  char buffer[BUFLEN];

  wld_filename = strdup(filename);

  strcpy(strrchr(wld_filename, '.'), ".wld");
  stream = fopen(wld_filename, "r");
  if(!stream) {
    strcpy(strrchr(wld_filename, '.'), ".tfw");
    stream = fopen(wld_filename, "r");
    if(!stream) {
      strcpy(strrchr(wld_filename, '.'), ".jgw");
      stream = fopen(wld_filename, "r");
      if(!stream) {
        strcpy(strrchr(wld_filename, '.'), ".gfw");
        stream = fopen(wld_filename, "r");
        if(!stream) {	
	  msSetError(MS_IOERR, "Unable to open world file for reading.", "readWorldFile()");
	  free(wld_filename);
	  return(-1);
        }
      }
    }
  }

  while(fgets(buffer, BUFLEN, stream)) {    
    switch(i) {
    case 0:
      *cx = atof(buffer);
      break;
    case 3:
      *cy = MS_ABS(atof(buffer));
      break;
    case 4:
      *ulx = atof(buffer);
      break;
    case 5:
      *uly = atof(buffer);
      break;
    default:
      break;
    }

    i++;
  }
    
  fclose(stream);
  free(wld_filename);

  return(0);
}

/************************************************************************/
/*                            readGEOTiff()                             */
/*                                                                      */
/*      read georeferencing info from geoTIFF header, if it exists      */
/************************************************************************/

#ifdef USE_TIFF 
static int readGEOTiff(TIFF *tif, double *ulx, double *uly, double *cx, double *cy)
{
  short entries;
  int i,fpos,swap,tiepos,cellpos;
  double tie[6],cell[6];
  TIFFDirEntry tdir;
  FILE *f;
  
  swap=TIFFIsByteSwapped(tif);
  fpos=TIFFCurrentDirOffset(tif);
  f=fopen((char*)TIFFFileName(tif),"rb");
  if (f==NULL) return(-1);
  fseek(f,fpos,0);
  fread(&entries,2,1,f);
  if (swap) TIFFSwabShort(&entries);
  tiepos=0; cellpos=0;
  for (i=0; i<entries; i++) {
    fread(&tdir,sizeof(tdir),1,f);
    if (swap) TIFFSwabShort(&tdir.tdir_tag);
    if (tdir.tdir_tag==0x8482) {  /* geoTIFF tie point */
      tiepos=tdir.tdir_offset;
      if (swap) TIFFSwabLong((long *)&tiepos);
      }
    if (tdir.tdir_tag==0x830e) {  /* geoTIFF cell size */
      cellpos=tdir.tdir_offset;
      if (swap) TIFFSwabLong((long *)&cellpos);
      }
    }
  if (tiepos==0 || cellpos==0) {
    fclose(f);
    return(-1);    
  }
  fseek(f,tiepos,0);
  fread(tie,sizeof(double),6,f);
  if (swap) TIFFSwabArrayOfDouble(tie,6);
  fseek(f,cellpos,0);
  fread(cell,sizeof(double),3,f);
  if (swap) TIFFSwabArrayOfDouble(cell,3);
  *cx=fabs(cell[0]);  *cy=fabs(cell[1]);
  *ulx=tie[3]-(tie[0]*cell[0]);
  *uly=tie[4]+(tie[1]*cell[1]);
  fclose(f);
  return(0);
}
#endif  
  
/************************************************************************/
/*                              drawTIFF()                              */
/************************************************************************/
static int drawTIFF(mapObj *map, layerObj *layer, gdImagePtr img, char *filename) 
{
#ifdef USE_TIFF
  int i,j; /* loop counters */

  double x,y;
  TIFF *tif=NULL; 
  unsigned char *buf; 
  long w, h;
  short type, nbits;
  unsigned short compression;
  unsigned short *red, *green, *blue;
  int cmap[MAXCOLORS];

  colorObj pixel;

  double startx, starty; /* this is where we start out reading */
  int st,strip;  /* current strip */
  int xi,yi,startj,endj,boffset,vv;

  double ulx, uly; /* upper left-hand coordinates */
  double skipx,skipy; /* skip factors (x and y) */  
  double cx,cy; /* cell sizes (x and y) */
  long rowsPerStrip;    

  char szPath[MS_MAXPATHLEN];

  if( layer->debug )
      msDebug( "drawTIFF(%s): entering\n", layer->name );

  for(i=0; i<MAXCOLORS; i++) cmap[i] = -1; // initialize the colormap to all transparent

  TIFFSetWarningHandler(NULL); // can these be directed to the mapserver error functions?
  TIFFSetErrorHandler(NULL);

  tif = TIFFOpen(msBuildPath3(szPath, map->mappath, map->shapepath, filename), "rb");
  if(!tif) {
    msSetError(MS_IMGERR, "Error loading TIFF image.", "drawTIFF()");
    return(-1);
  }
  
  if(readGEOTiff(tif, &ulx, &uly, &cx, &cy ) != 0) {
    if(readWorldFile(msBuildPath3(szPath,map->mappath,map->shapepath,filename),
                     &ulx, &uly, &cx, &cy) != 0) {
      TIFFClose(tif);
      return(-1);
    }   
  }

  skipx = map->cellsize/cx;
  skipy = map->cellsize/cy;
  startx = (map->extent.minx - ulx)/cx;
  starty = (uly - map->extent.maxy)/cy;

  TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &w);
  TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &h);

  TIFFGetField(tif, TIFFTAG_BITSPERSAMPLE, &nbits);
  if(nbits != 8 && nbits != 4 && nbits != 1) {
    msSetError(MS_IMGERR, "Only 1, 4, and 8-bit images are supported.", "drawTIFF()");
    TIFFClose(tif);
    return(-1); 
  }

  TIFFGetField(tif, TIFFTAG_ROWSPERSTRIP, &rowsPerStrip);
  TIFFGetField(tif, TIFFTAG_COMPRESSION, &compression);

  TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &type); /* check image type */
  switch(type) {  
  case(PHOTOMETRIC_PALETTE):    
    TIFFGetField(tif, TIFFTAG_COLORMAP, &red, &green, &blue);
    if(layer->numclasses > 0) {
      int c;

      for(i=0; i<MAXCOLORS; i++) {

	pixel.red = CVT(red[i]);
	pixel.green = CVT(green[i]);
	pixel.blue = CVT(blue[i]);
	pixel.pen = i;

	if(!MS_COMPARE_COLORS(pixel, layer->offsite)) {	  
	  c = msGetClass(layer, &pixel);
	  
	  if(c == -1) /* doesn't belong to any class, so handle like offsite */
	    cmap[i] = -1;
	  else {
            RESOLVE_PEN_GD(img, layer->class[c].styles[0].color);
	    if(MS_VALID_COLOR(layer->class[c].styles[0].color)) 
	      cmap[i] = msAddColorGD(map,img, 0, layer->class[c].styles[0].color.red, layer->class[c].styles[0].color.green, layer->class[c].styles[0].color.blue); // use class color
	    else if(MS_TRANSPARENT_COLOR(layer->class[c].styles[0].color)) 
	      cmap[i] = -1; // make transparent
	    else
              cmap[i] = msAddColorGD(map,img, 0, pixel.red, pixel.green, pixel.blue); // use raster color
	  }
	} 
      }
    } else {
      for(i=0; i<MAXCOLORS; i++) {

	pixel.red = CVT(red[i]);
	pixel.green = CVT(green[i]);
	pixel.blue = CVT(blue[i]);
	pixel.pen = i;

	if(!MS_COMPARE_COLORS(pixel, layer->offsite))
	  cmap[i] = msAddColorGD(map,img, 0, pixel.red, pixel.green, pixel.blue); // use raster color        
      }
    }
    break;
  case PHOTOMETRIC_MINISBLACK: /* classes are NOT honored for non-colormapped data */
    if (nbits==1) {
      for (i=0; i<2; i++) {
	pixel.red = pixel.green = pixel.blue = pixel.pen = i; // offsite would be specified as 0 or 1

	if(!MS_COMPARE_COLORS(pixel, layer->offsite))
          cmap[i]=msAddColorGD(map,img, 0, i*255,i*255,i*255); // use raster color, stretched to use entire grayscale range
      }      
    } else { 
      if (nbits==4) {	
	for (i=0; i<16; i++) {
	  pixel.red = pixel.green = pixel.blue = pixel.pen = i; // offsite would be specified in range 0 to 15

	  if(!MS_COMPARE_COLORS(pixel, layer->offsite))
	    cmap[i] = msAddColorGD(map,img,0, i*17, i*17, i*17); // use raster color, stretched to use entire grayscale range	  
	}
      } else { /* 8-bit */
	for (i=0; i<256; i++) {
	  pixel.red = pixel.green = pixel.blue = pixel.pen = i; // offsite would be specified in range 0 to 255

	  if(!MS_COMPARE_COLORS(pixel, layer->offsite))	    
	    cmap[i] = msAddColorGD(map,img, 0, (i>>4)*17, (i>>4)*17, (i>>4)*17); // use raster color
	}
      }
    }
    break;
  case PHOTOMETRIC_MINISWHITE: /* classes are NOT honored for non-colormapped data */
    if (nbits==1) {
      for (i=0; i<2; i++) {
	pixel.red = pixel.green = pixel.blue = pixel.pen = i; // offsite would be specified as 0 or 1

	if(!MS_COMPARE_COLORS(pixel, layer->offsite))
	  cmap[i]=msAddColorGD(map,img, 0, i*255,i*255,i*255); // use raster color, stretched to use entire grayscale range
      }      
    }
    else {
      msSetError(MS_IMGERR,"Can't do inverted grayscale images","drawTIFF()");
      TIFFClose(tif);
      return(-1);
    }
    break;
  default:
    msSetError(MS_IMGERR, "Only colormapped and grayscale images are supported.", "drawTIFF()");
    TIFFClose(tif);
    return(-1);
  }

  buf = (unsigned char *)_TIFFmalloc(TIFFStripSize(tif)); /* allocate strip buffer */
  
  /* 
  ** some set-up;  the startx calculation is problematical analytically so we
  ** find it iteratively 
  */
  x=startx;
  for (j=0; j<img->sx && MS_NINT(x)<0; j++) x+=skipx;
  startx=x; startj=j;
  for (j=startj; j<img->sx && MS_NINT(x)<w; j++) x+=skipx;
  endj=j;
  y=starty;
  strip=-1;
  for(i=0; i<img->sy; i++) { /* for each row */
    yi=MS_NINT(y);
    if((yi >= 0) && (yi < h)) {
      st=TIFFComputeStrip(tif,yi,0);
      if (st!=strip) {
        TIFFReadEncodedStrip(tif, st, buf, TIFFStripSize(tif));
        strip=st;
      }  
      x = startx;

      switch (nbits) {
      case 8:
        boffset=(yi % rowsPerStrip) * w;
        for(j=startj; j<endj; j++) {    
	  xi=MS_NINT(x);
	  vv=buf[xi+boffset];  
	  
	  if (cmap[vv] != -1) 
	    img->pixels[i][j] = cmap[vv];
	  
	  x+=skipx;
        }
        break;
      case 4:
        boffset=(yi % rowsPerStrip) * ((w+1) >> 1);
        for(j=startj; j<endj; j++) {
          xi=MS_NINT(x);
          vv=(buf[(xi >> 1) + boffset] >> (4*((yi+1) & 1))) & 15;

	  if (cmap[vv] != -1) 
	    img->pixels[i][j] = cmap[vv];

	  x+=skipx;
	}  
        break;  
      case 1:
        boffset=(yi % rowsPerStrip) * ((w+7) >> 3);
        for(j=startj; j<endj; j++) {
          xi=MS_NINT(x);    
          vv=(buf[(xi >> 3) + boffset] >> (7-(xi & 7))) & 1;         

	  if (cmap[vv] != -1) 
	    img->pixels[i][j] = cmap[vv];

	  x+=skipx;
	}  
        break; 
      }   
    }
    y+=skipy;  
  }
  _TIFFfree(buf);

  TIFFClose(tif);
  return(0);
#else
   msSetError(MS_IMGERR, "TIFF support is not available.", "drawTIFF()");
   return(-1);
#endif
} 

/************************************************************************/
/*                              drawPNG()                               */
/************************************************************************/
static int drawPNG(mapObj *map, layerObj *layer, gdImagePtr img, char *filename) 
{
#ifdef USE_GD_PNG
  int i,j; /* loop counters */
  double x,y;
  int cmap[MAXCOLORS];
  double w, h;

  FILE *pngStream;
  gdImagePtr png;

  int vv;

  colorObj pixel;

  double startx, starty; /* this is where we start out reading */

  double ulx, uly; /* upper left-hand coordinates */
  double skipx,skipy; /* skip factors (x and y) */  
  double cx,cy; /* cell sizes (x and y) */
  char szPath[MS_MAXPATHLEN];

  for(i=0; i<MAXCOLORS; i++) cmap[i] = -1; // initialize the colormap to all transparent

  pngStream = fopen(msBuildPath3(szPath, map->mappath, map->shapepath, filename), "rb");
  if(!pngStream) {
    msSetError(MS_IOERR, "Error open image file.", "drawPNG()");
    return(-1);
  }

  png = gdImageCreateFromPng(pngStream);
  fclose(pngStream);

  if(!png) {
    msSetError(MS_IMGERR, "Error loading PNG file.", "drawPNG()");
    return(-1);
  }

  w = gdImageSX(png) - .5; /* to avoid rounding problems */
  h = gdImageSY(png) - .5;

  if(layer->transform) {
    if(readWorldFile(msBuildPath3(szPath, map->mappath,map->shapepath,filename), 
                     &ulx, &uly, &cx, &cy) != 0)
      return(-1);

    skipx = map->cellsize/cx;
    skipy = map->cellsize/cy;
    startx = (map->extent.minx - ulx)/cx;
    starty = (uly - map->extent.maxy)/cy;
  } else {
    skipx = skipy = 1;
    startx = starty = 0;    
  }

  if(layer->numclasses > 0) {
    int c;

    for(i=0; i<gdImageColorsTotal(png); i++) {

      pixel.red = gdImageRed(png,i);
      pixel.green = gdImageGreen(png,i);
      pixel.blue = gdImageBlue(png,i);
      pixel.pen = i; 

      if(!MS_COMPARE_COLORS(pixel, layer->offsite) && i != gdImageGetTransparent(png)) {	
	c = msGetClass(layer, &pixel);

	if(c == -1) /* doesn't belong to any class, so handle like offsite */
	  cmap[i] = -1;
	else {
          RESOLVE_PEN_GD(img, layer->class[c].styles[0].color);
          if(MS_VALID_COLOR(layer->class[c].styles[0].color)) 
	    cmap[i] = msAddColorGD(map,img, 0, layer->class[c].styles[0].color.red, layer->class[c].styles[0].color.green, layer->class[c].styles[0].color.blue); // use class color
	  else if(MS_TRANSPARENT_COLOR(layer->class[c].styles[0].color)) 
	    cmap[i] = -1; // make transparent
	  else
            cmap[i] = msAddColorGD(map,img, 0, pixel.red, pixel.green, pixel.blue); // use raster color	  
	}
      }
    }
  } else {
    for(i=0; i<gdImageColorsTotal(png); i++) {

      pixel.red = gdImageRed(png,i);
      pixel.green = gdImageGreen(png,i);
      pixel.blue = gdImageBlue(png,i);
      pixel.pen = i;

      if(!MS_COMPARE_COLORS(pixel, layer->offsite) && i != gdImageGetTransparent(png)) 
	cmap[i] = msAddColorGD(map,img, 0, pixel.red, pixel.green, pixel.blue);
    }
  }

  y=starty;
  for(i=0; i<img->sy; i++) { /* for each row */
    if((y >= -0.5) && (y < h)) {
      x = startx;
      for(j=0; j<img->sx; j++) {
	if((x >= -0.5) && (x < w)) {
	  vv = png->pixels[(y == -0.5)?0:MS_NINT(y)][(x == -0.5)?0:MS_NINT(x)];	  
	  if(cmap[vv] != -1)
	    img->pixels[i][j] = cmap[vv];
	}
	x+=skipx;
      }
    }
    y+=skipy;  
  }

  gdImageDestroy(png);
  return(0);
#else
  msSetError(MS_IMGERR, "PNG support is not available.", "drawPNG()");
  return(-1);
#endif
}

/************************************************************************/
/*                              drawGIF()                               */
/************************************************************************/
static int drawGIF(mapObj *map, layerObj *layer, gdImagePtr img, char *filename) 
{
#ifdef USE_GD_GIF
  int i,j; /* loop counters */
  double x,y;
  int cmap[MAXCOLORS];
  double w, h;

  FILE *gifStream;
  gdImagePtr gif;

  int vv;
 
  colorObj pixel;

  double startx, starty; /* this is where we start out reading */

  double ulx, uly; /* upper left-hand coordinates */
  double skipx,skipy; /* skip factors (x and y) */  
  double cx,cy; /* cell sizes (x and y) */
  char szPath[MS_MAXPATHLEN];

  for(i=0; i<MAXCOLORS; i++) cmap[i] = -1; // initialize the colormap to all transparent

  gifStream = fopen(msBuildPath3(szPath, map->mappath, map->shapepath, filename), "rb");
  if(!gifStream) {
    msSetError(MS_IOERR, "Error open image file.", "drawGIF()");
    return(-1);
  }

  gif = gdImageCreateFromGif(gifStream);
  fclose(gifStream);

  if(!gif) {
    msSetError(MS_IMGERR, "Error loading GIF file.", "drawGIF()");
    return(-1);
  }

  w = gdImageSX(gif) - .5; /* to avoid rounding problems */
  h = gdImageSY(gif) - .5;

  if(layer->transform) {
    if(readWorldFile(msBuildPath3(szPath,map->mappath,map->shapepath,filename),
                     &ulx, &uly, &cx, &cy) != 0)
      return(-1);

    skipx = map->cellsize/cx;
    skipy = map->cellsize/cy;
    startx = (map->extent.minx - ulx)/cx;
    starty = (uly - map->extent.maxy)/cy;
  } else {
    skipx = skipy = 1;
    startx = starty = 0;    
  }

  if(layer->numclasses > 0) {
    int c;

    for(i=0; i<gdImageColorsTotal(gif); i++) {

      pixel.red = gdImageRed(gif,i);
      pixel.green = gdImageGreen(gif,i);
      pixel.blue = gdImageBlue(gif,i);	
      pixel.pen = i;

      if(!MS_COMPARE_COLORS(pixel, layer->offsite) && i != gdImageGetTransparent(gif)) {	
	c = msGetClass(layer, &pixel);

        if(c == -1) /* doesn't belong to any class, so handle like offsite */
	  cmap[i] = -1;
	else {
          RESOLVE_PEN_GD(img, layer->class[c].styles[0].color);
          if(MS_VALID_COLOR(layer->class[c].styles[0].color)) 
	    cmap[i] = msAddColorGD(map,img, 0, layer->class[c].styles[0].color.red, layer->class[c].styles[0].color.green, layer->class[c].styles[0].color.blue); // use class color
	  else if(MS_TRANSPARENT_COLOR(layer->class[c].styles[0].color)) 
	    cmap[i] = -1; // make transparent
	  else
            cmap[i] = msAddColorGD(map,img, 0, pixel.red, pixel.green, pixel.blue); // use raster color	  
	}	
      }
    }
  } else {
    for(i=0; i<gdImageColorsTotal(gif); i++) {

      pixel.red = gdImageRed(gif,i);
      pixel.green = gdImageGreen(gif,i);
      pixel.blue = gdImageBlue(gif,i);
      pixel.pen = i;

      if(!MS_COMPARE_COLORS(pixel, layer->offsite) && i != gdImageGetTransparent(gif)) 
	cmap[i] = msAddColorGD(map,img, 0, pixel.red, pixel.green, pixel.blue);      
    }    
  }

  y=starty;
  for(i=0; i<img->sy; i++) { /* for each row */
    if((y >= -0.5) && (y < h)) {
      x = startx;
      for(j=0; j<img->sx; j++) {
	if((x >= -0.5) && (x < w)) {
	  vv = gif->pixels[(y == -0.5)?0:MS_NINT(y)][(x == -0.5)?0:MS_NINT(x)];	  
	  if(cmap[vv] != -1)
	    img->pixels[i][j] = cmap[vv];
	}
	x+=skipx;
      }
    }
    y+=skipy;  
  }

  gdImageDestroy(gif);
  return(0);
#else
  msSetError(MS_IMGERR, "GIF support is not available.", "drawGIF()");
  return(-1);
#endif
}

/************************************************************************/
/*                              drawJPEG()                              */
/************************************************************************/

static int drawJPEG(mapObj *map, layerObj *layer, gdImagePtr img, char *filename) 
{
#ifdef USE_JPEG
  int i,j,k; /* loop counters */  
  int cmap[MAXCOLORS];
  double w, h;

  unsigned char vv;
  JSAMPARRAY buffer;

  double startx, starty; /* this is where we start out reading */
  double skipx,skipy; /* skip factors (x and y) */ 
  double x,y;

  double ulx, uly; /* upper left-hand coordinates */  
  double cx,cy; /* cell sizes (x and y) */

  colorObj pixel;

  FILE *jpegStream;

  struct jpeg_decompress_struct cinfo;
  struct jpeg_error_mgr jerr;

  char szPath[MS_MAXPATHLEN];

  cinfo.err = jpeg_std_error(&jerr);
  jpeg_create_decompress(&cinfo);

  jpegStream = fopen(msBuildPath3(szPath, map->mappath, map->shapepath, filename), "rb");
  if(!jpegStream) {
    msSetError(MS_IOERR, "Error open image file.", "drawJPEG()");
    return(-1);
  }
  jpeg_stdio_src(&cinfo, jpegStream);

  jpeg_read_header(&cinfo, TRUE);

  if(cinfo.jpeg_color_space != JCS_GRAYSCALE) {
    jpeg_destroy_decompress(&cinfo);
    fclose(jpegStream);
    msSetError(MS_IOERR, "Only grayscale JPEG images are supported.", "drawJPEG()");
    return(-1);
  }

  // set up the color map
  for (i=0; i<MAXCOLORS; i++) {
    pixel.red = pixel.green = pixel.blue = pixel.pen = i;

    cmap[i] = -1; // initialize to transparent
    if(!MS_COMPARE_COLORS(pixel, layer->offsite))      
      cmap[i] = msAddColorGD(map,img, 0, (i>>4)*17,(i>>4)*17,(i>>4)*17);
  }

  if(readWorldFile(msBuildPath3(szPath, map->mappath, map->shapepath,filename),
                   &ulx, &uly, &cx, &cy) != 0)
    return(-1);
  
  skipx = map->cellsize/cx;
  skipy = map->cellsize/cy;

  // muck with scaling
  if(MS_MIN(skipx, skipy) >= 8)
    cinfo.scale_denom = 8;
  else
    if(MS_MIN(skipx, skipy) >= 4)
      cinfo.scale_denom = 4;
    else
      if(MS_MIN(skipx, skipy) >= 2)
	cinfo.scale_denom = 2;

  skipx = skipx/cinfo.scale_denom;
  skipy = skipy/cinfo.scale_denom;
  startx = (map->extent.minx - ulx)/(cinfo.scale_denom*cx);
  starty = (uly - map->extent.maxy)/(cinfo.scale_denom*cy);

  // initialize the decompressor, this sets output sizes etc...
  jpeg_start_decompress(&cinfo);

  w = cinfo.output_width - .5; /* to avoid rounding problems */
  h = cinfo.output_height - .5;

  // one pixel high buffer as wide as the image, goes away when done with the image
  buffer = (*cinfo.mem->alloc_sarray) ((j_common_ptr) &cinfo, JPOOL_IMAGE, cinfo.output_width, 1);

  // skip any unneeded scanlines *yuck*
  for(i=0; i<starty-1; i++)
    jpeg_read_scanlines(&cinfo, buffer, 1);

  y=starty;

  for(i=0; i<img->sy; i++) { /* for each row */
    if((y > -0.5) && (y < cinfo.output_height)) {
      
      jpeg_read_scanlines(&cinfo, buffer, 1);
      
      x = startx;
      for(j=0; j<img->sx; j++) {
	if((x >= -0.5) && (x < cinfo.output_width)) {
	  vv = buffer[0][(x == -0.5)?0:MS_NINT(x)];
	  if(cmap[vv] != -1)
	    img->pixels[i][j] = cmap[vv];
	}
	x+=skipx;
	if(x>=cinfo.output_width) // next x is out of the image, so quit
	  break;
      }
    }
    
    y+=skipy;

    if(y>=cinfo.output_height) // next y is out of the image, so quit
      break;

    for(k=cinfo.output_scanline; k<y-1; k++)
      jpeg_read_scanlines(&cinfo, buffer, 1);
  } 

  jpeg_destroy((j_common_ptr) &cinfo);
  fclose(jpegStream);

  return(0);
#else
  msSetError(MS_IMGERR, "JPEG support is not available.", "drawJPEG()");
  return(-1);
#endif
}

/************************************************************************/
/*                              drawEPP()                               */
/************************************************************************/
  
static int drawEPP(mapObj *map, layerObj *layer, gdImagePtr img, char *filename) 
{
#ifdef USE_EPPL
  int ncol,nrow,yi,rr,i,j,startj,endj,vv;
  double cx,cy,skipx,skipy,x,y,startx,starty;
  int cmap[MAXCOLORS];
  char szPath[MS_MAXPATHLEN];
  eppfile epp;
  TRGB color;
  clrfile clr;

  colorObj pixel;

  for(i=0; i<MAXCOLORS; i++) cmap[i] = -1; // initialize the colormap to all transparent

  strcpy(epp.filname,msBuildPath3(szPath, map->mappath, map->shapepath,
                                  filename));
  if (!eppreset(&epp)) return -1;
  
  ncol=epp.lc-epp.fc+1;
  nrow=epp.lr-epp.fr+1;
  cx=(epp.lcx-epp.fcx)/ncol;
  cy=(epp.fry-epp.lry)/nrow;
  skipx=map->cellsize/cx;
  skipy=map->cellsize/cy;
  startx=(map->extent.minx-epp.fcx)/cx;
  starty=(epp.fry-map->extent.maxy)/cy;
  
  if (epp.kind!=8) {
    msSetError(MS_IMGERR,"Only 8 bit EPPL files supported.","drawEPP()");
    eppclose(&epp);
    return -3;
  }
  
  /* set up colors here */
  strcpy(clr.filname,msBuildPath3(szPath, map->mappath, map->shapepath,
                                  filename));
  if (!clrreset(&clr)) { /* use gray from min to max if no color file, classes not honored for greyscale */
    for (i=epp.minval; i<=epp.maxval; i++) {

      pixel.red = pixel.green = pixel.blue = pixel.pen = i;

      if(!MS_COMPARE_COLORS(pixel, layer->offsite)) {
	j=(((i-epp.minval)*16) / (epp.maxval-epp.minval+1))*17; 
	cmap[i]=msAddColorGD(map,img,0,j,j,j);
      }
    }
  } else {
    if(layer->numclasses > 0) {
      int c;
      
      for (i=epp.minval; i<=epp.maxval; i++) {

	pixel.red = color.red;
	pixel.green = color.green;
	pixel.blue = color.blue;
	pixel.pen = i;

	if(!MS_COMPARE_COLORS(pixel, layer->offsite)) {	  
	  c = msGetClass(layer, &pixel);
	  
	  if(c == -1) 
	    cmap[i] = -1;
	  else {
            RESOLVE_PEN_GD(img, layer->class[c].styles[0].color);
	    if(MS_VALID_COLOR(layer->class[c].styles[0].color)) 
	      cmap[i] = msAddColorGD(map,img, 0, layer->class[c].styles[0].color.red, layer->class[c].styles[0].color.green, layer->class[c].styles[0].color.blue); // use class color
	    else if(MS_TRANSPARENT_COLOR(layer->class[c].styles[0].color)) 
	      cmap[i] = -1; // make transparent
	    else {              
	      clrget(&clr,i,&color);
	      cmap[i] = msAddColorGD(map,img, 0, color.red, color.green, color.blue); // use raster color
	    }
	  }
	}
      }
    } else {
      for (i=epp.minval; i<=epp.maxval; i++) {
	pixel.red = color.red;
	pixel.green = color.green;
	pixel.blue = color.blue;
	pixel.pen = i;

	if(!MS_COMPARE_COLORS(pixel, layer->offsite)) {
	  clrget(&clr,i,&color);
	  cmap[i] = msAddColorGD(map,img, 0, color.red, color.green, color.blue);
	}
      }
    }
    clrclose(&clr);
  }
  
  /* loop setup */
  x=startx;
  for (j=0; j<img->sx && MS_NINT(x)<0; j++) x+=skipx;
  startx=x; startj=j;
  for (j=startj; j<img->sx && MS_NINT(x)<ncol; j++) x+=skipx;
  endj=j;
  rr=-1;
  y=starty;  
  for (i=0; i<img->sy; i++) {
    yi=MS_NINT(y);
    if (yi>=0 && yi<nrow) {
      if (yi>rr+1) if (!position(&epp,yi+epp.fr)) return -2;
      if (yi>rr) if (!get_row(&epp)) return -2;
      rr=yi;
      x=startx;
      for (j=startj; j<endj; j++) {
        vv=epp.rptr[(int)MS_NINT(x)+1];

	if(cmap[vv] != -1)
	  img->pixels[i][j] = cmap[vv];

        x+=skipx;
      }
    }
    y+=skipy;
  }
  eppclose(&epp);
  return 0;
#else
  msSetError(MS_IMGERR, "EPPL7 support is not available.", "drawEPP()");
  return(-1);
#endif  
}
 
/************************************************************************/
/*                        msDrawRasterLayerLow()                        */
/*                                                                      */
/*      Check for various file types and act appropriately.  Handle     */
/*      tile indexing.                                                  */
/************************************************************************/

int msDrawRasterLayerLow(mapObj *map, layerObj *layer, imageObj *image) 
{
  int status, i, done;
  FILE *f;  
  char dd[8];
  char *filename=NULL, tilename[MS_MAXPATHLEN];

  layerObj *tlp=NULL; // pointer to the tile layer either real or temporary
  int tileitemindex=-1, tilelayerindex=-1;
  shapeObj tshp;

  int force_gdal;
  char szPath[MS_MAXPATHLEN], cwd[MS_MAXPATHLEN];
  int final_status = MS_SUCCESS;

  rectObj searchrect;
  gdImagePtr img;

  cwd[0] = '\0';

  if(layer->debug > 0 || map->debug > 1)
    msDebug( "msDrawRasterLayerLow(%s): entering.\n", layer->name );

  if(!layer->data && !layer->tileindex) {
    if(layer->debug == MS_TRUE) msDebug( "msDrawRasterLayerLow(%s): layer data and tileindex NULL ... doing nothing.", layer->name );
    return(0);
  }

  if((layer->status != MS_ON) && (layer->status != MS_DEFAULT)) {
    if(layer->debug == MS_TRUE) msDebug( "msDrawRasterLayerLow(%s): not status ON or DEFAULT, doing nothing.", layer->name );
    return(0);
  }

  if(map->scale > 0) {
    if((layer->maxscale > 0) && (map->scale > layer->maxscale)) {
      if(layer->debug == MS_TRUE) msDebug( "msDrawRasterLayerLow(%s): skipping, map scale %.2g > MAXSCALE=%g\n", layer->name, map->scale, layer->maxscale );
      return(0);
    }
    if((layer->minscale > 0) && (map->scale <= layer->minscale)) {
      if(layer->debug == MS_TRUE) msDebug( "msDrawRasterLayerLow(%s): skipping, map scale %.2g < MINSCALE=%g\n", layer->name, map->scale, layer->minscale );
      return(0);
    }
  }

  force_gdal = MS_FALSE;
  if(MS_RENDERER_GD(image->format))
    img = image->img.gd;
  else {
    img = NULL;
    force_gdal = MS_TRUE;
  }

  // Only GDAL supports 24bit GD output.
  if(img != NULL && gdImageTrueColor(img)){
#ifndef USE_GDAL
    msSetError(MS_MISCERR, "Attempt to render raster layer to IMAGEMODE RGB or RGBA but\nwithout GDAL available.  24bit output requires GDAL.", "msDrawRasterLayerLow()" );
    return MS_FAILURE;
#else
    force_gdal = MS_TRUE;
#endif
  }

  // Only GDAL support image warping.
  if(layer->transform && msProjectionsDiffer(&(map->projection), &(layer->projection))) {
#ifndef USE_GDAL
    msSetError(MS_MISCERR, "Attempt to render raster layer that requires reprojection but\nwithout GDAL available.  Image reprojection requires GDAL.", "msDrawRasterLayerLow()" );
    return MS_FAILURE;
#else
    force_gdal = MS_TRUE;
#endif
  }

  // This force use of GDAL if available.  This is usually but not always a
  // good idea.  Remove this line for local builds if necessary.
#ifdef USE_GDAL 
  force_gdal = MS_TRUE;
#endif

  if(layer->tileindex) { // we have an index file
    
    msInitShape(&tshp);
    
    tilelayerindex = msGetLayerIndex(layer->map, layer->tileindex);
    if(tilelayerindex == -1) { // the tileindex references a file, not a layer

      // so we create a temporary layer
      tlp = (layerObj *) malloc(sizeof(layerObj));
      if(!tlp) {
        msSetError(MS_MEMERR, "Error allocating temporary layerObj.", "msDrawRasterLayerLow()");
        return(MS_FAILURE);
      }
      initLayer(tlp, map);

      // set a few parameters for a very basic shapefile-based layer
      tlp->name = strdup("TILE");
      tlp->type = MS_LAYER_TILEINDEX;
      tlp->data = strdup(layer->tileindex);
    } else
      tlp = &(layer->map->layers[tilelayerindex]);
      
    status = msLayerOpen(tlp);
    if(status != MS_SUCCESS)
    {
        final_status = status;
        goto cleanup;
    }

    // build item list (no annotation) since we may have to classify the shape, plus we want the tileitem
    status = msLayerWhichItems(tlp, MS_TRUE, MS_FALSE, layer->tileitem);
    if(status != MS_SUCCESS)
    {
        final_status = status;
        goto cleanup;
    }
 
    // get the tileitem index
    for(i=0; i<tlp->numitems; i++) {
      if(strcasecmp(tlp->items[i], layer->tileitem) == 0) {
        tileitemindex = i;
        break;
      }
    }
    if(i == tlp->numitems) { // didn't find it
        msSetError(MS_MEMERR, 
                   "Could not find attribute %s in tileindex.", 
                   "msDrawRasterLayerLow()", 
                   layer->tileitem);
        final_status = MS_FAILURE;
        goto cleanup;
    }
 
    searchrect = map->extent;
#ifdef USE_PROJ
    // if necessary, project the searchrect to source coords
    if((map->projection.numargs > 0) && (layer->projection.numargs > 0)) msProjectRect(&map->projection, &layer->projection, &searchrect);
#endif
    status = msLayerWhichShapes(tlp, searchrect);
    if (status != MS_SUCCESS) {
        // Can be either MS_DONE or MS_FAILURE
        if (status != MS_DONE) 
            final_status = status;

        goto cleanup;
    }
  }

  done = MS_FALSE;
  while(done != MS_TRUE) { 
    if(layer->tileindex) {
      status = msLayerNextShape(tlp, &tshp);
      if( status == MS_FAILURE)
      {
          final_status = MS_FAILURE;
          break;
      }

      if(status == MS_DONE) break; // no more tiles/images
       
      if(layer->data == NULL) // assume whole filename is in attribute field
          strcpy( tilename, tshp.values[tileitemindex] );
      else
          sprintf(tilename, "%s/%s", tshp.values[tileitemindex], layer->data);
      filename = tilename;
      
      msFreeShape(&tshp); // done with the shape
    } else {
      filename = layer->data;
      done = MS_TRUE; // only one image so we're done after this
    }

    if(strlen(filename) == 0) continue;

#ifdef USE_SDE
    if (layer->connectiontype == MS_SDE) {
      status = drawSDE(map, layer, img);
      if(status == -1) {
          final_status = status;
          goto cleanup;
      }
      continue;
    }
#endif

    msBuildPath3(szPath, map->mappath, map->shapepath, filename);

    f = fopen( szPath, "rb");
    if(!f) {
      memset( dd, 0, 8 );
      strcpy( szPath, filename );
    } else {
      fread(dd,8,1,f); // read some bytes to try and identify the file
      fclose(f);
    }

    if((memcmp(dd,"II*\0",4)==0 || memcmp(dd,"MM\0*",4)==0) && !force_gdal) {
      status = drawTIFF(map, layer, img, filename);
      if(status == -1) {
        return(MS_FAILURE);
      }
      continue;
    }

    if(memcmp(dd,"GIF8",4)==0 && !force_gdal ) {
      status = drawGIF(map, layer, img, filename);
      if(status == -1) {
        return(MS_FAILURE);
      }
      continue;
    }

    if(memcmp(dd,PNGsig,8)==0 && !force_gdal) {
      status = drawPNG(map, layer, img, filename);
      if(status == -1) {
        return(MS_FAILURE);
      }
      continue;
    }

    if(memcmp(dd,JPEGsig,3)==0 && !force_gdal) {
      status = drawJPEG(map, layer, img, filename);
      if(status == -1) {
        return(MS_FAILURE);
      }
      continue;
    }

#ifdef USE_GDAL
    {
      GDALDatasetH  hDS;

      msGDALInitialize();

      msAcquireLock( TLOCK_GDAL );
      hDS = GDALOpen(szPath, GA_ReadOnly );

      if(hDS != NULL) {
        double	adfGeoTransform[6];

            if (layer->projection.numargs > 0 && 
                EQUAL(layer->projection.args[0], "auto"))
            {
                const char *pszWKT;

                pszWKT = GDALGetProjectionRef( hDS );

                if( pszWKT != NULL && strlen(pszWKT) > 0 )
                {
                    if( msOGCWKT2ProjectionObj(pszWKT, &(layer->projection),
                                               layer->debug ) != MS_SUCCESS )
                    {
                        char	szLongMsg[MESSAGELENGTH*2];
                        errorObj *ms_error = msGetErrorObj();

                        sprintf( szLongMsg, 
                                 "%s\n"
                                 "PROJECTION AUTO cannot be used for this "
                                 "GDAL raster (`%s').",
                                 ms_error->message, filename);
                        szLongMsg[MESSAGELENGTH-1] = '\0';

                        msSetError(MS_OGRERR, "%s","msDrawRasterLayer()",
                                   szLongMsg);

                        msReleaseLock( TLOCK_GDAL );
                        final_status = MS_FAILURE;
                        break;
                    }
                }
            }

            msGetGDALGeoTransform( hDS, map, layer, adfGeoTransform );

            /* 
            ** We want to resample if the source image is rotated, or if
            ** the projections differ.  However, due to limitations in 
            ** msResampleGDALToMap() we can only resample rotated images
            ** if they also have fully defined projections.
            */
#ifdef USE_PROJ
            if( ((adfGeoTransform[2] != 0.0 || adfGeoTransform[4] != 0.0)
                 && layer->transform
                 && map->projection.proj != NULL 
                 && layer->projection.proj != NULL)
                || msProjectionsDiffer( &(map->projection), 
                                        &(layer->projection) ) )
            {
                status = msResampleGDALToMap( map, layer, image, hDS );
            }
            else
#endif
            {
                if( adfGeoTransform[2] != 0.0 || adfGeoTransform[4] != 0.0 )
                {
                    if( layer->debug || map->debug )
                        msDebug( "Layer %s has rotational coefficients but we are unable to\nuse them, is projection set?", layer->name );
                    
                }
                status = msDrawRasterLayerGDAL(map, layer, image, hDS );
            }

            if( status == -1 )
            {
                GDALClose( hDS );
                msReleaseLock( TLOCK_GDAL );
                final_status = MS_FAILURE;
                break;
            }

            GDALClose( hDS );
            msReleaseLock( TLOCK_GDAL );
            continue;
        }
        else
        {
            msReleaseLock( TLOCK_GDAL );
        }
    }
#endif

    /* If GDAL doesn't recognise it, and it wasn't successfully opened 
    ** Generate an error.
    */
    if( !f ) {
      msSetError(MS_IOERR, "(%s)", "msDrawRaster()", filename);

#ifndef IGNORE_MISSING_DATA
      if( layer->debug || map->debug )
          msDebug( "Unable to open file %s for layer %s ... fatal error.\n", filename, layer->name );

      final_status = MS_FAILURE;
      break;
#else
      if( layer->debug || map->debug )
          msDebug( "Unable to open file %s for layer %s ... ignoring this missing data.\n", filename, layer->name );

      continue; // skip it, next tile
#endif
    }

    /* put others which may require checks here */  
    
    /* No easy check for EPPL so put here */      
    status=drawEPP(map, layer, img, filename);
    if(status != 0) {
      if (status == -2) msSetError(MS_IMGERR, "Error reading EPPL file; probably corrupt.", "msDrawEPP()");
      if (status == -1) msSetError(MS_IMGERR, "Unrecognized or unsupported image format", "msDrawRaster()");
      return(MS_FAILURE);
    }
    continue;
  } // next tile

cleanup:
  if(layer->tileindex) { // tiling clean-up
    msLayerClose(tlp);
    if(tilelayerindex == -1) {
      freeLayer(tlp);
      free(tlp);
    }
  }

  return final_status;
}

/************************************************************************/
/*                         msDrawReferenceMap()                         */
/************************************************************************/

//TODO : this will msDrawReferenceMapGD
imageObj *msDrawReferenceMap(mapObj *map) {
  double cellsize;
  int c=-1, oc=-1;
  int x1,y1,x2,y2;
  char szPath[MS_MAXPATHLEN];

  imageObj   *image = NULL;
  gdImagePtr img=NULL;

  image = msImageLoadGD( msBuildPath(szPath, map->mappath, 
                                     map->reference.image) );
  if( image == NULL )
      return NULL;

  if (map->web.imagepath)
      image->imagepath = strdup(map->web.imagepath);
  if (map->web.imageurl)
      image->imageurl = strdup(map->web.imageurl);

  img = image->img.gd;
  
  // make sure the extent given in mapfile fits the image
  cellsize = msAdjustExtent(&(map->reference.extent), 
                            image->width, image->height);

  // Allocate a fake bg color because when using gd-1.8.4 with a PNG reference
  // image, the box color could end up being set to color index 0 which is 
  // transparent (yes, that's odd!).
  gdImageColorAllocate(img, 255,255,255);

  // allocate some colors
  if( MS_VALID_COLOR(map->reference.outlinecolor) )
    oc = gdImageColorAllocate(img, map->reference.outlinecolor.red, map->reference.outlinecolor.green, map->reference.outlinecolor.blue);
  if( MS_VALID_COLOR(map->reference.color) )
    c = gdImageColorAllocate(img, map->reference.color.red, map->reference.color.green, map->reference.color.blue); 

  // convert map extent to reference image coordinates
  x1 = MS_MAP2IMAGE_X(map->extent.minx,  map->reference.extent.minx, cellsize);
  x2 = MS_MAP2IMAGE_X(map->extent.maxx,  map->reference.extent.minx, cellsize);  
  y1 = MS_MAP2IMAGE_Y(map->extent.maxy,  map->reference.extent.maxy, cellsize);
  y2 = MS_MAP2IMAGE_Y(map->extent.miny,  map->reference.extent.maxy, cellsize);

  // if extent are smaller than minbox size
  // draw that extent on the reference image
  if( (abs(x2 - x1) > map->reference.minboxsize) || 
          (abs(y2 - y1) > map->reference.minboxsize) )
  {
      if( map->reference.maxboxsize == 0 || 
              ((abs(x2 - x1) < map->reference.maxboxsize) && 
                  (abs(y2 - y1) < map->reference.maxboxsize)) )
      {
          if(c != -1)
              gdImageFilledRectangle(img,x1,y1,x2,y2,c);
          if(oc != -1)
              gdImageRectangle(img,x1,y1,x2,y2,oc);
      }
  
  }
  else // else draw the marker symbol
  {
      if( map->reference.maxboxsize == 0 || 
              ((abs(x2 - x1) < map->reference.maxboxsize) && 
                  (abs(y2 - y1) < map->reference.maxboxsize)) )
      {
	  styleObj style;

          initStyle(&style);
          style.color = map->reference.color;
          style.outlinecolor = map->reference.outlinecolor;
          style.size = map->reference.markersize;

          //if the marker symbol is specify draw this symbol else draw a cross
          if(map->reference.marker != 0)
          {
              pointObj *point = NULL;

              point = malloc(sizeof(pointObj));
              point->x = (double)(x1 + x2)/2;
              point->y = (double)(y1 + y2)/2;

	      style.symbol = map->reference.marker;

              msDrawMarkerSymbol(&map->symbolset, image, point, &style, 1.0);
              free(point);
          }
          else if(map->reference.markername != NULL)
          {              
              pointObj *point = NULL;

              point = malloc(sizeof(pointObj));
              point->x = (double)(x1 + x2)/2;
              point->y = (double)(y1 + y2)/2;

	      style.symbol = msGetSymbolIndex(&map->symbolset,  map->reference.markername, MS_TRUE);

              msDrawMarkerSymbol(&map->symbolset, image, point, &style, 1.0);
              free(point);
          }
          else
          {
              int x21, y21;
              //determine the center point
              x21 = MS_NINT((x1 + x2)/2);
              y21 = MS_NINT((y1 + y2)/2);

              //get the color
              if(c == -1)
                  c = oc;

              //draw a cross
              if(c != -1)
              {
                  gdImageLine(img, x21-8, y21, x21-3, y21, c);
                  gdImageLine(img, x21, y21-8, x21, y21-3, c);
                  gdImageLine(img, x21, y21+3, x21, y21+8, c);
                  gdImageLine(img, x21+3, y21, x21+8, y21, c);
              }
          }
      }
  }

  return(image);
}
