/*
**
**	$Id: GrabScreen.c,v 21.2 92/05/18 02:27:41 chris Exp $
**	$Revision: 21.2 $
**
**	$Filename: Examples/GrabScreen.c $
**	$Author: chris $
**	$Release: 21.1 $
**	$Date: 92/05/18 02:27:41 $
**
**	IFF library example program. Compile with Lattice C 5.x.
**	This program saves the contents of the frontmost screen as an IFF file
**	with filename 'RAM:grabscreen.pic'.
**	This example makes use of the IFFL_PushChunk() / IFFL_WriteChunkBytes() /
**	IFFL_PopChunk() functions, it does not use IFFL_SaveBitMap().
**
**	COPYRIGHT (C) 1987-1992 BY CHRISTIAN A. WEBER, BRUGGERWEG 2,
**	CH-8037 ZUERICH, SWITZERLAND.
**	THIS FILE MAY BE FREELY DISTRIBUTED. USE AT YOUR OWN RISK.
**
*/

#include <proto/exec.h>
#include <exec/memory.h>
#include <proto/graphics.h>
#include <graphics/gfxbase.h>
#include <proto/intuition.h>
#include <intuition/intuitionbase.h>
#include <dos/dos.h>

#include <stdio.h>

#include <libraries/iff.h>		/* IFF library include file */

/*
**	Flags that should be masked out of old 16-bit CAMG before save or use.
**	Note that 32-bit mode id (non-zero high word) bits should not be twiddled
*/
#define OLDCAMGMASK  (~(SPRITES|VP_HIDE|GENLOCK_AUDIO|GENLOCK_VIDEO))

#define MAXSAVEDEPTH		24
#define BODYBUFSIZE			10000
#define MAXPACKEDSIZE(s)	((s)*2)

/*
**	Masking techniques
*/
#define	mskNone					0
#define	mskHasMask				1
#define	mskHasTransparentColor	2
#define	mskLasso				3

/*
**	Compression techniques
*/
#define	cmpNone					0
#define	cmpByteRun1				1


/****************************************************************************
**	Globals
*/

struct Library	*IFFBase;
char			version[] = "\0$VER: GrabScreen 21.2 by Christian A. Weber";


/****************************************************************************
**	This is an example implementation of an IFF writer. It writes the
**	contents of a screen to an IFF ILBM file.
**	Basically, this routine does the same as IFFL_SaveBitMap(), but it is
**	here anyway to show you how to write an IFF file using iff.library's
**	generic write functions.
*/

