/*
 *
 * $Header: /usr/u/wjr/src/ADT/RCS/image.h,v 2.3 1994/08/22 16:07:24 wjr Exp $
 *
 * Copyright (c) 1990, 1991, 1992, 1993 Cornell University.  All Rights
 * Reserved.
 *
 * Copyright (c) 1991, 1992 Xerox Corporation.  All Rights Reserved.
 *  
 * Use, reproduction, preparation of derivative works, and distribution
 * of this software is permitted.  Any copy of this software or of any
 * derivative work must include both the above copyright notices of
 * Cornell University and Xerox Corporation and this paragraph.  Any
 * distribution of this software or derivative works must comply with all
 * applicable United States export control laws.  This software is made
 * available AS IS, and XEROX CORPORATION DISCLAIMS ALL WARRANTIES,
 * EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE,
 * AND NOTWITHSTANDING ANY OTHER PROVISION CONTAINED HEREIN, ANY
 * LIABILITY FOR DAMAGES RESULTING FROM THE SOFTWARE OR ITS USE IS
 * EXPRESSLY DISCLAIMED, WHETHER ARISING IN CONTRACT, TORT (INCLUDING
 * NEGLIGENCE) OR STRICT LIABILITY, EVEN IF XEROX CORPORATION IS ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGES.
 */
/*
 * Header file for image maintenance routines
 */

#ifndef IMAGE_H
#define IMAGE_H

/* For FILE */
#include <stdio.h>

#ifdef	IMAGE_CHECKALL
/* Need eassert() and panic() */
#include "panic.h"
#endif

/* For boolean */
#include "misc.h"

/* The number of colours per component (or total for gray images) */
#define	COLDEPTH	8
#define COLRNG		(1 << COLDEPTH)

#ifndef	IMAGE_OLD_RGB

typedef struct {
    unsigned char r, g, b;
    } RGB;

#else

typedef unsigned char RGB[3];

#endif

typedef unsigned char ImageType;

/* The image types */
#define	IMAGE_GRAY	((ImageType)1)
#define	IMAGE_FLOAT	((ImageType)2)
#define	IMAGE_RGB	((ImageType)3)
#define	IMAGE_DOUBLE	((ImageType)4)
#define	IMAGE_BINARY	((ImageType)5)
#define	IMAGE_LONG	((ImageType)6)
#define	IMAGE_PTR	((ImageType)7)
#define	IMAGE_SHORT	((ImageType)8)

/* Several types of image */
typedef struct {
    unsigned char **data;
    struct _ImageHeader *header;
    } *GrayImage;
typedef unsigned char *	GrayPixPtr;

typedef struct {
    float **data;
    struct _ImageHeader *header;
    } *FloatImage;
typedef float *		FloatPixPtr;

typedef struct {
    RGB **data;
    struct _ImageHeader *header;
    } *RGBImage;
typedef RGB *		RGBPixPtr;

typedef struct {
    double **data;
    struct _ImageHeader *header;
    } *DoubleImage;
typedef double *	DoublePixPtr;

typedef struct {
    char **data;
    struct _ImageHeader *header;
    } *BinaryImage;
typedef char *		BinaryPixPtr;

typedef struct {
    long **data;
    struct _ImageHeader *header;
    } *LongImage;
typedef long *		LongPixPtr;

typedef struct {
    void ***data;
    struct _ImageHeader *header;
    } *PtrImage;
typedef void **		PtrPixPtr;

typedef struct {
    short **data;
    struct _ImageHeader *header;
    } *ShortImage;
typedef short *		ShortPixPtr;

/* An invisible image header type */
struct _ImageHeader {
    unsigned width, height;
    int xbase, ybase;
    ImageType tag;
    char *comment;
    union {
	GrayImage grayptr;
	FloatImage floatptr;
	RGBImage RGBptr;
	DoubleImage doubleptr;
	BinaryImage binaryptr;
	LongImage longptr;
	PtrImage ptrptr;
	ShortImage shortptr;
	} body;
    };
typedef struct _ImageHeader ImageHeader;

/* and some manipulation routines */
extern void *imNew(ImageType type, unsigned width, unsigned height);
extern void *imNewOffset(ImageType type, unsigned width, unsigned height,
			 int xbase, int ybase);
