/*
$Revision: 5.0 $
*/
/*
$Log: readWriteXWD.c,v $
Revision 5.0  2004/12/11 11:42:19  keidel
*** empty log message ***

Revision 4.0  2004/02/28 09:53:00  keidel
Major Version

Revision 3.2  2002/11/01 09:33:10  keidel
added ThumbIndex PD-widget
uil support for PD-widgets

*/
/*
        mindlib/readWriteXWD.c

        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, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "../include/Image.h"
#include <stdio.h>
#include <string.h>

#include <X11/XWDFile.h>

/*
**  Some version of XWDFile.h don't have the 'typedef' infront of
**    the XWDColor definition.
*/
typedef struct {
        CARD32  pixel B32;
        CARD16  red B16;
        CARD16  green B16;
        CARD16  blue B16;
        CARD8   flags;
        CARD8   pad;
} myXWDColor;

#define XWDColor	myXWDColor

#ifdef SCO
#define putc(c, fp) fprintf(fp,"%c",c)
#define getc(fp) mygetc(fp)

char mygetc(FILE *fp)
{
  char l;
  fread(&l,1,1,fp);
  return l;
}
void *calloc(int, int);
#endif


#define True	1
#define False	0

#define LSBFirst	0
#define MSBFirst	1

#define XYBitmap	0
#define XYPixmap	1
#define ZPixmap		2

#define StaticGray	0
#define GrayScale	1
#define StaticColor	2
#define PseudoColor	3
#define TrueColor	4
#define DirectColor	5

#if XWD_FILE_VERSION != 7
*** This is an error ***

Do not know how to write anything but version 7 X window dumps

#endif

/*
**  Some read and write routines for long values
*/

#if 0
static unsigned long readlong(FILE *fp)
{
	unsigned char	a, b, c, d;

	a = getc(fp);
	b = getc(fp);
	c = getc(fp);
	d = getc(fp);

	return ((a & 0xff) << 24) |
	       ((b & 0xff) << 16) |
	       ((c & 0xff) <<  8) |
	        (d & 0xff);
}
#endif

#if 0
static unsigned short readshort(FILE *fp)
{
	unsigned char	a, b;
	a = getc(fp);
	b = getc(fp);

	return ((a & 0xff) << 8) | (b & 0xff);
}
#endif

static void writelong(FILE *fp, long l)
{
	putc( (l >> 24) & 0xff, fp);
	putc( (l >> 16) & 0xff, fp);
	putc( (l >>  8) & 0xff, fp);
	putc(  l        & 0xff, fp);
}
static void writeshort(FILE *fp, unsigned short s)
{
	putc( (s >>  8) & 0xff, fp);
	putc(  s        & 0xff, fp);
}

#if 0
static long swaplong(long l)
{
	unsigned char	b1, b2, b3, b4;

	b1 = (l >> 24) & 0xff;
	b2 = (l >> 16) & 0xff;
	b3 = (l >>  8) & 0xff;
	b4 =  l        & 0xff;

	return ((long)b4 << 24) | ((long)b3 << 16) | (b2 << 8) | b1;
}

static short swapshort(short l)
{
	unsigned char	b1, b2;

	b1 = (l >>  8) & 0xff;
	b2 =  l        & 0xff;

	return (b2 << 8) | b1;
}

static int 		bits_per_item, bits_used, bit_shift, bits_per_pixel;
static unsigned long 	pixel_mask;
static int 		byte_order, bit_order;


