/*
 * $Id: imtranslate.cxx,v 4.3 2003/07/22 13:06:55 hut66au Exp $
 *
 * Imview, the portable image analysis application
 * http://www.cmis.csiro.au/Hugues.Talbot/imview
 * ----------------------------------------------------------
 *
 *  Imview is an attempt to provide an image display application
 *  suitable for professional image analysis. It was started in
 *  1997 and is mostly the result of the efforts of Hugues Talbot,
 *  Image Analysis Project, CSIRO Mathematical and Information
 *  Sciences, with help from others (see the CREDITS files for
 *  more information)
 *
 *  Imview is Copyrighted (C) 1997-2001 by Hugues Talbot and was
 *  supported in parts by the Australian Commonwealth Science and 
 *  Industry Research Organisation. Please see the COPYRIGHT file 
 *  for full details. Imview also includes the contributions of 
 *  many others. Please see the CREDITS file for full details.
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *  
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *  
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
 * */

/*------------------------------------------------------------------------
 *
 * imtranslate.C
 *
 * This file contains code to translate raw image data into
 * viewable pixmaps
 *
 * This is a simple job, yet requires a bit of abstraction.
 *
 * Hugues Talbot	21 Jan 1998
 *
 * Actually at present this is a complete mess Hugues Talbot	26 Feb 2001
 *      
 *-----------------------------------------------------------------------*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "imunistd.h"
#include <limits.h>
#include <float.h>
#include "imview.hxx"
#include "imviewWindow.hxx"
#include "imageIO.hxx"
#include "imageViewer.hxx"
#include "menubar.hxx"
#include "nocase.hxx"
#include "io/newprefio.hxx"

// application components
extern imageViewer       *mainViewer;
extern imviewWindow      *mainWindow;
extern imprefs           *fileprefs;
extern imViewMenuBar     *mainMenuBar;

// other naughty global variables
extern char               appName[]; // this is needed to change the window's title
extern int                applyTransferToAll;
extern bool               lutWraparound;
extern int                makeImageCharIfFits;
extern int                RGBdisplaysRGB;
extern int                fitCharOverAllSamples;
extern int                fitCharOverAllSlices;
extern unsigned char      FL_COLOURS[][3];

// this function return a static char pointer to a buffer
// giving information on the underlying data.
// This is only intended for `casual' data inspection (data box on image)
// for complete information on the data, you must use the next function
// (same name, but with a zpos third argument
char *imageIO::getRawDataInfo(int xpos, int ypos)
{
    static char returnInfo1[512], returnInfo2[256], buff[128];
    int         zpos; // not an argument here
    int         maxComp; // maximum number of samples to display, if present
    long        zoff;
    
    // in this function, zpos is not given, we take the current position as the default
    zpos = currZpos;
    zoff = buffZOffset;

    // maximum nb of samples to display
    maxComp = trivmin(3, currImgNbSamples);


    // pixtype-independent information
    if ((xOffset != 0)
	|| (yOffset != 0)
	|| (zOffset != 0)) {
	if (currImgThickness > 1) {
	    // we are pedantic only if need be
	    sprintf(returnInfo1, " (X=%d, Y=%d, Z=%d) ",
		    xpos+xOffset,
		    ypos+yOffset,
		    zpos+zOffset);
	} else {
	    // only display the 3-D info if needed
	    sprintf(returnInfo1, " (X=%d, Y=%d) ",
		    xpos+xOffset,
		    ypos+yOffset);
	}
    } else {
	returnInfo1[0] = '\0';
    }

    switch (currPixType) {
      case IM_BINARY:
	sprintf(returnInfo2," <BIN %d> ",
		!(*(imdata + zoff + currImgWidth*ypos + xpos) != 0));
	break;

      case IM_UINT1:
      case IM_INT1:
	returnInfo2[0] = '\0'; // default: no colourmap
	if (!currImgHasCLUT && (currBuffp != 0)) { // hmmm... && (currImgNbSamples > 1)) {
	    int i;

	    if (currComp < 2) {
		sprintf(returnInfo2, " <CHR=%d", *((uchar *)currBuffp[0] + zoff + currImgWidth*ypos + xpos));
		for (i = 1 ; (i < maxComp) && (i < currImgNbSamples) ; i++) {
		    sprintf(buff, ",%d", *((uchar *)currBuffp[i] + zoff + currImgWidth*ypos + xpos));
		    strcat(returnInfo2, buff);
		}
		if (i < currImgNbSamples) {
		    sprintf(buff, ",...> (%d)", currImgNbSamples);
		    strcat(returnInfo2, buff);
		} else
		    strcat(returnInfo2, ">");
	    } else if (currComp < currImgNbSamples-maxComp+1) {
		sprintf(returnInfo2, " <CHR=...,%d,%d,%d,...> (%d)",
			*((uchar *)currBuffp[currComp-1] + zoff + currImgWidth*ypos + xpos),
			*((uchar *)currBuffp[currComp] + zoff + currImgWidth*ypos + xpos),
			*((uchar *)currBuffp[currComp+1] + zoff + currImgWidth*ypos + xpos),
			currImgNbSamples);
	    } else {
		sprintf(returnInfo2, " <CHR=...,%d,%d,%d> (%d)",
			*((uchar *)currBuffp[currImgNbSamples-3] + zoff + currImgWidth*ypos + xpos),
			*((uchar *)currBuffp[currImgNbSamples-2] + zoff + currImgWidth*ypos + xpos),
			*((uchar *)currBuffp[currImgNbSamples-1] + zoff + currImgWidth*ypos + xpos),
			currImgNbSamples);
	    }
	} else if (currImgHasCLUT && (currBuffp != 0) && (currBuffp[currComp] != 0)) {
	    sprintf(returnInfo2, " <LUT %d> ",
		    *((uchar *)currBuffp[currComp] + zoff + currImgWidth*ypos + xpos));
	}
	break;

      case IM_INT2:
	if (currBuffp != 0) {
	    int i;
	    if (currComp < 2) {
		sprintf(returnInfo2, " <SHT=%d", *((short *)currBuffp[0] + zoff + currImgWidth*ypos + xpos));
		for (i = 1 ; i < maxComp ; i++) {
		    sprintf(buff, ",%d", *((short *)currBuffp[i] + zoff + currImgWidth*ypos + xpos));
		    strcat(returnInfo2, buff);
		}
		if (i < currImgNbSamples) {
		    sprintf(buff, ",...> (%d)", currImgNbSamples);
		    strcat(returnInfo2, buff);
		} else
		    strcat(returnInfo2, ">");
	    } else if (currComp < currImgNbSamples-maxComp+1) {
		sprintf(returnInfo2, " <SHT=...,%d,%d,%d,...> (%d)",
			*((short *)currBuffp[currComp-1] + zoff + currImgWidth*ypos + xpos),
			*((short *)currBuffp[currComp] + zoff + currImgWidth*ypos + xpos),
			*((short *)currBuffp[currComp+1] + zoff + currImgWidth*ypos + xpos),
			currImgNbSamples);
	    } else {
		sprintf(returnInfo2, " <SHT=...,%d,%d,%d> (%d)",
			*((short *)currBuffp[currImgNbSamples-3] + zoff + currImgWidth*ypos + xpos),
			*((short *)currBuffp[currImgNbSamples-2] + zoff + currImgWidth*ypos + xpos),
			*((short *)currBuffp[currImgNbSamples-1] + zoff + currImgWidth*ypos + xpos),
			currImgNbSamples);
	    }
	    
	} else {
	    sprintf(returnInfo2, " <SHT ?> ");
	}
	break;
      
      case IM_UINT2:
	if (currBuffp != 0) {
	    int i;
	    if (currComp < 2) {
		sprintf(returnInfo2, " <UST=%d", *((unsigned short *)currBuffp[0] + zoff + currImgWidth*ypos + xpos));
		for (i = 1 ; i < maxComp ; i++) {
		    sprintf(buff, ",%d", *((unsigned short *)currBuffp[i] + zoff + currImgWidth*ypos + xpos));
		    strcat(returnInfo2, buff);
		}
		if (i < currImgNbSamples) {
		    sprintf(buff, ",...> (%d)", currImgNbSamples);
		    strcat(returnInfo2, buff);
		} else
		    strcat(returnInfo2, ">");
	    } else if (currComp < currImgNbSamples-maxComp+1) {
		sprintf(returnInfo2, " <UST=...,%d,%d,%d,...> (%d)",
			*((unsigned short *)currBuffp[currComp-1] + zoff + currImgWidth*ypos + xpos),
			*((unsigned short *)currBuffp[currComp] + zoff + currImgWidth*ypos + xpos),
			*((unsigned short *)currBuffp[currComp+1] + zoff + currImgWidth*ypos + xpos),
			currImgNbSamples);
	    } else {
		sprintf(returnInfo2, " <UST=...,%d,%d,%d> (%d)",
			*((unsigned short *)currBuffp[currImgNbSamples-3] + zoff + currImgWidth*ypos + xpos),
			*((unsigned short *)currBuffp[currImgNbSamples-2] + zoff + currImgWidth*ypos + xpos),
			*((unsigned short *)currBuffp[currImgNbSamples-1] + zoff + currImgWidth*ypos + xpos),
			currImgNbSamples);
	    }
	    
	} else {
	    sprintf(returnInfo2, " <UST ?> ");
	}
	break;
	
      case IM_INT4:
      case IM_UINT4:
	if (!currImgHasCLUT) {
	    if (currBuffp != 0) {
		int i;
		if (currComp < 2) {
		    sprintf(returnInfo2, " <INT=%d", *((int *)currBuffp[0] + zoff + currImgWidth*ypos + xpos));
		    for (i = 1 ; (i < maxComp) && (i < currImgNbSamples) ; i++) {
			sprintf(buff, ",%d", *((int *)currBuffp[i] + zoff + currImgWidth*ypos + xpos));
			strcat(returnInfo2, buff);
		    }
		    if (i < currImgNbSamples) {
			sprintf(buff, ",...> (%d)", currImgNbSamples);
			strcat(returnInfo2, buff);
		    } else
			strcat(returnInfo2, ">");
		} else if (currComp < currImgNbSamples-maxComp+1) {
		    sprintf(returnInfo2, " <INT=...,%d,%d,%d,...> (%d)",
			*((int *)currBuffp[currComp-1] + zoff + currImgWidth*ypos + xpos),
			*((int *)currBuffp[currComp] + zoff + currImgWidth*ypos + xpos),
			*((int *)currBuffp[currComp+1] + zoff + currImgWidth*ypos + xpos),
			currImgNbSamples);
		} else {
		    sprintf(returnInfo2, " <INT=...,%d,%d,%d> (%d)",
			    *((int *)currBuffp[currImgNbSamples-3] + zoff + currImgWidth*ypos + xpos),
			    *((int *)currBuffp[currImgNbSamples-2] + zoff + currImgWidth*ypos + xpos),
			    *((int *)currBuffp[currImgNbSamples-1] + zoff + currImgWidth*ypos + xpos),
			    currImgNbSamples);
		}
	    } else {
		sprintf(returnInfo2, " <INT ?> ");
	    }
	} else if ((currBuffp != 0) && (currBuffp[currComp] != 0)) {
	    int val = *((int *)currBuffp[currComp] + zoff + currImgWidth*ypos + xpos);
	    sprintf(returnInfo2, " <LUT %d (%d)> ",
		    val,val % 256);
	} else {
	    sprintf(returnInfo2, " <INT ?,?> ");
	}
	break;

      case IM_DOUBLE:
	if (currBuffp != 0) {
	    int i;
	    if (currComp < 2) {
		sprintf(returnInfo2, " <DBL=%.6g", *((double *)currBuffp[0] + zoff + currImgWidth*ypos + xpos));
		for (i = 1 ; (i < maxComp) && (i < currImgNbSamples) ; i++) {
		    sprintf(buff, ", %.6g", *((double *)currBuffp[i] + zoff + currImgWidth*ypos + xpos));
		    strcat(returnInfo2, buff);
		}
		if (i < currImgNbSamples) {
		    sprintf(buff, ",...> (%d)", currImgNbSamples);
		    strcat(returnInfo2, buff);
		} else
		    strcat(returnInfo2, ">");
	    } else if (currComp < currImgNbSamples-maxComp+1) {
		sprintf(returnInfo2, " <DBL=...,%.6g,%.6g,%.6g,...> (%d)",
			*((double *)currBuffp[currComp-1] + zoff + currImgWidth*ypos + xpos),
			*((double *)currBuffp[currComp] + zoff + currImgWidth*ypos + xpos),
			*((double *)currBuffp[currComp+1] + zoff + currImgWidth*ypos + xpos),
			currImgNbSamples);
	    } else {
		sprintf(returnInfo2, " <DBL=...,%.6g,%.6g,%.6g> (%d)",
			*((double *)currBuffp[currImgNbSamples-3] + zoff + currImgWidth*ypos + xpos),
			*((double *)currBuffp[currImgNbSamples-2] + zoff + currImgWidth*ypos + xpos),
			*((double *)currBuffp[currImgNbSamples-1] + zoff + currImgWidth*ypos + xpos),
			currImgNbSamples);
	    }
	} else {
	    sprintf(returnInfo2, " <DBL ?> ");
	}
	break;

	
      default:
	sprintf(returnInfo2, " <ERR !>"); // this means error, of course
	break;
    }

    return strcat(returnInfo1, returnInfo2);
}

// This version returns as much information as possible, even when redundant
// this function should not be called to decide whether raw information
// is present or not. Call the `hasRawData' function for that...
char *imageIO::getRawDataInfo(int xpos, int ypos, int zpos)
{ 
    static char *returnInfo1 = 0, *returnInfo2 = 0, buff[128];
    long        zoff; 
     
    if (zpos < 0) zpos = 0;
    if (zpos >= currImgThickness) zpos = currImgThickness-1;
    zoff = zpos * (currImgWidth*currImgHeight);

    if (returnInfo1 != 0) {
	delete[] returnInfo1;
	delete[] returnInfo2;
    }
	
    returnInfo1 = new char[currImgNbSamples * 30 + 40]; // 30 chars per sample should be enough...
    returnInfo2 = new char[currImgNbSamples * 30];
    
    // pixtype-independent information
    sprintf(returnInfo1, " %4d %4d %4d ",
	    xpos,
	    ypos,
	    zpos);

    // pixtype-depend information
    switch (currPixType) {
      case IM_BINARY:
	sprintf(returnInfo2," %2d BIN %4d ",
		1, (*(imdata + zoff + currImgWidth*ypos + xpos) == 0));
	if ((currBuffp != 0) && (currBuffp[0] != 0)) {
	    sprintf(returnInfo2, " %2d BIN %4d", currImgNbSamples, (*((uchar *)currBuffp[0] + zoff + currImgWidth*ypos + xpos) == 0));
	    for (int i = 1 ; i < currImgNbSamples; i++) {
		sprintf(buff, " %4d", (*((uchar *)currBuffp[i] + zoff + currImgWidth*ypos + xpos) == 0));
		strcat(returnInfo2, buff);
	    }
	} else {
	    if (currImgNbSamples == 1)
		sprintf(returnInfo2," %2d BIN %4d ",
			1, (*(imdata + currImgWidth*ypos + xpos) == 0));
	    else if (currImgNbSamples == 3) {
		uchar *xp;
		xp = imdata + 3 * (currImgWidth*ypos + xpos);
		sprintf(returnInfo2," %2d BIN %4d %4d %4d",
			3, (*xp == 0), (*(xp+1) == 0), (*(xp+2) == 0));
	    } else {
		sprintf(returnInfo2," %2d BIN ???", currImgNbSamples); // this is an error, of course
	    }
	}
	break;

      case IM_UINT1:
      case IM_INT1:
	if (currImgHasCLUT && (currBuffp != 0) && (currBuffp[0] != 0)) {
	    sprintf(returnInfo2, " %2d LUT %4d ",
		    1, *((uchar *)currBuffp[0] + zoff + currImgWidth*ypos + xpos));
	} else {
	    if ((currBuffp != 0) && (currBuffp[0] != 0)) {
		sprintf(returnInfo2, " %2d CHR %4d", currImgNbSamples, *((uchar *)currBuffp[0] + zoff + currImgWidth*ypos + xpos));
		for (int i = 1 ; i < currImgNbSamples; i++) {
		    sprintf(buff, " %4d", *((uchar *)currBuffp[i] + zoff + currImgWidth*ypos + xpos));
		    strcat(returnInfo2, buff);
		}
	    } else {
		if (currImgNbSamples == 1)
		    sprintf(returnInfo2," %2d CHR %4d ",
			    1, *(imdata + currImgWidth*ypos + xpos));
		else if (currImgNbSamples == 3) {
		    uchar *xp;
		    xp = imdata + 3 * (currImgWidth*ypos + xpos);
		    sprintf(returnInfo2," %2d CHR %4d %4d %4d",
			    3, *xp, *(xp+1), *(xp+2));
		    
		} else {
		    sprintf(returnInfo2," %2d CHR ???", currImgNbSamples); // this is an error, of course
		}
	    }
	    
	}
	break;

      case IM_INT2:
	if (currBuffp != 0) {
	    sprintf(returnInfo2, " %2d SHT %4d", currImgNbSamples, *((short *)currBuffp[0] + zoff + currImgWidth*ypos + xpos));
	    for (int i = 1 ; i < currImgNbSamples ; i++) {
		sprintf(buff, " %4d", *((short *)currBuffp[i] + zoff + currImgWidth*ypos + xpos));
		strcat(returnInfo2, buff);
	    }
	} else {
	    sprintf(returnInfo2, "  0 SHT ??? ");
	}
	break;

      case IM_UINT2:
	if (currBuffp != 0) {
	    sprintf(returnInfo2, " %2d UST %4d", currImgNbSamples, *((unsigned short *)currBuffp[0] + zoff + currImgWidth*ypos + xpos));
	    for (int i = 1 ; i < currImgNbSamples ; i++) {
		sprintf(buff, " %4d", *((unsigned short *)currBuffp[i] + zoff + currImgWidth*ypos + xpos));
		strcat(returnInfo2, buff);
	    }
	} else {
	    sprintf(returnInfo2, "  0 UST ??? ");
	}
	break;
	
      case IM_INT4:
      case IM_UINT4:
	if (currImgHasCLUT && (currBuffp != 0) && (currBuffp[0] != 0)) {
	    int val = *((int *)currBuffp[0] + zoff + currImgWidth*ypos + xpos);
	    sprintf(returnInfo2, " %2d LUT %4d %4d ",
		    2, val, val % 256 ); 
	} else if ((currBuffp != 0) && (currBuffp[0] != 0)) {
	    sprintf(returnInfo2, " %2d INT %4d", currImgNbSamples, *((int *)currBuffp[0] + zoff + currImgWidth*ypos + xpos));
	    for (int i = 1 ; i < currImgNbSamples ; i++) {
		sprintf(buff, " %4d", *((int *)currBuffp[i] + zoff + currImgWidth*ypos + xpos));
		strcat(returnInfo2, buff);
	    }
	} else {
	    sprintf(returnInfo2, "  0 INT ??? ");
	}
	break;

      case IM_DOUBLE:
	if (currBuffp != 0) {
	    sprintf(returnInfo2, " %2d DBL %f", currImgNbSamples, *((double *)currBuffp[0] + zoff + currImgWidth*ypos + xpos));
	    for (int i = 1 ; i < currImgNbSamples ; i++) {
		sprintf(buff, " %f", *((double *)currBuffp[i] + zoff + currImgWidth*ypos + xpos));
		strcat(returnInfo2, buff);
	    }
	} else {
	    sprintf(returnInfo2, "  0 DBL ??? ");
	}
	break;

	
      default:
	sprintf(returnInfo2, "  0 ERR !"); // this means error, of course
	break;
    }

    return strcat(returnInfo1, returnInfo2);
}

// return the data in vector form to display as a spectrum
double *imageIO::getRawDataVector(int xpos, int ypos, int *nb)
{
    int zpos, zoff;
    static double *returnInfo = 0;

    zpos = currZpos;
    if (zpos < 0) zpos = 0;
    if (zpos >= currImgThickness) zpos = currImgThickness-1;
    zoff = zpos * (currImgWidth*currImgHeight);

    if (returnInfo != 0) {
	delete[] returnInfo;
    }
	
    returnInfo = new double[currImgNbSamples];
    *nb = currImgNbSamples;
    
    switch (currPixType) {
      case IM_BINARY:
      case IM_UINT1:
      case IM_INT1:
	if ((currBuffp != 0) && (currBuffp[0] != 0)) {
	    for (int i = 0 ; i < currImgNbSamples; i++) 
		returnInfo[i] = (double) *((uchar *)currBuffp[i] + zoff + currImgWidth*ypos + xpos);
	} else {
	    if (currImgNbSamples == 1)
		returnInfo[0] = (double) *(imdata + currImgWidth*ypos + xpos);
	    else if (currImgNbSamples == 3) {
		uchar *xp;
		xp = imdata + 3 * (currImgWidth*ypos + xpos);
		returnInfo[0] = (double) *xp;
		returnInfo[1] = (double) *(xp+1);
		returnInfo[2] = (double) *(xp+2);
	    } 
	}
	break;


      case IM_INT2:
	if (currBuffp != 0) {
	    for (int i = 0 ; i < currImgNbSamples ; i++) 
		returnInfo[i] = (double) *((short *)currBuffp[i] + zoff + currImgWidth*ypos + xpos);
	} 
	break;

      case IM_UINT2:
	if (currBuffp != 0) {
	    for (int i = 0 ; i < currImgNbSamples ; i++) 
		returnInfo[i] = (double) *((unsigned short *)currBuffp[i] + zoff + currImgWidth*ypos + xpos);
	} 
	break;
	
      case IM_INT4:
      case IM_UINT4:
	if (currBuffp != 0) {
	    for (int i = 0 ; i < currImgNbSamples ; i++) 
		returnInfo[i] = (double) *((int *)currBuffp[i] + zoff + currImgWidth*ypos + xpos);
	}
	break;

      case IM_FLOAT:
	if (currBuffp != 0) {
	    for (int i = 0 ; i < currImgNbSamples ; i++) 
		returnInfo[i] = (double)  *((float *)currBuffp[i] + zoff + currImgWidth*ypos + xpos);
	} 
	break;
	
      case IM_DOUBLE:
	if (currBuffp != 0) {
	    for (int i = 0 ; i < currImgNbSamples ; i++) 
		returnInfo[i] = *((double *)currBuffp[i] + zoff + currImgWidth*ypos + xpos);
	} 
	break;
	
      default:
	for (int i = 0 ; i < currImgNbSamples ; i++)
	    returnInfo[i] = 0.0;
	break;
    }
    return returnInfo;
}

int imageIO::getRawDataPoint(long delta, double &GL)
{
    double dummyG, dummyB;

    return(getRawDataPoint(delta, GL, dummyG, dummyB));
}

int imageIO::getRawDataPoint(long delta, double &R, double &G, double &B)
{
    int retval = 0;

    if (currBuffp != 0) {
        // in this function, zpos is not given, we take the current position as the default
        int zoff = buffZOffset;
        if ((imspp == 1) || (currImgNbSamples != 3) || (!RGBdisplaysRGB)) { // colour LUT or Overlay a possibility
            switch (currPixType) {
              case IM_BINARY:
              case IM_UINT1:
                R = G = B = (double)(*((char *)currBuffp[currComp] + zoff + delta));
                break;

              case IM_INT2:
                R = G = B = (double)(*((short *)currBuffp[currComp] + zoff + delta));
                break;

              case IM_UINT2:
                R = G = B = (double)(*((unsigned short *)currBuffp[currComp] + zoff + delta));
                break;
        
              case IM_INT4:
                R = G = B = (double)(*((int *)currBuffp[currComp] + zoff + delta));
                break;

              case IM_FLOAT:
                R = G = B = (double)(*((float *)currBuffp[currComp] + zoff + delta));
                break;

              case IM_DOUBLE:
                R = G = B = (double)(*((double *)currBuffp[currComp] + zoff + delta));
                break;

              default:
                retval = 1;
                break;
            }
        } else {
            // we assume exactly 3 components, meaning RGB data (not always true)
	    assert(currImgNbSamples == 3);
            switch (currPixType) {
              case IM_BINARY:
              case IM_UINT1:
                R =  (double)(*((char *)currBuffp[0] + zoff + delta));
                G =  (double)(*((char *)currBuffp[1] + zoff + delta));
                B =  (double)(*((char *)currBuffp[2] + zoff + delta));
                break;

              case IM_INT2:
                R = (double)(*((short *)currBuffp[0] + zoff + delta));
                G = (double)(*((short *)currBuffp[1] + zoff + delta));
                B = (double)(*((short *)currBuffp[3] + zoff + delta));
                break;

              case IM_UINT2:
                R = (double)(*((unsigned short *)currBuffp[0] + zoff + delta));
                G = (double)(*((unsigned short *)currBuffp[1] + zoff + delta));
                B = (double)(*((unsigned short *)currBuffp[2] + zoff + delta));
                break;
        
              case IM_INT4:
                R = (double)(*((int *)currBuffp[0] + zoff + delta));
                G = (double)(*((int *)currBuffp[1] + zoff + delta));
                B = (double)(*((int *)currBuffp[2] + zoff + delta));
                break;

              case IM_FLOAT:
                R = (double)(*((float *)currBuffp[0] + zoff + delta));
                G = (double)(*((float *)currBuffp[1] + zoff + delta));
                B = (double)(*((float *)currBuffp[2] + zoff + delta));
                break;

              case IM_DOUBLE:
                R = (double)(*((double *)currBuffp[0] + zoff + delta));
                R = (double)(*((double *)currBuffp[1] + zoff + delta));
                R = (double)(*((double *)currBuffp[2] + zoff + delta));
                break;

              default:
                retval = 1;
                break;
            }
        }
    } else {
        if (imspp == 1) {
            G = B = R = imdata[delta];
        } else {
            R = imdata[delta * 3];
            G = imdata[delta * 3 + 1];
            B = imdata[delta * 3 + 2];
        }
    }

    return retval;
}

// Same as above, but image is really 3D, pretending it's hyperspectral.
// This is a hack for the sake of my colleagues who can't be bothered
// fixing things when they are broken. I HATE this.
// Note: This is all because Z-IMAGE handles hyperspectral images
//       like a pig, although it had been designed from the start
//       to do better than that.
// Hugues Talbot 31 Dec 1998
// To be fair, this might prove to be useful in the end...
// return the data in vector form to display as a spectrum
double *imageIO::get3DRawDataVector(int xpos, int ypos, int *nb)
{
    int cpos, zoff;
    static double *returnInfo = 0;

    // we'll use the currently displayed component 
    cpos = currComp;
    if (cpos < 0) cpos = 0;
    if (cpos >= currImgNbSamples) cpos = currImgNbSamples-1;
    
    zoff = currImgWidth*currImgHeight;

    if (returnInfo != 0) {
	delete[] returnInfo;
    }
	
    returnInfo = new double[currImgThickness];
    *nb = currImgThickness;
    
    switch (currPixType) {
      case IM_BINARY:
      case IM_UINT1:
      case IM_INT1:
	if ((currBuffp != 0) && (currBuffp[cpos] != 0)) {
	    for (int i = 0 ; i < currImgThickness; i++) 
		returnInfo[i] = (double) *((uchar *)currBuffp[cpos] + zoff*i + currImgWidth*ypos + xpos);
	} else {
	    if (currImgNbSamples == 1)
		for (int i = 0 ; i < currImgThickness; i++) 
		    returnInfo[i] = (double) *(imdata + zoff*i + currImgWidth*ypos + xpos);
	    else if (currImgNbSamples == 3) {
		uchar *xp;
		xp = imdata + 3 * (currImgWidth*ypos + xpos);
		for (int i = 0 ; i < currImgThickness; i++) 
		    returnInfo[i] = (double) *(imdata + 3 * (zoff*i + currImgWidth*ypos + xpos) + cpos);
	    } 
	}
	break;


      case IM_INT2:
	if ((currBuffp != 0) && (currBuffp[cpos] != 0)) {
	    for (int i = 0 ; i < currImgThickness ; i++) 
		returnInfo[i] = (double) *((short *)currBuffp[cpos] + zoff*i + currImgWidth*ypos + xpos);
	} 
	break;

      case IM_UINT2:
	if ((currBuffp != 0) && (currBuffp[cpos] != 0)) {
	    for (int i = 0 ; i < currImgThickness ; i++) 
		returnInfo[i] = (double) *((unsigned short *)currBuffp[cpos] + zoff*i + currImgWidth*ypos + xpos);
	} 
	break;
	
      case IM_INT4:
      case IM_UINT4:
	if ((currBuffp != 0) && (currBuffp[cpos] != 0)) {
	    for (int i = 0 ; i < currImgThickness ; i++) 
		returnInfo[i] = (double) *((int *)currBuffp[cpos] + zoff*i + currImgWidth*ypos + xpos);
	}
	break;

      case IM_FLOAT:
	if ((currBuffp != 0) && (currBuffp[cpos] != 0)) {
	    for (int i = 0 ; i < currImgThickness ; i++) 
		returnInfo[i] = (double)  *((float *)currBuffp[cpos] + zoff*i + currImgWidth*ypos + xpos);
	} 
	break;
	
      case IM_DOUBLE:
	if ((currBuffp != 0) && (currBuffp[cpos] != 0)) {
	    for (int i = 0 ; i < currImgThickness ; i++) 
		returnInfo[i] = *((double *)currBuffp[cpos] + zoff*i + currImgWidth*ypos + xpos);
	} 
	break;
	
      default:
	for (int i = 0 ; i < currImgThickness ; i++)
	    returnInfo[i] = 0.0;
	break;
    }
    return returnInfo;
}



// helps to decide whether current image has more data than meet the eye.
int imageIO::hasRawData(void)
{
    int retval = 0;

    if ((xOffset != 0)
	|| (yOffset != 0)
	|| (zOffset != 0)
	|| (currImgThickness > 1)) {
	retval = 1;
    } else if ((currBuffp != 0) && (currBuffp[0] != 0)) {
	retval = 2;
    }
	

    return retval;
    
}

// returns the content of the image along the
// bresenham line given by its start and end points.
// allocates the buffers!
void imageIO::getLineDataVectors(int X2, int Y2, int X, int Y,
				 double *&red, double *&green, double *&blue,
				 int &nb)
{
    int delta, i;

    // sanity check, X2 and Y2 are assumed OK
    if (X >= imageWidth())
	X = imageWidth() - 1;
    if (X < 0)
	X = 0;
    if (Y >= imageHeight())
	Y = imageHeight() - 1;
    if (Y < 0)
	Y = 0;
    
    nb = trivmax(abs(X2-X), abs(Y2-Y));
    nb = trivmax(nb, 1);  // at least one value is returned.
    
    if (imspp == 1) {
	red = new double[nb];
	green = blue = 0;
	delta = bresinit(X2,Y2,X,Y);
	i = 0;
	do {
	    getRawDataPoint(delta, red[i]) ;
            ++i;
	} while ((delta = bresgetnext())> 0 && (i < nb));
    } else {
	red = new double[nb];
	green = new double[nb];
	blue = new double[nb];
	delta = bresinit(X2,Y2,X,Y);
	i = 0;
	do {
            getRawDataPoint(delta, red[i], green[i], blue[i]);
            ++i;
	} while ((delta = bresgetnext())> 0 && (i < nb));
    }

    if (i != nb) {
	dbgprintf("Adjusting profile vector length from %d to %d\n", nb, i);
	nb = i;
    }
    
    return;
}

// the well-known Bresenham algorithm in a flexible incarnation
int imageIO::bresinit(int x1_, int y1_, int x2_, int y2_)
{
    sizex_ = imageWidth();
    dx_ = x2_ - x1_;
    dy_ = y2_ - y1_;
    if (dx_ < 0) dx_ = -dx_;
    if (dy_ < 0) dy_ = -dy_;
    if (x2_ < x1_)
	incx_ = -1;
    else
	incx_ = 1;
    if (y2_ < y1_)
	incy_ = -1;
    else
	incy_ = 1;

    xpos_ = x1_;
    ypos_ = y1_;

    if (dx_ > dy_) {
	e_ = 2*dy_ - dx_;
	inc1_ = 2*(dy_-dx_);
	inc2_ = 2*dy_;
    } else {
	e_ = 2*dx_ - dy_;
	inc1_ = 2*(dx_-dy_);
	inc2_ = 2*dx_;
    }
    i_ = 0;
    
    return (xpos_ + ypos_ * sizex_);
}

int imageIO::bresgetnext(void)
{
    if (dx_ > dy_) {
	if (++i_ >= dx_)
	    return -1;
	else {
	    if (e_ >= 0) {
		ypos_ += incy_;
		e_ += inc1_;
	    }
	    else e_ += inc2_;
	    xpos_ += incx_;
	    return (xpos_ + ypos_ * sizex_);
	}
    } else {
	if (++i_ >= dy_)
	    return -1;
	else {
	    if (e_ >= 0) {
		xpos_ += incx_;
		e_ += inc1_;
	    }
	    else e_ += inc2_;
	    ypos_ += incy_;
	    return (xpos_ + ypos_ * sizex_);
	}
    }
}

void imageIO::translateForDisplay(bool keepOverlay)
{
    uchar *newbuf;
    int    newspp;
    bool   freeBuffers = !keepOverlay;
    bool   makeChar = (makeImageCharIfFits > 0);
    bool   interpretRGB = (RGBdisplaysRGB > 0);
    bool   minmaxAllSamples = (fitCharOverAllSamples > 0);
    bool   minmaxAllSlices = (fitCharOverAllSlices > 0);
    
    dbgprintf("Image is [%d]x[%d]x[%d]\n",
	      currImgWidth, currImgHeight, currImgThickness);

    if (currBuffp != 0) { // translation is required
	switch (currPixType) {
        case IM_BINARY:
	    convertBINARY(&newspp, &newbuf); 
	    break;
	
        case IM_INT1: // this is dodgy
        case IM_UINT1:
	    convertUINT1(&newspp, &newbuf, freeBuffers, interpretRGB);
	    break;

	    // the template convert function has more features
#ifdef HAVE_MEMBER_TEMPLATES
        case IM_INT2:
            convert<short>(&newspp, &newbuf,
                           freeBuffers, makeChar, interpretRGB, minmaxAllSamples, minmaxAllSlices);
            break;

        case IM_UINT2:
            convert<unsigned short>(&newspp, &newbuf,
                                    freeBuffers, makeChar, interpretRGB, minmaxAllSamples, minmaxAllSlices);
            break;

        case IM_INT4:
            convert<int>(&newspp, &newbuf,
                         freeBuffers, makeChar, interpretRGB, minmaxAllSamples, minmaxAllSlices);
            break;

        case IM_UINT4:
            convert<unsigned int>(&newspp, &newbuf,
                                  freeBuffers, makeChar, interpretRGB, minmaxAllSamples, minmaxAllSlices);
            break;

        case IM_FLOAT:
            convert<float>(&newspp, &newbuf,
                           freeBuffers, false, interpretRGB, minmaxAllSamples, minmaxAllSlices);
            break;

        case IM_DOUBLE:
	    convert<double>(&newspp, &newbuf,
			    freeBuffers, false, interpretRGB, minmaxAllSamples, minmaxAllSlices);
	    break;
	    
#else
            // more restricted set of transforms with fewer features
        case IM_INT2:
	    convertINT2(&newspp, &newbuf);
	    break;

        case IM_UINT2:
	    convertUINT2(&newspp, &newbuf);
	    break;
	    
        case IM_INT4:
	    convertINT4(&newspp, &newbuf);
	    break;

        case IM_UINT4:
	    convertUINT4(&newspp, &newbuf);
	    break;
	    
        case IM_DOUBLE:
	    convertDOUBLE(&newspp, &newbuf);
	    break;
#endif
        default:
	    errprintf("imtranslate: format as yet unsupported, type=%s\n",
		      typeName(currPixType));
	    freeCurrentBuffers();
	    // fake buffer to get through this tough point
	    newbuf = new uchar[currImgWidth*currImgHeight];
	    newspp = 1;
	    break;		
	}
	if (newbuf != 0) {
	    delete[] imdata;
	    
	    imdata = newbuf;
//	    if (!keepOverlay) {
//		delete[] overlay;
//		overlay = 0;
//	    }
	    imspp = newspp;
	}
    } else { // no translation required
	imspp = currImgNbSamples;
    }
}


// THis applies the clut that has been saved
int imageIO::applyCurrentCLUT(void)
{
    if (currImgHasCLUT)
	return applyCLUTtoCurrentImage(currImgCLUTPath);
    else 
	return 0; // not an error, but do nothing.
}


// there are 2 strategies to apply a CLUT, it could be either
// on the raw data, or on the converted 8 bit data.
// The former can only be applied on a subset of data:
//   - char
//   - int
//   - single spectra images (it creates RGB images)
// the latter can be applied to any kind of data and an
//    arbitrary multispectral image (but not already RGB!)
// The only advandage of the former is that when applying
// a region-type CLUT on a labelled (int) image, neighboring regions
// will have different colours, which is highly desirable for
// identification purposes.
//
// This function now performs both, with a switch to decide
// which strategy should be considered
// 
// Hugues Talbot	 3 Jul 1999
//
// this function applies a LUT to an already-read single-sample image
int imageIO::applyCLUTtoCurrentImage(const char *CLUTpath, bool useRaw)
{
    int    retval = 0;
    uchar *olddata ;
    int    olddepth;
    uchar  theCLUT[3][256];

    // this is indispensable
    olddata = imdata;
    olddepth = imspp;
    
    dbgprintf("Applying CLUT %s to current image\n", CLUTpath);
    if (lutWraparound) {
	dbgprintf("Applying CLUT on Raw data, if possible\n");
	if (currImgNbSamples == 1) {
	    // then we can do something...
	    dbgprintf("Expected number of samples: 1\n");

	    // fill up the return data as best we can
	    currImgHasCLUT = false;
	    currImgCLUTPath = 0;

	    if (currBuffp == 0) {
		// the raw data buffer has been freed, therefore datatype should be char
		if ((currPixType != IM_BINARY)
		    && (currPixType != IM_UINT1)
		    && (currPixType != IM_INT1)) {
		    errprintf("Raw data not present, yet image is of type %d (%s): this is a bug\n",
			      currPixType, typeName(currPixType));
		    retval = 1;
		} else {
		    dbgprintf("Recycling the old data\n");
		    if ((currBuffp = (void **)malloc(sizeof(void *))) == 0) {
			errprintf("ApplyCLUTtoCurrentImage: not enough memory!\n");
			retval = 2;
		    } else if ((currBuffp[0] = (uchar *)malloc(currImgWidth*currImgHeight*sizeof(uchar))) == 0) {
			free(currBuffp);
			currBuffp = 0;
			errprintf("ApplyCLUTtoCurrentImage: not enough memory!\n");
			retval = 3;
		    } else {
			memcpy(currBuffp[0], imdata, currImgWidth*currImgHeight*sizeof(uchar));
		    }
		}
	    }
	    // a new currBuffp may just have been allocated...
	    if ((currBuffp != 0) && (currBuffp[0] != 0)) {
		dbgprintf("Current image is [%d]x[%d]x[%d]\n",
			  currImgWidth, currImgHeight, currImgThickness);
		// read the CLUT in
		if (checkForClut(CLUTpath, 0, theCLUT)) {
		    switch (currPixType) {
		      case IM_BINARY:
		      case IM_UINT1:
		      case IM_INT1:
			dbgprintf("Current pixeltype is binary or char\n");
			convertUINT1withLUT(&imspp, &imdata, theCLUT);
			currImgHasCLUT = true; // current image is shown with colourmap
			currImgCLUTPath = CLUTpath;
			break;

		      case IM_INT4:
		      case IM_UINT4:
			dbgprintf("Current pixeltype is int\n");
			convertINT4withLUT_wraparound(&imspp, &imdata, theCLUT);
			currImgHasCLUT = true;
			currImgCLUTPath = CLUTpath;
			break;

#ifdef HAVE_MEMBER_TEMPLATES
		      case IM_INT2:
			dbgprintf("Current pixeltype is short\n");
			convertWithLUT_wraparound<short>(&imspp, &imdata, theCLUT);
			currImgHasCLUT = true;
			currImgCLUTPath = CLUTpath;
			break;
		      case IM_UINT2:
			dbgprintf("Current pixeltype is unsigned short\n");
			convertWithLUT_wraparound<unsigned short>(&imspp, &imdata, theCLUT);
			currImgHasCLUT = true;
			currImgCLUTPath = CLUTpath;
			break;
		      case IM_FLOAT:
			dbgprintf("Current pixeltype is float\n");
			convertWithLUT_wraparound<float>(&imspp, &imdata, theCLUT);
			currImgHasCLUT = true;
			currImgCLUTPath = CLUTpath;
			break;
		      case IM_DOUBLE:
			dbgprintf("Current pixeltype is double\n");
			convertWithLUT_wraparound<double>(&imspp, &imdata, theCLUT);
			currImgHasCLUT = true;
			currImgCLUTPath = CLUTpath;
			break;
		      default:
			errprintf("Colour maps cannot be applied to type %d (%s)\n", currPixType, typeName(currPixType));
			retval = 4;
			break;
#else
		      default:
			errprintf("Colour maps cannot be applied to type %d (%s)\n", currPixType, typeName(currPixType));
			retval = 4;
			break;
#endif
		
		    }
		} else {
		    errprintf("Colour LUT %s couldn't be read\n", CLUTpath);
		    retval = 5;
		}
	    } else {
		if (currBuffp) {
		    errprintf("Pointer to raw data lost: this is a bug\n");
		    retval = 6;
		}
	    }
	} else {
	    errprintf("Cannot apply Colour LUT to image with %d samples\n", currImgNbSamples);
	    retval = 7;
	}

    } else {
	uchar *newData; // we need this to avoid circular references

	dbgprintf("Applying CLUT on imdata buffer, if possible\n");
	if ((imspp != 1) && !currImgHasCLUT){ 
	    errprintf("The image is currently displayed as an RGB image,\n"
		      "applying a CLUT to an RGB image is impossible");
	} else {
	    if ( (imspp != 1) && currImgHasCLUT) {
		// the RGB problem is due to a pre-applied CLUT
		// revert to grey-level first
		translateForDisplay(); // this can obviously creates a new imdata
		if (imdata != olddata) {
		    // the current olddata is now invalid (points to old imdata, no longer
		    // the present imdata). The old imdata has already been freed!
		    olddata = imdata;
		}
	    }
	
	    saveToRawBuffers(true); // save original data to raw buffers if necessary
	    if (checkForClut(CLUTpath, 0, theCLUT)) {
		convertDirectLUT(&imspp, &newData, theCLUT);
		currImgHasCLUT = true; // current image is shown with colourmap
		currImgCLUTPath = CLUTpath;
		imdata = newData;
	    } else {
		currImgHasCLUT = false;
		currImgCLUTPath = 0;
	    }	
	}
    }
    
    if (imdata != 0 && imdata != olddata) {
	delete[] olddata;
	delete[] overlay;
	overlay = 0;
	olddata = 0; // ?
    } else {
	imdata = olddata;
	imspp = olddepth;
    }

    //if (applyTransferToAll)
    //	 applyGammaToCurrentImage(); // this means reuse predefined gamma if present
    
    return retval;
}


#if 0

// this function applies a look-up table to an existing image
// gamma is applied on the data about to be displayed...
// Hugues Talbot  4 Mar 1999: this is a bit confusing:
// if reset is true, map is 0 
//       delete the old buffer and return
// if reset is false and map is 0:
//       apply the old map to the new image
// if reset is false and map is non-null:
//       store the new map and apply it to the new image.
//
// Default is reset = false, map = 0.

int imageIO::oldapplyGammaToCurrentImage(bool reset, uchar *map)
{
    int    retval = 0;
    static uchar *olddata = 0;
    static uchar *oldmap = 0;
    uchar  *p, *q, *end;
    long   nbpix;

    if (imdata != 0) {
	if (reset) {
	    if (olddata) {
		delete[] olddata;
		olddata = 0;
	    }
	    return retval; 
	}
	nbpix = currImgWidth * currImgHeight * imspp;
	if (olddata == 0) {
	    // this is the first time: we allocate & copy the data
	    olddata = new uchar[nbpix];
	    memcpy(olddata, imdata, nbpix);
	}
	 // we apply the gamma
	p = olddata;
	q = imdata;
	end = p + nbpix;
	if (map != 0) {
	    while (p!=end) {
		*q++ = map[*p++]; 
	    }
	    oldmap = map;
	} else if (oldmap != 0) {
	    while (p!=end) {
		*q++ = oldmap[*p++]; 
	    }
	}
	// else nothing gets changed
    } 
    
    return retval;
}

#endif

//
// To save space, when reading in RGB or GL images,
// the raw buffers (currBuffp) are not used.
// we need them again when we muck around with the
// data, such as imposing a gamma, etc.
// This saves back the `imdata' data to the raw buffers.
// The one argument tells this function to use only
// one buffer for grey images if possible. If the
// data is colour RGB 3 buffers will be used anyway,
// but even for grey images we might want to save the
// data in 3 buffers, for example when imposing a
// tranform on the red channel and not the others. The
// input might be Grey but the output will be colour...
// /
// Hope this makes sense. Hugues Talbot	29 May 1999
int imageIO::saveToRawBuffers(bool keepGreyIfPossible)
{
    int retval = 0;
    
    if (currBuffp == 0) { // the data is not already in the raw buffers.
	if (imspp == 1) {
	    if (keepGreyIfPossible) {
		// the transformation remains grey-level
	    
		// the raw data buffer has been freed, therefore datatype should be char
		if ((currPixType != IM_BINARY)
		    && (currPixType != IM_UINT1)
		    && (currPixType != IM_INT1)) {
		    errprintf("Raw data not present, yet image is of type %d (%s): this is a bug\n",
			      currPixType, typeName(currPixType));
		    retval = 1;
		} else {
		    dbgprintf("Saving the old data to cuffBuffp\n");
		    if ((currBuffp = (void **)malloc(sizeof(void *))) == 0) {
			errprintf("saveToRawBuffers: not enough memory!\n");
			retval = 2;
		    } else if ((currBuffp[0] = (uchar *)malloc(currImgWidth*currImgHeight*sizeof(uchar))) == 0) {
			free(currBuffp);
			currBuffp = 0;
			errprintf("saveToRawBuffers: not enough memory!\n");
			retval = 3;
		    } else {
			memcpy(currBuffp[0], imdata, currImgWidth*currImgHeight*sizeof(uchar));
			dontFreeBuffers = false; // we _can_ free these buffers!
		    }
		}
	    } else {
		// the output image will not be grey-level anymore!
		// the raw data buffer has been freed, therefore datatype should be char
		if ((currPixType != IM_BINARY)
		    && (currPixType != IM_UINT1)
		    && (currPixType != IM_INT1)) {
		    errprintf("Raw data not present, yet image is of type %d (%s): this is a bug\n",
			      currPixType, typeName(currPixType));
		    retval = 1;
		} else {
		    dbgprintf("Saving the old data to cuffBuffp AS COLOUR !\n");
		    if ((currBuffp = (void **)malloc(3*sizeof(void *))) == 0) {
			errprintf("saveToRawBuffers: not enough memory!\n");
			retval = 2;
		    } else if ((currBuffp[0] = (uchar *)malloc(currImgWidth*currImgHeight*sizeof(uchar))) == 0) {
			free(currBuffp);
			currBuffp = 0;
			errprintf("saveToRawBuffers: not enough memory for first component!\n");
			retval = 3;
		    } else if ((currBuffp[1] = (uchar *)malloc(currImgWidth*currImgHeight*sizeof(uchar))) == 0) {
			free(currBuffp[0]);
			free(currBuffp);
			currBuffp = 0;
			errprintf("saveToRawBuffers: not enough memory for second component!\n");
			retval = 3;
		    } else if ((currBuffp[2] = (uchar *)malloc(currImgWidth*currImgHeight*sizeof(uchar))) == 0) {
			free(currBuffp[1]);
			free(currBuffp[0]);
			free(currBuffp);
			currBuffp = 0;
			errprintf("saveToRawBuffers: not enough memory for third component!\n");
			retval = 3;
		    } else {
			memcpy(currBuffp[0], imdata, currImgWidth*currImgHeight*sizeof(uchar));
			memcpy(currBuffp[1], imdata, currImgWidth*currImgHeight*sizeof(uchar));
			memcpy(currBuffp[2], imdata, currImgWidth*currImgHeight*sizeof(uchar));
			imspp = 3;
			currImgNbSamples = 3;
			dontFreeBuffers = false; // these buffers _can_ be freed
		    }
		}
	    }
	} else if (imspp == 3) {
	    // the raw data buffer has been freed, therefore datatype should be char
	    if ((currPixType != IM_BINARY)
		&& (currPixType != IM_UINT1)
		&& (currPixType != IM_INT1)) {
		errprintf("Raw data not present, yet image is of type %d (%s): this is a bug\n",
			  currPixType, typeName(currPixType));
		retval = 1;
	    } else {
		dbgprintf("Saving the old data to currBuffp\n");
		if ((currBuffp = (void **)malloc(3*sizeof(void *))) == 0) {
		    errprintf("saveToRawBuffers: not enough memory!\n");
		    retval = 2;
		} else if ((currBuffp[0] = (uchar *)malloc(currImgWidth*currImgHeight*sizeof(uchar))) == 0) {
		    free(currBuffp);
		    currBuffp = 0;
		    errprintf("saveToRawBuffers: not enough memory for first component!\n");
		    retval = 3;
		} else if ((currBuffp[1] = (uchar *)malloc(currImgWidth*currImgHeight*sizeof(uchar))) == 0) {
		    free(currBuffp[0]);
		    free(currBuffp);
		    currBuffp = 0;
		    errprintf("saveToRawBuffers: not enough memory for second component!\n");
		    retval = 3;
		} else if ((currBuffp[2] = (uchar *)malloc(currImgWidth*currImgHeight*sizeof(uchar))) == 0) {
		    free(currBuffp[1]);
		    free(currBuffp[0]);
		    free(currBuffp);
		    currBuffp = 0;
		    errprintf("saveToRawBuffers: not enough memory for third component!\n");
		    retval = 3;
		} else {
		    int i, j;
		    uchar *p = imdata;
		    
		    // actually saving the data
		    for (i = 0, j = 0 ; i < currImgWidth*currImgHeight ; i++) {
			((uchar *)currBuffp[0])[j] = *p++;
			((uchar *)currBuffp[1])[j] = *p++;
			((uchar *)currBuffp[2])[j] = *p++;
			++j;
		    }
		    dontFreeBuffers = false; // these buffers _can_ be freed
		}
	    }
	}
    } // if (currBuffp == 0) ... : else nothing to do.

    return retval;
}

// this function saves the gamma parameters so we
// can call the `applyGamma' function without needing
// to know them. Pretty obvious stuff.
void imageIO::saveGammaParameters(double *x0, double *y0,
				  double *x1, double *y1,
				  double *gammas,
				  uchar *Rmap, uchar *Gmap, uchar *Bmap)
{
    int i;

    for (i = 0 ; i < 3 ; i++) {
	mx0[i] = x0[i];
	my0[i] = y0[i];
	mx1[i] = x1[i];
	my1[i] = y1[i];
	mgammas[i] = gammas[i];
    }

    if (Gmap && Bmap) {
	for (i = 0 ; i < 256 ; i++) {
	    mRmap[i] = Rmap[i];
	    mGmap[i] = Gmap[i];
	    mBmap[i] = Bmap[i];
	}
	hasGmap = true;
    } else {
	for (i = 0 ; i < 256 ; i++) 
	    mRmap[i] = Rmap[i];
	hasGmap = false;
    }

    return;
}

// This function is the same as the next one.
// It only applies a gamma that has been saved before.
int imageIO::applyGammaToCurrentImage(void)
{

    if (mgammas[0] != -1.0) { // there is a gamma to worry about
	// relay the call
	if (hasGmap) 
	    return applyGammaToCurrentImage(mx0, my0, mx1, my1,
					    mgammas, mRmap, mGmap, mBmap);
	else
	    return applyGammaToCurrentImage(mx0, my0, mx1, my1,
					    mgammas, mRmap, 0, 0);
    } else {
	// just make up a new imdata with correct data in
	translateForDisplay();
    }

    return 0;
}


int imageIO::applyGammaToCurrentImage(double *x0, double *y0,
				      double *x1, double *y1,
				      double *gammas,
				      uchar *Rmap, uchar *Gmap, uchar *Bmap)
{
    int    retval = 0;
    uchar *olddata;
    int    olddepth, newdepth;

    // fill up the return data as best we can
    olddata = imdata;
    olddepth = imspp;
    currImgHasGamma = false;
    
    dbgprintf("Applying transfer function (gamma) transform to current image\n");

    if ( (Gmap == 0) || (Bmap == 0)) {
	saveToRawBuffers(true); // try to keep the image grey-level
    } else {
	saveToRawBuffers(false); // we have to assume the 3 maps will be different, and the output will be RGB
    }
    
    // after this very complicated point, 
    saveGammaParameters(x0,  y0, x1,  y1, gammas,
			Rmap,  Gmap,  Bmap);
    

    // a new currBuffp may just have been allocated...
    if ((currBuffp != 0) && (currBuffp[0] != 0)) {
	dbgprintf("Current image is [%d]x[%d]x[%d]\n",
		  currImgWidth, currImgHeight, currImgThickness);
	// read the CLUT in
	switch (currPixType) {
        case IM_BINARY:
        case IM_UINT1:
        case IM_INT1:
	    dbgprintf("Current image is binary or char\n");
	    applyGamma_UINT1(&newdepth, &imdata, Rmap, Gmap, Bmap);
	    currImgHasGamma = true; // current image is shown with colourmap
	    break;

#ifdef HAVE_MEMBER_TEMPLATES
        case IM_INT2:
	    dbgprintf("Current image is short\n");
	    applyGamma<short>(&newdepth, &imdata, x0, y0, x1, y1, gammas);
	    currImgHasGamma = true;
	    break;

        case IM_UINT2:
	    dbgprintf("Current image is unsigned short\n");
	    applyGamma<unsigned short>(&newdepth, &imdata, x0, y0, x1, y1, gammas);
	    currImgHasGamma = true;
	    break;

        case IM_INT4:
	    dbgprintf("Current image is int\n");
	    applyGamma<int>(&newdepth, &imdata, x0, y0, x1, y1, gammas);
	    currImgHasGamma = true;
	    break;

        case IM_UINT4:
	    dbgprintf("Current image is unsigned int\n");
	    applyGamma<unsigned int>(&newdepth, &imdata, x0, y0, x1, y1, gammas);
	    currImgHasGamma = true;
	    break;

        case IM_FLOAT:
	    dbgprintf("Current image is float\n");
	    applyGamma<float>(&newdepth