extern void im_Free_(void *im, ImageHeader *header);
extern void *imLoad(ImageType type, char *filename);
extern void *imLoadF(ImageType type, FILE *inFile);
extern int im_Save_(void *im, ImageHeader *header, char *filename);
extern int im_SaveF_(void *im, ImageHeader *header, FILE *outFile);
extern void *im_Dup_(void *im, ImageHeader *header, boolean copycomment);
extern void *im_Promote_(void *im, ImageHeader *header, ImageType promoteto);
extern void imSetPromotion(ImageType promotefrom, ImageType promoteto,
			   double slope, double offset);
extern void *im_Demote_(void *im, ImageHeader *header, ImageType demoteto);
extern void *im_Convert_(void *im, ImageHeader *header, ImageType convertto);
extern boolean im_SetComment_(void *im, ImageHeader *header, char *newcomment);

/* and some useful routines */
#ifndef	IMAGE_CHECKALL
#define	imGetWidth(i)	((i)->header->width)
#define	imGetHeight(i)	((i)->header->height)
#define	imGetXBase(i)	((i)->header->xbase)
#define	imGetYBase(i)	((i)->header->ybase)
#define	imGetXMax(i)	(((i)->header->xbase)+(int)((i)->header->width)-1)
#define	imGetYMax(i)	(((i)->header->ybase)+(int)((i)->header->height)-1)
#define imGetType(i)	((i)->header->tag)
#define	imGetComment(i)	((i)->header->comment)
#else
#define	imGetWidth(i)	(eassert((i) != NULL), ((i)->header->width))
#define	imGetHeight(i)	(eassert((i) != NULL), ((i)->header->height))
#define	imGetXBase(i)	(eassert((i) != NULL), ((i)->header->xbase))
#define	imGetYBase(i)	(eassert((i) != NULL), ((i)->header->ybase))
#define	imGetXMax(i)	(eassert((i) != NULL), (((i)->header->xbase)+(int)((i)->header->width)-1))
#define	imGetYMax(i)	(eassert((i) != NULL), (((i)->header->ybase)+(int)((i)->header->height)-1))
#define imGetType(i)	(eassert((i) != NULL), ((i)->header->tag))
#define	imGetComment(i)	(eassert((i) != NULL), ((i)->header->comment))
#endif

/*
 * These macros are strange - they attempt to make imFree etc somewhat
 * polymorphic by figuring out, in a context where the type is known (at
 * compile-time), the type of the image and passing that in. If it was
 * not done this way, imFree etc would not be able to figure out what
 * sort of Image they had been passed (since the structures may not match).
 */
#ifndef	IMAGE_CHECKALL
#define imFree(i)	im_Free_((void *)(i), ((i)->header))
#define	imSetOffset(i, nx, ny)	im_SetOffset_((i), ((i)->header), (nx), (ny))
#define imSave(i, n)	im_Save_((void *)(i), ((i)->header), (n))
#define imSaveF(i, f)	im_SaveF_((void *)(i), ((i)->header), (f))
#define	imDup(i)	im_Dup_((void *)(i), ((i)->header), TRUE)
#define	imPromote(i, t)	im_Promote_((void *)(i), ((i)->header), (t))
#define	imDemote(i, t)	im_Demote_((void *)(i), ((i)->header), (t))
#define	imConvert(i, t)	im_Convert_((void *)(i), ((i)->header), (t))
#define	imSetComment(i, c)	im_SetComment_((void *)(i), ((i)->header), (c))
#else
#define imFree(i)	(eassert((i) != NULL), (im_Free_((void *)(i), ((i)->header))))
#define	imSetOffset(i,nx,ny)	(eassert((i) != NULL), (im_SetOffset_((i), ((i)->header), (nx), (ny))))
#define imSave(i, n)	(eassert((i) != NULL), (im_Save_((void *)(i), ((i)->header), (n))))
#define imSaveF(i, f)	(eassert((i) != NULL), (im_SaveF_((void *)(i), ((i)->header), (f))))
#define	imDup(i)	(eassert((i) != NULL), (im_Dup_((void *)(i), ((i)->header), TRUE)))
#define	imPromote(i, t)	(eassert((i) != NULL), (im_Promote_((void *)(i), ((i)->header), (t))))
#define	imDemote(i, t)	(eassert((i) != NULL), (im_Demote_((void *)(i), ((i)->header), (t))))
#define	imConvert(i, t)	(eassert((i) != NULL), (im_Convert_((void *)(i), ((i)->header), (t))))
#define	imSetComment(i, c)	(eassert((i) != NULL), im_SetComment_((void *)(i), ((i)->header), (c)))