static unsigned long getpixnum(FILE *fp)
{
	static unsigned char	b8;
	static unsigned short	b16;
	static unsigned long	b32;
	unsigned long 		val;

	if (bits_used == bits_per_item ) {
		switch (bits_per_item) {
		case 8:
			b8 = getc(fp);
			break;
		case 16:
			b16 = readshort(fp);
			if (byte_order != MSBFirst)
				b16 = swapshort(b16);
			break;
		case 32:
			b32 = readlong(fp);
			if (byte_order != MSBFirst)
				b32 = swaplong(b32);
			break;
		}
		bits_used = 0;

		if (bit_order == MSBFirst)
			bit_shift = bits_per_item - bits_per_pixel;
		else
			bit_shift = 0;
	}

	switch (bits_per_item) {
	case 8:
		val = (b8 >> bit_shift) & pixel_mask;
		break;
	case 16:
		val = (b16 >> bit_shift) & pixel_mask;
		break;
	case 32:
		val = (b32 >> bit_shift) & pixel_mask;
		break;
	}

	if (bit_order == MSBFirst)
		bit_shift -= bits_per_pixel;
	else
		bit_shift += bits_per_pixel;
	bits_used += bits_per_pixel;

	return val;
}
#endif

/*
**  Straight forward writer.
**   Take advantage of the fact that a Grey image is 8 bits deep
**   and some simple properties of colormap images for simplicity.
*/
int WriteXWD(char *file, Image *image)
{
	static char	*name = "XPaint Image";
	XWDColor	color;
	XWDFileHeader	hdr;
	FILE		*fp;
	int		i, x, y;
	int		isBW = 0, isGrey = 0;

	if ((fp = fopen(file, "w")) == NULL)
		return True;

	/* 
	**  Set up the header.
	*/
	hdr.header_size = sizeof(hdr) + strlen(name) + 1;
	hdr.file_version = XWD_FILE_VERSION;
	hdr.pixmap_format = ZPixmap;
	hdr.pixmap_width = image->width;
	hdr.pixmap_height = image->height;
	hdr.xoffset = 0;
	hdr.byte_order = MSBFirst;
	hdr.bitmap_bit_order = MSBFirst;
	hdr.window_width = image->width;
	hdr.window_height = image->height;
	hdr.window_x = 0;
	hdr.window_y = 0;
	hdr.window_bdrwidth = 0;

	if (image->isBW) {
		hdr.pixmap_depth = 1;
		hdr.bits_per_pixel = 1;
		hdr.colormap_entries = image->cmapSize;
		hdr.ncolors = image->cmapSize;
		hdr.bytes_per_line = ( image->width + 7 ) / 8;

		isBW = 1;

		/*
		**  Common between grey and BW
		*/
		hdr.bitmap_unit = 8;
		hdr.bitmap_pad = 8;
		hdr.visual_class = StaticGray;
		hdr.red_mask = 0;
		hdr.green_mask = 0;
		hdr.blue_mask = 0;
	} else if (image->isGrey) {
		hdr.pixmap_depth = 8;
		hdr.bits_per_pixel = 8;
		hdr.colormap_entries = image->cmapSize;
		hdr.ncolors = image->cmapSize;
		hdr.bytes_per_line = image->width;

		isGrey = 1;

		/*
		**  Common between grey and BW
		*/
		hdr.bitmap_unit = 8;
		hdr.bitmap_pad = 8;
		hdr.visual_class = StaticGray;
		hdr.red_mask = 0;
		hdr.green_mask = 0;
		hdr.blue_mask = 0;
	} else if (image->cmapSize > 0) {
		/*
		**  Color mapped image
		*/
		hdr.pixmap_depth = (image->cmapSize > 256) ? 16 : 8;
		hdr.bits_per_pixel = (image->cmapSize > 256) ? 16 : 8;
		hdr.visual_class = PseudoColor;
		hdr.colormap_entries = image->cmapSize;
		hdr.ncolors = image->cmapSize;
		hdr.red_mask = 0;
		hdr.green_mask = 0;
		hdr.blue_mask = 0;
		hdr.bytes_per_line = image->width;
		hdr.bitmap_unit = 8;
		hdr.bitmap_pad = 8;
	} else {
		/*
		**  RGB Image
		*/
		hdr.pixmap_depth = 24;
		hdr.bitmap_unit = 32;
		hdr.bitmap_pad = 32;
		hdr.bits_per_pixel = 32;
		hdr.visual_class = DirectColor;
		hdr.colormap_entries = 256;
		hdr.ncolors = 0;
		hdr.red_mask = 0xff0000;
		hdr.green_mask = 0xff00;
		hdr.blue_mask = 0xff;
		hdr.bytes_per_line = image->width * 4;
	}
	hdr.bits_per_rgb = hdr.pixmap_depth;

	/* Write out the header in big-endian order. */
	writelong(fp, hdr.header_size );
	writelong(fp, hdr.file_version );
	writelong(fp, hdr.pixmap_format );
	writelong(fp, hdr.pixmap_depth );
	writelong(fp, hdr.pixmap_width );
	writelong(fp, hdr.pixmap_height );
	writelong(fp, hdr.xoffset );
	writelong(fp, hdr.byte_order );
	writelong(fp, hdr.bitmap_unit );
	writelong(fp, hdr.bitmap_bit_order );
	writelong(fp, hdr.bitmap_pad );
	writelong(fp, hdr.bits_per_pixel );
	writelong(fp, hdr.bytes_per_line );
	writelong(fp, hdr.visual_class );
	writelong(fp, hdr.red_mask );
	writelong(fp, hdr.green_mask );
	writelong(fp, hdr.blue_mask );
	writelong(fp, hdr.bits_per_rgb );
	writelong(fp, hdr.colormap_entries );
	writelong(fp, hdr.ncolors );
	writelong(fp, hdr.window_width );
	writelong(fp, hdr.window_height );
	writelong(fp, hdr.window_x );
	writelong(fp, hdr.window_y );
	writelong(fp, hdr.window_bdrwidth );

	/* 
	**  Write out the dump name.
	*/
	fwrite(name, 1, strlen(name) + 1, fp);

	/* 
	**   Write out the colormap, big-endian order.
	*/
	color.flags = 7;
	color.pad = 0;
	for ( i = 0; i < image->cmapSize; ++i ) {
		color.pixel = i;

		if (isBW) {
			color.red   = i * 65535L;
			color.green = color.red;
			color.blue  = color.red;
		} else if (isGrey) {
			color.red   = i << 8;
			color.green = color.red;
			color.blue  = color.red;
		} else {
			color.red   = image->cmapData[i * 3 + 0] << 8;
			color.green = image->cmapData[i * 3 + 1] << 8;
			color.blue  = image->cmapData[i * 3 + 2] << 8;
		}
		writelong( fp, color.pixel );
		writeshort( fp, color.red );
		writeshort( fp, color.green );
		writeshort( fp, color.blue );
		(void)putc( color.flags, fp );
		(void)putc( color.pad, fp );
	}

	/* 
	** Finally, write out the data.
	*/
	for (y = 0; y < image->height; y++) {
		if (image->cmapSize == 0) {
			/*
			**  Write RGB Image
			*/
			unsigned char	*dp = &image->data[y * image->width * 3];
			for (x = 0; x < image->width; x++) {
				putc(0, fp);
				putc(*dp, fp); dp++;
				putc(*dp, fp); dp++;
				putc(*dp, fp); dp++;
			}
		} else if (isBW) {
			/*
			**  Write a B&W Image, which is 1 bit per pixel.
			*/
			unsigned char	*dp = &image->data[y * image->width];
			int		v = 0, idx = 7;
			for (x = 0; x < image->width; x++, dp++) {
				v |= *dp << idx;
				if (--idx < 0) {
					idx = 7;
					putc(v & 0xff, fp);
					v = 0;
				}
			}
			if (idx != 7) 
				putc(v & 0xff, fp);
		} else if (image->cmapSize > 256) {
			/*
			**  Write wide colormapped image
			*/
			unsigned short	*dp = &((unsigned short *)image->data)[y * image->width];
			for (x = 0; x < image->width; x++, dp++)
				writeshort(fp, *dp);
		} else {
			/*
			**  Write normal colormapped image, which is also a Grey image
			*/
			unsigned char	*dp = &image->data[y * image->width];
			for (x = 0; x < image->width; x++, dp++)
				putc(*dp, fp);
		}
	}

	fclose(fp);

	return False;
}

