/*
IMG decompression

In a clinic special last year (in ST World again), Dimitri Koveos described and
explained the IMG file format. However, he did not include any code for reading
and writing IMG files, which is what the redoubtable Lloyd Patton has provided
for the clinic this month. The IMG reading routine is written in C.
    "The code is contained in the file IMGREAD. The function decompress() does
the actual work and is fairly self-explanatory. All the complications are in
the interface function read_image_file(). Which is simply concerned with error
checking and initialising the data structures. Images that are less than a
screen width or height size are placed in a memory form block that is at least
a 640 pixels wide and 400 pixels high as this will allow easier manipulation of
the picture area at a later date

------------------------------------------------------------------------------
*/

/* IMG file decompression */
/* Author : Lloyd Patton */

#include <local.h>
#include <gemdefs.h>
#include <obdefs.h>

typedef struct {
  WORD version, header_length, number_of_planes
  WORD pattern_length, micron_width, micron_height,
  WORD line_length, number_of_lines;
  WORD flag, palette[16];
  } XIMGHDR;  /* this typedef should be in a header file */

#define NoFile  2
#define NoLoadMemory  4

static BYTE
  MONOonly[] = "[3][Only monochrome IMG|files supported!][Sorry ]",
  BadFile[] = "[3][IMG file format error|encountered!][ Sorry ]";

/* decompress an image file - returns 0 if error occurs */
static decompress_image_file(fp, lines, pixels, llength,picture)
  register FILE *fp;  /* inout file pointer /*
  UWORD lines, pixels;  /* no. scan lines && pixels */
  LONG llength;
  BYTE *picture;
{	WORD repcnt, this;
	register WORD first, second, dtype, count;
	register BYTE *buffer, *pptr, *bptr;

	if ((buffer = lcalloc(llength, 1L)) == NULL)
	{	form_error(NoLoadMemory);
		return (0);
	}
	wind_update(BEG_MCTRL);
	graf_mouse(HOURGLASS, NULL);
	for (this = dtype = 0; this < lines && dtype != EOF; this += repcnt)
	{	repcnt = 1;
		bptr = buffer;
		while (((bptr - buffer) << 3) < pixels
			&& (dtype = getc(fp)) != EOF)
		{	switch (dtype)
			{
			case 0x80:  /* BIT STRING */
				count = getc(fp); /* copy count no. of bytes */
				while (count--)
					*bptr++ = getc(fp);
				break;
			case 0: /* either VERT REPL count || PATTERN RUN */
				count = getc(fp);
				first = getc(fp);
				second = getc(fp);
				if (count == 0) /* VERTICAL REPLICATION */
				{	if (first == 255 && bptr == buffer)
						repcnt = second;
					else
						lines = this; /* read error */
					continue;
				}
				else /* PATTERN RUN */
				{	while (count--)
					{	*bptr++ =  first;
						*bptr++ = second;
					}
				}
				break;
			default:  /* SOLID RUN */
				first = (dtype & 0x80) ? -1: 0;
				count = dtype & 0x7F;
				while (count--)
					*bptr++ = first;
				break;
			}
		}
		for (first = 0; first < repcnt; ++first)
		{ /* copy scan line to picture buffer */
			pptr = picture + (this + first) * llength;
			bcopy(buffer, pptr, (WORD)llength);
		}
	}
	graf_mouse(ARROW, NULL);
	wind_update(END_MCTRL);
	return (lines == this && dtype != EOF);
} /* ends decompress_image_file(fp, lines, pixels, llength,picture) */


/*
 * read an image file - returns pointer to picture data or
 * NULL if error.
 * Your program should not be within a BEG_MCTRL loop
 * when this function is called.
 */

BYTE *read_image_file(vdi, img, mform, path)
  WORD vdi;       /* vdi handle */
  register MFDB *mform; /* memory form descriptor for pic*/
  register XIMGHDR *img;  /* IMG file header */
  BYTE *path;       /* full pathname for file to read */
{	register FILE *fp;
	register LONG psize;
	register UWORD lines, pwidth, wwidth;
	register WORD pxy[8];
	MFDB tform;

	if ((fp = fopen(path, "br")) == NULL)
	{	form_error(NoFile);
		return (NULL);
	}
	fread(img, sizeof(XIMGHDR), 1, fp);
	fseek(fp, (LONG)(img -> header_length * 2), 0);
	if (img -> number_of_planes != 1)
	{	form_alert(1, MONOonly);
		return (NULL);
	}
	mform -> fd_nplanes = 1;
	mform -> fd_stand = 0;
	psize = (pwidth = mform -> fd_w = img -> line_length) / 16;
	mform -> fd_wdwidth = wwidth = psize += (img ->line_length % 16) ? 1: 0;
	psize *= lines = mform -> fd_h = img -> number_of_lines;
	tform.fd_addr = mform -> fd_addr;
	if (mform -> fd_addr)
		mform -> fd_addr = (LONG)lrealloc(mform -> fd_addr, psize * 2L);
	else
		mform -> fd_addr = (LONG)lcalloc(mform -> fd_addr, psize * 2L, 1L);
	if (mform -> fd_addr == NULL)
	{	fclose(fp);
		form_error(NoLoadMemory);
		return ((BYTE *)(mform -> fd_addr = tform.fd_addr));
	}
	if (decompress_image_file(fp, lines, pwidth, (ULONG)wwidth*2L,
		mform -> fd_addr))
	{	fclose(fp);
		if (pwidth < 640 || lines < 400)
		{	/* ensure a minimum screen size form */
			tform = *mform;
			tform.fd_w = img -> line_length =
				max(img -> line_length, 640);
			tform.fd_h = img -> number_of_lines =
				max(img -> number_of_lines, 400);
			tform.fd_wdwidth = max(mform -> fd_wdwidth, 40);
if (tform.fd_addr = (LONG)lcalloc(tform.fd_wdwidth*2L,(LONG)tform.fd_h))
			{	pxy[0] = pxy[1] = pxy[4] = pxy[5] = 0;
				pxy[2] = pxy[6] = mform -> fd_w - 1;
				pxy[3] = pxy[7] = mform -> fd_h - 1;
				vro_cpyfm(vdi, S_ONLY, pxy, mform, &tform);
				free(mform -> fd_addr);
				*mform = tform;
			}
			else
			{	form_error(NoLoadMemory);
				free(mform);
				return (NULL);
			}
		}
		return ((BYTE *)mform -> fd_addr);
	}
    	else
	{	fclose(fp);
		form_alert(1, BadFile);
		free(mform -> fd_addr);
	}
	return NULL;
} /* ends read_image_file(vdi, img, mform, path) */

/* End of file */