BOOL MySaveScreen(struct Screen *screen,char *filename)
{
	IFFL_HANDLE		iff;
	struct BitMap	*bitmap = &screen->BitMap;
	BOOL			result = FALSE;

	/*
	**	Open the file for writing
	*/
	if(iff = IFFL_OpenIFF(filename,IFFL_MODE_WRITE))
	{
		struct IFFL_BMHD	bmhd;
		ULONG				modeid;
		UBYTE				*colortable;
		int					count;

		/*
		**	Prepare the BMHD structure
		*/
		bmhd.w					= screen->Width;	
		bmhd.h					= screen->Height;
		bmhd.x					= screen->LeftEdge;		/* Not very useful :-) */
		bmhd.y					= screen->TopEdge;
		bmhd.nPlanes			= bitmap->Depth;
		bmhd.masking			= mskNone;
		bmhd.compression		= cmpByteRun1;
		bmhd.pad1				= 0;
		bmhd.transparentColor	= 0;
		bmhd.xAspect			= screen->Width;
		bmhd.yAspect			= screen->Height;
		bmhd.pageWidth			= screen->Width;
		bmhd.pageHeight			= screen->Height;

		/*
		**	Create the BMHD chunk. (goto considered useful :-))
		*/
		if(IFFL_PushChunk(iff,ID_ILBM,ID_BMHD))
		{
			if(!IFFL_WriteChunkBytes(iff,&bmhd,sizeof(bmhd)))
			{
				printf("Error writing BMHD data.\n");
				goto error;
			}

			if(!IFFL_PopChunk(iff))
			{
				printf("Error closing BMHD chunk.\n");
				goto error;
			}
		}
		else
		{
			printf("Error creating BMHD chunk.\n");
			goto error;
		}

		/*
		**	Create the CMAP chunk.
		*/
	    count = screen->ViewPort.ColorMap->Count;
	    if(colortable = AllocMem(count * 3, MEMF_ANY))
		{
			int i;

			for(i=0; i<count; ++i)
			{
				UWORD rgb4 =  GetRGB4(screen->ViewPort.ColorMap,i);

				colortable[i*3+0]  = (rgb4 >> 4) & 0xF0;
				colortable[i*3+0] |= colortable[i*3+0] >> 4;

				colortable[i*3+1]  = (rgb4     ) & 0xF0;
				colortable[i*3+1] |= colortable[i*3+1] >> 4;

				colortable[i*3+2]  = (rgb4 << 4) & 0xF0;
				colortable[i*3+2] |= colortable[i*3+2] >> 4;
			}

			if(IFFL_PushChunk(iff,ID_ILBM,ID_CMAP))
			{
				if(!IFFL_WriteChunkBytes(iff,colortable,count * 3))
				{
					printf("Error writing CMAP data.\n");
					goto error;
				}

				if(!IFFL_PopChunk(iff))
				{
					printf("Error closing CMAP chunk.\n");
					goto error;
				}
			}
			else
			{
				printf("Error creating CMAP chunk.\n");
				goto error;
			}

			FreeMem(colortable,count * 3);
		}

		/*
		**	Get the viewport's modes for the CAMG chunk.
		*/
		modeid = (GfxBase->LibNode.lib_Version >= 36) ?
			GetVPModeID(&screen->ViewPort) : (screen->ViewPort.Modes&OLDCAMGMASK);

		if(IFFL_PushChunk(iff,ID_ILBM,ID_CAMG))
		{
			if(!IFFL_WriteChunkBytes(iff,&modeid,sizeof(modeid)))
			{
				printf("Error writing CAMG data.\n");
				goto error;
			}

			if(!IFFL_PopChunk(iff))
			{
				printf("Error closing CAMG chunk.\n");
				goto error;
			}
		}
		else
		{
			printf("Error creating CAMG chunk.\n");
			goto error;
		}

		/*
		**	Generate BODY
		*/
		if(IFFL_PushChunk(iff,ID_ILBM,ID_BODY))
		{
			UBYTE	*bodybuf;

			if(bodybuf = AllocMem(BODYBUFSIZE,MEMF_ANY))
			{
				UBYTE	*planes[MAXSAVEDEPTH];
				int		row,rowbytes,iplane,bodysize=0;

				rowbytes = bitmap->BytesPerRow;

				/*
				**	Copy the plane pointers into the local array "planes"
				*/
				for(iplane=0; iplane < bmhd.nPlanes; ++iplane)
				{
					planes[iplane] = bitmap->Planes[iplane];
				}

				for(row=0; row < bmhd.h; ++row)
				{
					for(iplane=0; iplane < bmhd.nPlanes; ++iplane)
					{
						int comprsize;

						/*
						**	To speed up things, we collect as much data
						**	as possible in bodybuf before we write it out.
						*/
						if(bodysize > (BODYBUFSIZE-MAXPACKEDSIZE(rowbytes)))
						{
							if(!IFFL_WriteChunkBytes(iff,bodybuf,bodysize))
							{
								printf("Error writing BODY data.\n");
								goto error;
							}
							bodysize = 0;
						}

						/*
						**	Compress the next row
						*/
						if(!(comprsize = IFFL_CompressBlock(planes[iplane],
								bodybuf+bodysize,rowbytes,IFFL_COMPR_BYTERUN1)))
						{
							printf("Error compressing BODY data.\n");
							goto error;
						}
						bodysize		+= comprsize;
						planes[iplane]	+= rowbytes;
					}
				}

				/*
				**	Now we're done, so flush the body data buffer
				*/
				if(bodysize)
				{
					if(!IFFL_WriteChunkBytes(iff,bodybuf,bodysize))
					{
						printf("Error writing BODY data.\n");
						goto error;
					}
				}

				FreeMem(bodybuf,BODYBUFSIZE);
			}
			else
			{
				printf("No memory for BODY buffer.\n");
				goto error;
			}

			if(!IFFL_PopChunk(iff))
			{
				printf("Error closing BODY chunk.\n");
				goto error;
			}

			/*
			**	If we get here, everything was fine.
			*/
			result = TRUE;
		}
		else
		{
			printf("Error creating BODY chunk.\n");
			goto error;
		}
error:
		IFFL_CloseIFF(iff);
	}

	return result;
}

/****************************************************************************
**	Main program
*/

int main(int argc, char **argv)
{
	int result = RETURN_FAIL;

	if(GfxBase = (struct GfxBase *)OpenLibrary("graphics.library",0))
	{
		if(IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library",0))
		{
			/*
			**	For the new functions we need at least version 21 of iff.library.
			*/
			if(IFFBase = OpenLibrary(IFFNAME,21))
			{
				/*
				**	Note that we don't lock the screen, so it might go away while
				**	we're writing its contents to the file, and the picture may
				**	then contain garbage.
				*/
				MySaveScreen(IntuitionBase->FirstScreen,"RAM:grabscreen.pic");
				result = RETURN_OK;

				CloseLibrary(IFFBase);
			}
			else printf("Can't open iff.library V21+\n");

			CloseLibrary(IntuitionBase);
		}
		else printf("Can't open intuition.library!\n");

		CloseLibrary(GfxBase);
	}
	else printf("Can't open graphics.library!\n");

	return result;
}