#endif

/* and an access routine - gives an *lvalue* */
#ifndef	IMAGE_CHECKALL
#define	imRef(i, x, y)	(((i)->data)[(y)][(x)])
#else
static char __imPanicStr[100];
#define	imRef(i, x, y)	(eassert((i) != NULL), ((((int)(x) < (int)(i)->header->xbase) || ((int)(y) < (int)(i)->header->ybase) || ((int)(x) >= (int)(i)->header->xbase + (int)(i)->header->width) || ((int)(y) >= (int)(i)->header->ybase + (int)(i)->header->height)) ? ({ sprintf(__imPanicStr, "bounds check fail: %d <= %d < %d, %d <= %d < %d", (int)(i)->header->xbase, (int)(x), (int)(i)->header->xbase + (int)(i)->header->width, (int)(i)->header->ybase, (int)(y), (int)(i)->header->ybase + (int)(i)->header->height); panic(__imPanicStr);}), ((i)->data[0][0]) : (((i)->data)[(y)][(x)])))
#endif

/* Here come macros for increased efficiency access */

#ifndef	IMAGE_CHECKALL

#define	imGetPixPtr(i, x, y) (&(imRef((i), (x), (y))))
#define	imPtrRef(i, pp) (*(pp))
#define	imPtrDup(i, pp) (pp)
#define	imPtrUp(i, pp) ((pp) -= (i)->header->width)
#define	imPtrDown(i, pp) ((pp) += (i)->header->width)
#define	imPtrLeft(i, pp) ((pp)--)
#define	imPtrRight(i, pp) ((pp)++)
#define	imPtrEq(i, pp1, pp2) ((pp1) == (pp2))

#else

/* Check a pointer against bounds; eval expr if successful */
#define	__IMPCHECK(i, pp, expr) (eassert((i) != NULL), ((((pp) < (&((i)->data[(i)->header->ybase][(i)->header->xbase]))) || ((pp) > &((i)->data[(i)->header->ybase+(int)(i)->header->height-1][(i)->header->xbase+(int)(i)->header->width-1]))) ? (({ panic("PixPtr bounds check fail"); }), (expr)) : (expr)))

/* imRef is error-checked already, but just saying &imRef doesn't work... */
#define	imGetPixPtr(i, x, y)	(eassert((i) != NULL), ((((int)(x) < (int)(i)->header->xbase) || ((int)(y) < (int)(i)->header->ybase) || ((int)(x) >= (int)(i)->header->xbase + (int)(i)->header->width) || ((int)(y) >= (int)(i)->header->ybase + (int)(i)->header->height)) ? ({ sprintf(__imPanicStr, "bounds check fail: %d <= %d < %d, %d <= %d < %d", (int)(i)->header->xbase, (int)(x), (int)(i)->header->xbase + (int)(i)->header->width, (int)(i)->header->ybase, (int)(y), (int)(i)->header->ybase + (int)(i)->header->height); panic(__imPanicStr);}), (&((i)->data[0][0])) : (&(((i)->data)[(y)][(x)]))))
#define	imPtrRef(i, pp) __IMPCHECK((i), (pp), (*(pp)))
#define imPtrDup(i, pp) __IMPCHECK((i), (pp), (pp))
#define	imPtrUp(i, pp) __IMPCHECK((i), (pp), ((pp) -= (i)->header->width))
#define	imPtrDown(i, pp) __IMPCHECK((i), (pp), ((pp) += (i)->header->width))
#define	imPtrLeft(i, pp) __IMPCHECK((i), (pp), ((pp)--))
#define	imPtrRight(i, pp) __IMPCHECK((i), (pp), ((pp)++))
#define	imPtrEq(i, pp1, pp2) __IMPCHECK((i), (pp1), __IMPCHECK((i), (pp2), ((pp1) == (pp2))))

#endif


#endif
