/*
**	MandelSquare - AmigaDOS 2.0/3.0 Mandelbrot set explorer
**
**	SaveAnim.c, Routines to save IFF-ANIM files
**
**	Copyright © 1991-1992 by Olaf `Olsen' Barthel
**		All Rights Reserved
*/

	/* Animation information. */

struct AnimInfo
{
	struct IFFHandle	*Handle;
	struct ViewPort		*VPort;
	ULONG			 ViewModes;
	struct BitMap		*BitMaps[4],
				*Source;

	STRPTR			 FileName;

	UWORD			 Width,
				 Height,
				 Depth;
};

	/* In skip.c */

extern UBYTE * __regargs	skip_comp_plane(UBYTE *in, UBYTE *last_in, UBYTE *out, WORD next_line, WORD rows);
extern WORD __regargs		skip_count_plane(UBYTE *in, UBYTE *last_in, WORD next_line, WORD rows);

	/* In Packer.c */

extern LONG __regargs		PackRow(BYTE **pSource, BYTE **pDest, LONG rowSize);

	/* In ILBM.c */

extern VOID			FreeCustomBitMap(struct BitMap *BitMap);
extern struct BitMap *		AllocCustomBitMap(UWORD Depth,UWORD Width,UWORD Height);
extern BYTE			PutBitMap(struct IFFHandle *Handle,struct BitMap *BitMap);
extern BYTE			PutViewModes(struct IFFHandle *Handle,struct ViewPort *VPort);
extern BYTE			PutColourMap(struct IFFHandle *Handle,struct ViewPort *VPort);
extern BYTE			PutBitMapHeader(struct IFFHandle *Handle,struct Screen *Screen);

	/* Local, static routines. */

STATIC VOID __regargs		FreeCustomBitMaps(struct AnimInfo *Info);
STATIC BYTE __regargs		AllocCustomBitMaps(struct AnimInfo *Info);
STATIC BYTE __regargs		PutDelta(struct IFFHandle *Handle,LONG *Data,LONG nData);

	/* Exported routines. */

VOID				CloseAnim(struct AnimInfo *Info,BYTE Success);
struct AnimInfo *		OpenAnim(STRPTR Name,struct Screen *Screen);
BYTE				AddAnim(struct AnimInfo *Info);

	/* FreeCustomBitMaps(struct AnimInfo *Info):
	 *
	 *	Free the four auxilary bitmaps.
	 */

STATIC VOID __regargs
FreeCustomBitMaps(struct AnimInfo *Info)
{
	WORD i;

	for(i = 0 ; i < 4 ; i++)
	{
		if(Info -> BitMaps[i])
			FreeCustomBitMap(Info -> BitMaps[i]);
	}
}

	/* AllocCustomBitMaps(struct AnimInfo *Info):
	 *
	 *	Allocate four auxilary bitmaps.
	 */

STATIC BYTE __regargs
AllocCustomBitMaps(struct AnimInfo *Info)
{
	WORD i;

	for(i = 0 ; i < 4 ; i++)
	{
		if(!(Info -> BitMaps[i] = AllocCustomBitMap(Info -> Depth,Info -> Width,Info -> Height)))
		{
			FreeCustomBitMaps(Info);

			return(FALSE);
		}
	}

	return(TRUE);
}

	/* PutDelta(struct IFFHandle *Handle,LONG *Data,LONG nData):
	 *
	 *	Save the delta data to the file.
	 */

STATIC BYTE __regargs
PutDelta(struct IFFHandle *Handle,LONG *Data,LONG nData)
{
	AnimationHeader Header;

		/* Reset the animation header contents. */

	memset(&Header,0,sizeof(AnimationHeader));

		/* Fill in compression information and relative
		 * timing information (1 jiffie = 1/60 second).
		 */

	Header . operation	= 5;
	Header . reltime	= 1;

	if(!PushChunk(Handle,ID_ILBM,ID_FORM,IFFSIZE_UNKNOWN))
	{
		if(!PushChunk(Handle,0,ID_ANHD,sizeof(AnimationHeader)))
		{
			if(WriteChunkRecords(Handle,&Header,sizeof(AnimationHeader),1) == 1)
			{
				if(!PopChunk(Handle))
				{
					if(!PushChunk(Handle,0,ID_DLTA,nData))
					{
						if(WriteChunkRecords(Handle,Data,nData,1) == 1)
						{
							if(!PopChunk(Handle))
							{
								if(!PopChunk(Handle))
									return(TRUE);
							}
						}
					}
				}
			}
		}
	}

	return(FALSE);
}

	/* CloseAnim(struct AnimInfo *Info,BYTE Success):
	 *
	 *	Close an animation file, freeing the auxilary data.
	 */

VOID
CloseAnim(struct AnimInfo *Info,BYTE Success)
{
	if(Info -> Handle)
	{
		if(PopChunk(Info -> Handle))
			Success = FALSE;

		CloseIFF(Info -> Handle);

		if(Info -> Handle -> iff_Stream)
		{
			if(!Close(Info -> Handle -> iff_Stream))
				Success = FALSE;
		}
		else
			Success = TRUE;

		FreeIFF(Info -> Handle);
	}

	FreeCustomBitMaps(Info);

	if(!Success)
		DeleteFile(Info -> FileName);
	else
		SetProtection(Info -> FileName,FIBF_EXECUTE);

	FreeVec(Info);
}

	/* OpenAnim(STRPTR Name,struct Screen *Screen):
	 *
	 *	Create an animation file.
	 */

struct AnimInfo *
OpenAnim(STRPTR Name,struct Screen *Screen)
{
	struct AnimInfo	*Info;
	WORD		 Len = strlen(Name),
			 i;

		/* Allocate the auxilary buffer. */

	if(Info = (struct AnimInfo *)AllocVec(sizeof(struct AnimInfo) + Len + 1,MEMF_ANY | MEMF_CLEAR))
	{
			/* Fill in the header information. */

		Info -> FileName	= (STRPTR)(Info + 1);
		Info -> VPort		= &Screen -> ViewPort;
		Info -> Source		= Screen -> RastPort . BitMap;
		Info -> ViewModes	= GetVPModeID(Info -> VPort);

		strcpy(Info -> FileName,Name);

		Info -> Width		= Screen -> Width;
		Info -> Height		= Screen -> Height;
		Info -> Depth		= Info -> Source -> Depth;

			/* Allocate the bitmaps. */

		if(AllocCustomBitMaps(Info))
		{
				/* Copy the image information to all three bitmaps. */

			for(i = 0 ; i < 3 ; i++)
				BltBitMap(Info -> Source,0,0,Info -> BitMaps[i],0,0,Info -> Width,Info -> Height,0xC0,0xFF,NULL);

				/* Set up the vanilla iff stuff... */

			if(Info -> Handle = AllocIFF())
			{
				if(Info -> Handle -> iff_Stream = Open(Name,MODE_NEWFILE))
				{
					InitIFFasDOS(Info -> Handle);

					if(!OpenIFF(Info -> Handle,IFFF_WRITE))
					{
							/* Set up the standard ANIM information. */

						if(!PushChunk(Info -> Handle,ID_ANIM,ID_FORM,IFFSIZE_UNKNOWN))
						{
							if(!PushChunk(Info -> Handle,ID_ILBM,ID_FORM,IFFSIZE_UNKNOWN))
							{
								if(PutBitMapHeader(Info -> Handle,Screen))
								{
									if(PutColourMap(Info -> Handle,Info -> VPort))
									{
										if(PutViewModes(Info -> Handle,Info -> VPort))
										{
											if(PutBitMap(Info -> Handle,Info -> BitMaps[0]))
											{
												if(!PopChunk(Info -> Handle))
													return(Info);
											}
										}
									}
								}
							}
						}
					}
				}
			}
		}

		CloseAnim(Info,FALSE);
	}

	return(NULL);
}

	/* AddAnim(struct AnimInfo *Info):
	 *
	 *	Add another animation frame to the file.
	 */

BYTE
AddAnim(struct AnimInfo *Info)
{
	struct BitMap	*Save;
	LONG		*DeltaData,
			 DeltaCount,
			 PlaneCount[8],
			 PageSize,
			 RowBytes,
			 Rows,
			 j,i;
	BYTE		 Success = FALSE;

	UBYTE		*DeltaPtr,
			*Planes[2];

		/* Move the bitmaps up. */

	Save = Info -> BitMaps[3];

	for(i = 3 ; i > 0 ; i--)
		Info -> BitMaps[i] = Info -> BitMaps[i - 1];

	Info -> BitMaps[0] = Save;

		/* Copy the new screen bitmap to the first one in the list. */

	BltBitMap(Info -> Source,0,0,Save,0,0,Info -> Width,Info -> Height,0xC0,~0,NULL);

		/* Set up the startup information. */

	RowBytes	= Info -> BitMaps[1] -> BytesPerRow;
	Rows		= Info -> BitMaps[1] -> Rows;
	PageSize	= RowBytes * Rows;
	DeltaCount	= 64;

		/* Determine the buffer size required for compression. */

	for(i = 0 ; i < Info -> BitMaps[1] -> Depth ; i++)
	{
		Planes[0]	= Info -> BitMaps[1] -> Planes[i];
		Planes[1]	= Info -> BitMaps[3] -> Planes[i];
		PlaneCount[i]	= 0;
		j		= PageSize;

		while(j--)
		{
			if(*Planes[0]++ != *Planes[1]++)
			{
				PlaneCount[i]	 = skip_count_plane(Info -> BitMaps[1] -> Planes[i],Info -> BitMaps[3] -> Planes[i],RowBytes,Rows);
				DeltaCount	+= PlaneCount[i];

				break;
			}
		}
	}

		/* Allocate the compression buffer. */

	if(DeltaData = (LONG *)AllocVec(DeltaCount,MEMF_ANY | MEMF_CLEAR))
	{
		DeltaPtr	= (UBYTE *)&DeltaData[16];
		DeltaCount	= 64;

			/* Compress the planes. */

		for(i = 0 ; i < Info -> BitMaps[1] -> Depth ; i++)
		{
			if(PlaneCount[i])
			{
				DeltaData[i]	 = DeltaCount;
				DeltaCount	+= PlaneCount[i];
				DeltaPtr	 = skip_comp_plane(Info -> BitMaps[1] -> Planes[i],Info -> BitMaps[3] -> Planes[i],DeltaPtr,RowBytes,Rows);
			}
		}

			/* Save the delta data to disk. */

		Success = PutDelta(Info -> Handle,DeltaData,DeltaCount);

		FreeVec(DeltaData);
	}

	return(Success);
}
