/*
**	MandelSquare - AmigaDOS 2.0/3.0 Mandelbrot set explorer
**
**	MandelSquare.c, Main program module
**
**	Copyright © 1991-1992 by Olaf `Olsen' Barthel
**		All Rights Reserved
*/

#include "MandelSquare.h"

UBYTE VersTag[]	= VERSTAG;

	/* Menu IDs */

enum	{	MEN_OPEN=17,MEN_SAVE,MEN_SAVEAS,MEN_PRI0,MEN_PRI1,MEN_PRI2,MEN_PLAY,MEN_START,MEN_STOP,MEN_SCROLL,MEN_FRAME,MEN_INCLUDE,MEN_RESOLUTION,MEN_ABOUT,MEN_QUIT,
		MEN_ZOOM,MEN_CLIP,MEN_RERUN,MEN_PRECISE,MEN_COORDS,MEN_PALETTE,MEN_RUN };

	/* Dimensions of the coords control panel. */

#define COORDS_WIDTH		256
#define COORDS_HEIGHT		120

	/* Dimensions of the palette control panel. */

#define PALETTE_WIDTH		245
#define PALETTE_HEIGHT		53

	/* Dimensions of the scroll amount control panel. */

#define SCROLL_WIDTH		175
#define SCROLL_HEIGHT		45

	/* Two handy macros to set the window mouse pointer. */

#define SetWait(Window)		SetPointer(Window,Stopwatch,16,16,-6,0)
#define SetClear(Window)	SetPointer(Window,ClearData, 1,16, 0,0)

	/* Some handy signal names. */

#define SIG_KILL		SIGBREAKF_CTRL_C
#define SIG_HANDSHAKE		SIGBREAKF_CTRL_F
#define SIG_TIMER		(1 << TimePort -> mp_SigBit)
#define SIG_WINDOW		(1 << Window -> UserPort -> mp_SigBit)

	/* A second measured in microseconds. */

#define SECOND			1000000

	/* A picture info structure as returned by LoadPicture(). */

struct PictureInfo
{
	BitMapHeader		BitMapHeader;
	MandelHeader		MandelHeader;
	struct Palette *	Palette;
	ULONG			ViewModes;
	struct BitMap *		BitMap;
};

	/* In Plot.asm and Iterate.asm */

extern VOID __asm		Plot(register __a0 struct BitMap *,register __d0 WORD X,register __d1 WORD Y,register __d2 WORD Colour);
extern BYTE __asm		Test(register __a0 struct BitMap *,register __d0 WORD X,register __d1 WORD Y);
extern UBYTE __asm		Iterate(register __fp0 double const RealValue,register __fp1 double const ImaginaryValue);

	/* In Mandelbrot.c */

VOID __regargs			CycleColours(BYTE Forward);
LONG __regargs			GetModeSize(ULONG ID);
BYTE __regargs			SelectDisplayMode(struct Window *Window,ULONG *Mode);
VOID __regargs			DrawSquare(struct RastPort *RPort,WORD X,WORD Y,WORD Width);
VOID __regargs			DrawLine(struct RastPort *RPort,UWORD FromX,UWORD FromY,UWORD ToX,UWORD ToY);
VOID				IterationSetup(VOID);
VOID __saveds			CycleServer(VOID);
VOID				CloseWindowSafely(struct Window *Window);
VOID				GfxCleanup(VOID);
LONG				GfxSetup(VOID);
VOID __regargs			CloseAll(LONG ReturnCode);
VOID				OpenAll(VOID);
VOID __regargs			CopyLine(struct BitMap *BitMap,WORD Line);
VOID				GetTime(VOID);
VOID				BlockWindow(VOID);
VOID				ReleaseWindow(VOID);
BYTE				GetFile(UBYTE *Title,UBYTE *Directory,UBYTE *Name,UBYTE *Buffer,UBYTE *Pattern,BYTE SaveFlag);
WORD __stdargs			MyEasyRequest(UBYTE *Gadgets,UBYTE *Text,...);
ULONG __regargs			Random(LONG MaxValue);
struct Gadget *			CreateAllCoordsGadgets(struct Gadget **GadgetArray,struct Gadget **GadgetList,APTR VisualInfo,UWORD TopEdge);
struct Gadget *			CreateAllPaletteGadgets(struct Gadget **GadgetArray,struct Gadget **GadgetList,APTR VisualInfo,UWORD TopEdge);
struct Gadget *			CreateAllScrollGadgets(struct Gadget **GadgetArray,struct Gadget **GadgetList,APTR VisualInfo,UWORD TopEdge);
VOID				InfoText(VOID);
WORD				CalculateFrames(double MinReal,double MinImaginary,double Size);
VOID				RunAnim(VOID);
VOID				StopAnim(VOID);
VOID				Coords(VOID);
VOID				AdjustPalette(VOID);
VOID				MaxScrollPanel(VOID);
VOID				AreaZoom(VOID);
VOID				Zoom(VOID);
VOID				HandleEvent(VOID);
/*UBYTE __inline		Iterate(double RealValue,double ImaginaryValue);*/
VOID				Mandelbrot(double MinReal,double MinImaginary,double RealStep,double ImaginaryStep,LONG Left,LONG Top,LONG Width,LONG Height);
VOID				RunMandelbrot(double MinReal,double MaxReal,double MinImaginary,double MaxImaginary,LONG Width,LONG Height);
VOID				RunAreaMandelbrot(double RealStep,double ImaginaryStep);
BYTE				CheckAbort(VOID);
BYTE				ReplayAnim(VOID);
VOID __stdargs			main(VOID);

	/* In PlayAnim.c */

VOID				DeleteAnim(struct MinList *List);
struct MinList *		LoadAnim(STRPTR Name);
LONG				ValidateAnim(struct MinList *List,struct Screen *Screen);
BYTE				PlayAnim(struct MinList *List,struct Screen *Screen,BYTE (*CheckAbort)(VOID));

	/* In SaveAnim.c */

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

	/* In ILBM.c */

VOID				FreeCustomBitMap(struct BitMap *BitMap);
struct BitMap *			AllocCustomBitMap(UWORD Depth,UWORD Width,UWORD Height);

VOID				FreePicture(struct PictureInfo *Info);
struct PictureInfo *		LoadPicture(STRPTR Name);
BYTE				SavePicture(STRPTR Name,struct Screen *Screen,MandelHeader *MandelHeader);

	/* In Palette.c */

VOID				FreePalette(struct Palette *Palette);
struct Palette *		AllocPalette(LONG NumColours,BYTE TrueColour);
struct Palette *		GetPalette(struct Screen *Screen,struct Palette *Palette);
VOID				LoadPalette(struct Screen *Screen,struct Palette *Palette,LONG NumColours);
LONG				GetPaletteSize(struct Palette *Palette);
BYTE				GetPaletteTriplet(struct Palette *Palette,UBYTE *Triplet,LONG Index);
BYTE				SetPaletteTriplet(struct Palette *Palette,UBYTE R,UBYTE G,UBYTE B,LONG Index);
ULONG				GetPaletteEntry(struct Palette *Palette,LONG Index);
BYTE				SetPaletteEntry(struct Palette *Palette,ULONG Entry,LONG Index);

	/* Shared and global library data. */

struct IntuitionBase		*IntuitionBase;
struct GfxBase			*GfxBase;
struct Library			*GadToolsBase;
struct Library			*IFFParseBase;
struct Library			*AslBase;
struct Library			*TimerBase;
struct Library			*IconBase;

	/* Timer data. */

struct timerequest		*TimeRequest;
struct MsgPort			*TimePort;

	/* Window & Screen data. */

struct Screen			*Screen;
struct Window			*Window;
struct RastPort			*RPort;
struct ViewPort			*VPort;
APTR				 VisualInfo;
struct BitMap			*BackupBitMap;
struct Menu			*Menu;

	/* Current process and Window Pointer. */

struct Process			*ThisProcess;
APTR				 OldPtr;
BYTE				 OldPri;

	/* Colour cycling data. */

struct Task			*CycleTask;
BYTE				 DoCycle	= FALSE,
				 Add		= 0,
				 Forward;

	/* Some strings. */

UBYTE				 LastName[256],
				 NameBuffer[256],
				 DummyBuffer[256],
				 AnimSaveName[256],
				 AnimPlayName[256];

	/* Calculation data. */

LONG				 MaxIteration		= 32 - 1;

double				 MaxScroll		= 4,

				 RealStep,
				 ImaginaryStep,

				 RealWidth		= 4.5,
				 ImaginaryHeight	= 4.5,

				 MinReal,
				 MinImaginary,

				 MainMinReal,
				 MainMinImaginary,
				 MainRealWidth,
				 MainImaginaryHeight,

				 LastSize,
				 LastMinReal,
				 LastMinImaginary;

	/* Size and position of the screen. */

UWORD				 AreaLeft,
				 AreaTop,
				 AreaWidth,
				 AreaHeight;

	/* Various flags. */

BYTE				 Running	= FALSE,
				 NewCoords	= FALSE,
				 AreaActive	= FALSE,
				 GotClip	= FALSE,
				 NewMode	= FALSE,
				 Frame		= FALSE,
				 Include	= TRUE,
				 Faster		= TRUE,
				 Precise	= FALSE,
				 HoldIt		= FALSE,
				 AnimRunning	= FALSE,
				 FullPicture;

	/* Animation creation interface. */

struct AnimInfo			*AnimInfo;

	/* A range of colour intensities and the colour transition table. */

UBYTE				*Wave,
				*Range;
struct Palette			*Palette;

	/* The levels of the palette colour sliders. */

UBYTE				 RedLevel	= 27,
				 GreenLevel	= 22,
				 BlueLevel	= 17;

BYTE				 LevelPatched	= FALSE;

	/* Rendering data. */

LONG 				 Mode		= LORES_KEY,
				 Depth		= 5,
				 Square		= 256;

	/* The TextFont structure contained in Font.c */

extern struct TextFont		 DigitFont;

	/* A single line bitmap whose planes are to be located
	 * in fast ram.
	 */

struct BitMap			 LineBitMap;

	/* Current drawing pen colour. */

ULONG				 CurrentPen;

	/* Global image data. */

struct BitMap			*GlobalBitMap;
ULONG				 GlobalMode;
LONG				 GlobalDepth,
				 GlobalSize;
struct Palette			*GlobalPalette;

	/* Compatibility data. */

BYTE				 IsNative,
				 UseDouble,
				 Is39;
UWORD				 MaxRange,
				 PaletteSize;

	/* The standard font to be use throughout the
	 * whole program.
	 */

struct TextAttr DefaultFont =
{
	(UBYTE *)"topaz.font",
	8,
	FS_NORMAL,
	FPF_ROMFONT
};

	/* Error codes as returned by ValidateAnim(). */

STRPTR AnimErrors[] =
{
	NULL,

	"Invalid anim sequence",
	"Could not find body",
	"Unsupported anim op.",
	"Could not find frames",
	"Unsupported masking op.",
	"Invalid picture",
	"Unsupported compr. op.",
	"Could not find header",
	"Picture has wrong size",
	"Picture has wrong display mode"
};

	/* Definitions for the pull-down menu used by the program. */

struct NewMenu MandelbrotMenu[] =
{
	{ NM_TITLE, "Project",			 0 , 0,			0,	(APTR)0},
	{  NM_ITEM, "Open picture...",		"O", 0,			0,	(APTR)MEN_OPEN},
	{  NM_ITEM, "Save picture",		 0 , 0,			0,	(APTR)MEN_SAVE},
	{  NM_ITEM, "Save picture as...",	"S", 0,			0,	(APTR)MEN_SAVEAS},
	{  NM_ITEM, NM_BARLABEL,		 0 , 0,			0,	(APTR)0},
	{  NM_ITEM, "Priority",			 0 , 0,			0,	(APTR)0},
	{   NM_SUB, "-5",			 0 , CHECKIT|MENUTOGGLE,~1,	(APTR)MEN_PRI0},
	{   NM_SUB, " 0",			 0 , CHECKIT|CHECKED|MENUTOGGLE,~2,	(APTR)MEN_PRI1},
	{   NM_SUB, "+5",			 0 , CHECKIT|MENUTOGGLE,~4,	(APTR)MEN_PRI2},
	{  NM_ITEM, NM_BARLABEL,		 0 , 0,			0,	(APTR)0},
	{  NM_ITEM, "Replay animation...",	"!", 0,			0,	(APTR)MEN_PLAY},
	{  NM_ITEM, NM_BARLABEL,		 0 , 0,			0,	(APTR)0},
	{  NM_ITEM, "Begin animation...",	"B", 0,			0,	(APTR)MEN_START},
	{  NM_ITEM, "End animation",		"E", 0,			0,	(APTR)MEN_STOP},
	{  NM_ITEM, "Set scroll amount...",	"A", 0,			0,	(APTR)MEN_SCROLL},
	{  NM_ITEM, "Include frame",		 0 , CHECKIT|MENUTOGGLE,0,	(APTR)MEN_FRAME},
	{  NM_ITEM, "Include coordinates",	 0 , CHECKIT|CHECKED|MENUTOGGLE,0,	(APTR)MEN_INCLUDE},
	{  NM_ITEM, NM_BARLABEL,		 0 , 0,			0,	(APTR)0},
	{  NM_ITEM, "Change resolution",	"X", 0,			0,	(APTR)MEN_RESOLUTION},
	{  NM_ITEM, NM_BARLABEL,		 0 , 0,			0,	(APTR)0},
	{  NM_ITEM, "About...",			"?", 0,			0,	(APTR)MEN_ABOUT},
	{  NM_ITEM, NM_BARLABEL,		 0 , 0,			0,	(APTR)0},
	{  NM_ITEM, "Quit...",			"Q", 0,			0,	(APTR)MEN_QUIT},

	{ NM_TITLE, "Calculation",		 0 , 0,			0,	(APTR)0},
	{  NM_ITEM, "Magnify...",		"M", 0,			0,	(APTR)MEN_ZOOM},
	{  NM_ITEM, "Detail...",		"D", 0,			0,	(APTR)MEN_CLIP},
	{  NM_ITEM, "Recalculate...",		"R", 0,			0,	(APTR)MEN_RERUN},
	{  NM_ITEM, "Precise calculation",	 0 , CHECKIT|MENUTOGGLE,0,	(APTR)MEN_PRECISE},
	{  NM_ITEM, NM_BARLABEL,		 0 , 0,			0,	(APTR)0},
	{  NM_ITEM, "Coordinates...",		"C", 0,			0,	(APTR)MEN_COORDS},
	{  NM_ITEM, NM_BARLABEL,		 0 , 0,			0,	(APTR)0},
	{  NM_ITEM, "Palette...",		"P", 0,			0,	(APTR)MEN_PALETTE},
	{  NM_ITEM, NM_BARLABEL,		 0 , 0,			0,	(APTR)0},
	{  NM_ITEM, "Toggle calculation",	".", 0,			0,	(APTR)MEN_RUN},

	{ NM_TITLE, "Depth",			 0 , 0,			0,	(APTR)0},
	{  NM_ITEM, "   32",			"0", CHECKIT|CHECKED,	~  1,	(APTR)5},
	{  NM_ITEM, "   64",			"1", CHECKIT,		~  2,	(APTR)6},
	{  NM_ITEM, "  128",			"2", CHECKIT,		~  4,	(APTR)7},
	{  NM_ITEM, "  256",			"3", CHECKIT,		~  8,	(APTR)8},
	{  NM_ITEM, "  512",			"4", CHECKIT,		~ 16,	(APTR)9},
	{  NM_ITEM, " 1024",			"5", CHECKIT,		~ 32,	(APTR)10},
	{  NM_ITEM, " 2048",			"6", CHECKIT,		~ 64,	(APTR)11},
	{  NM_ITEM, " 4096",			"7", CHECKIT,		~128,	(APTR)12},
	{  NM_ITEM, " 8192",			"8", CHECKIT,		~256,	(APTR)13},
	{  NM_ITEM, "16384",			"9", CHECKIT,		~512,	(APTR)14},
	{   NM_END, 0,				 0 , 0,			0,	(APTR)0}
};

	/* Run-dump of the stopwatch mouse pointer. */

UWORD __chip Stopwatch[(2 + 16) * 2] =
{
	0x0000,0x0000,

	0x0400,0x07C0,
	0x0000,0x07C0,
	0x0100,0x0380,
	0x0000,0x07E0,
	0x07C0,0x1FF8,
	0x1FF0,0x3FEC,
	0x3FF8,0x7FDE,
	0x3FF8,0x7FBE,
	0x7FFC,0xFF7F,
	0x7EFC,0xFFFF,
	0x7FFC,0xFFFF,
	0x3FF8,0x7FFE,
	0x3FF8,0x7FFE,
	0x1FF0,0x3FFC,
	0x07C0,0x1FF8,
	0x0000,0x07E0,

	0x0000,0x0000
};

	/* Definitions for a clear sprite. */

UWORD __chip ClearData[(2 + 1) * 2] =
{
	0x0000,0x0000,

	0x0000,0x0000,

	0x0000,0x0000
};

	/* Run-dump of the picture icon image data. */

USHORT Picture1Data[700] =
{
	0x0000,0x0000,0x0000,0x0000,0x0010,0x0000,0x0000,0x0000,
	0x0000,0x0030,0x0000,0x0000,0x0000,0x0000,0x0030,0x0000,
	0x0000,0x0000,0x0000,0x0030,0x0000,0x0000,0x0000,0x0000,
	0x0030,0x0000,0x0000,0x0000,0x4000,0x0030,0x0000,0x0000,
	0x0000,0x0000,0x0030,0x0000,0x0000,0x0000,0x0400,0x0030,
	0x0000,0x0000,0x0000,0x1C00,0x0030,0x0000,0x0000,0x0000,
	0x0000,0x0030,0x0000,0x0000,0x0000,0x4800,0x0030,0x0000,
	0x0000,0x0000,0xC000,0x0030,0x0000,0x0000,0x0005,0x8000,
	0x0030,0x0000,0x0000,0x0001,0x0300,0x0030,0x0000,0x0000,
	0x0001,0x0200,0x0030,0x0000,0x0000,0x0001,0x0200,0x0030,
	0x0000,0x0000,0x0100,0x0020,0x0030,0x0000,0x0000,0x002F,
	0x8E80,0x8030,0x0000,0x0000,0x0328,0x0080,0x0030,0x0000,
	0x0000,0x0260,0x002E,0xC030,0x0000,0x0000,0x0200,0x0008,
	0x8030,0x0000,0x0000,0x0200,0x0000,0x8030,0x0000,0x0000,
	0x1600,0x0000,0x0030,0x0000,0x0000,0x1400,0x0000,0x8030,
	0x0000,0x2000,0x1000,0x0000,0x0030,0x0000,0x0040,0x3000,
	0x0000,0x9030,0x0000,0x4000,0x2000,0x0000,0x3030,0x0000,
	0x0080,0x6000,0x0000,0x6030,0x0000,0x0FB0,0x4000,0x0000,
	0x0030,0x0000,0x0800,0xC000,0x0000,0x2030,0x0000,0x0800,
	0x0000,0x0000,0x6030,0x0000,0x1800,0xC000,0x0000,0x4030,
	0x0000,0x3000,0x8000,0x0000,0x4030,0x0001,0x2000,0x8000,
	0x0000,0xC030,0x0003,0x6000,0x0000,0x0000,0x8030,0x0F82,
	0x0000,0x0000,0x0001,0x0030,0x0020,0x0000,0x0000,0x0000,
	0x0030,0x0008,0x0000,0x0000,0x0000,0x0030,0x0000,0x2000,
	0x0000,0x0000,0x0030,0x0003,0x8000,0x8000,0x0000,0x4030,
	0x0006,0x0000,0x8000,0x0000,0x4030,0x0000,0x4800,0x0000,
	0x0000,0x0030,0x0000,0x0802,0x4000,0x0000,0x2030,0x0000,
	0x0006,0x4000,0x0000,0x6030,0x0000,0x060C,0x0000,0x0000,
	0x0030,0x0000,0x3C78,0x0000,0x0000,0x0030,0x0000,0x20C0,
	0x0000,0x0000,0x1030,0x0000,0x2000,0x1800,0x0000,0xF030,
	0x0000,0x4000,0x0000,0x0001,0x8030,0x0000,0x0000,0x2000,
	0x0001,0x0030,0x0000,0x0000,0x0200,0x0000,0x0030,0x0000,
	0x0000,0x0200,0x0000,0x0030,0x0000,0x0000,0x0200,0x0000,
	0x4030,0x0000,0x0000,0x0080,0x003E,0xC030,0x0000,0x0000,
	0x0180,0x0120,0x8030,0x0000,0x0000,0x0D19,0x8360,0x8030,
	0x0000,0x0000,0x0101,0x0200,0x0030,0x0000,0x0000,0x0001,
	0x0200,0x0030,0x0000,0x0000,0x0000,0x0000,0x0030,0x0000,
	0x0000,0x0000,0x0300,0x0030,0x0000,0x0000,0x0005,0x0E00,
	0x0030,0x0000,0x0000,0x0001,0x1800,0x0030,0x0000,0x0000,
	0x0000,0x0000,0x0030,0x0000,0x0000,0x0000,0x2000,0x0030,
	0x0000,0x0000,0x0000,0x2C00,0x0030,0x0000,0x0000,0x0000,
	0x2000,0x0030,0x0000,0x0000,0x0000,0x0000,0x0030,0x0000,
	0x0000,0x0000,0x0000,0x0030,0x0000,0x0000,0x0000,0x0000,
	0x0030,0x7FFF,0xFFFF,0xFFFF,0xFFFF,0xFFF0,

	0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFE0,0xD555,0x5555,0x5555,
	0x5555,0x5540,0xD555,0x5555,0x5555,0x5555,0x5540,0xD555,
	0x5555,0x5555,0x5555,0x5540,0xD555,0x5555,0x5555,0x5555,
	0x5540,0xD555,0x5555,0x5555,0x1555,0x5540,0xD555,0x5555,
	0x5555,0x3555,0x5540,0xD555,0x5555,0x5555,0x8155,0x5540,
	0xD555,0x5555,0x5555,0x8155,0x5540,0xD555,0x5555,0x5557,
	0x0555,0x5540,0xD555,0x5555,0x555C,0x0555,0x5540,0xD555,
	0x5555,0x5550,0x0155,0x5540,0xD555,0x5555,0x5558,0x5155,
	0x5540,0xD555,0x5555,0x5558,0x5855,0x5540,0xD555,0x5555,
	0x7558,0x59D5,0x5540,0xD555,0x5555,0x6DF8,0x7197,0x5540,
	0xD555,0x5555,0x6080,0x401E,0x5540,0xD555,0x5555,0x4000,
	0x5010,0x5540,0xD555,0x5555,0x5015,0x5500,0x5540,0xD555,
	0x5555,0x5115,0x5540,0x1540,0xD555,0x5555,0xF155,0x5554,
	0x5540,0xD555,0x5555,0x8155,0x5551,0x5540,0xD555,0xD555,
	0x8155,0x5554,0x5540,0xD555,0x1555,0x8155,0x5556,0x5540,
	0xD555,0x9755,0x8555,0x5554,0x1540,0xD555,0x1E35,0x0555,
	0x5555,0x0540,0xD555,0x0005,0x1555,0x5555,0x0540,0xD555,
	0x4005,0x1555,0x5555,0x1540,0xD555,0xC040,0x1555,0x5555,
	0x1540,0xD555,0x0550,0x1555,0x5555,0x1540,0xD555,0x8554,
	0x5555,0x5555,0x1540,0xD55B,0x8556,0x1555,0x5557,0x1540,
	0xD558,0x0554,0x5555,0x5556,0x1540,0xFFF0,0x1555,0x5555,
	0x5554,0x1540,0xC000,0x1555,0x5555,0x555D,0x5540,0xD001,
	0x5555,0x5555,0x5550,0x5540,0xD541,0xD555,0x5555,0x5555,
	0x5540,0xD550,0x1557,0x5555,0x5554,0x5540,0xD558,0x1556,
	0x5555,0x5555,0x5540,0xD550,0x1556,0x5555,0x5555,0x1540,
	0xD551,0x055C,0x5555,0x5555,0x1540,0xD555,0x05F0,0x5555,
	0x5555,0x9540,0xD555,0x4580,0x1555,0x5555,0x1540,0xD555,
	0xC000,0x1555,0x5555,0x1540,0xD555,0x8001,0x1555,0x5555,
	0x9540,0xD555,0x0005,0x1555,0x5557,0x1540,0xD555,0x1515,
	0x8555,0x5556,0x0540,0xD555,0x5555,0x8555,0x555C,0x0540,
	0xD555,0x1555,0x0555,0x5550,0x5540,0xD555,0x5555,0x4155,
	0x5574,0x5540,0xD555,0x5555,0x5155,0x5564,0x5540,0xD555,
	0x5555,0x7155,0x55C6,0x5540,0xD555,0x5555,0x403D,0x5F80,
	0x1540,0xD555,0x5555,0x7001,0x5800,0x1540,0xD555,0x5555,
	0x4040,0x5014,0x5540,0xD555,0x5555,0x5040,0x5015,0x5540,
	0xD555,0x5555,0x5458,0x5955,0x5540,0xD555,0x5555,0x5558,
	0x5955,0x5540,0xD555,0x5555,0x5558,0x7155,0x5540,0xD555,
	0x5555,0x5550,0x6055,0x5540,0xD555,0x5555,0x5550,0x0155,
	0x5540,0xD555,0x5555,0x5554,0x0555,0x5540,0xD555,0x5555,
	0x5555,0x8555,0x5540,0xD555,0x5555,0x5555,0x8555,0x5540,
	0xD555,0x5555,0x5555,0x1155,0x5540,0xD555,0x5555,0x5555,
	0x5555,0x5540,0xD555,0x5555,0x5555,0x5555,0x5540,0xD555,
	0x5555,0x5555,0x5555,0x5540,0xD555,0x5555,0x5555,0x5555,
	0x5540,0x8000,0x0000,0x0000,0x0000,0x0000
};

	/* Definitions for the picture icon image. */

struct Image Picture1Image =
{
	0,0,
	76,70,2,
	(USHORT *)&Picture1Data[0],
	0x03,0x00,
	(struct Image *)NULL
};

	/* Tool types for the picture project icon. */

char *PictureToolTypes[] =
{
	"FILETYPE=ILBM",
	NULL
};

	/* The name of the project's default tool (will be filled
	 * in later).
	 */

UBYTE DefaultTool[256];

	/* Definitions for the picture project icon. */

struct DiskObject PictureIcon =
{
	WB_DISKMAGIC,
	WB_DISKVERSION,

	(struct Gadget *)NULL,
	0,0,
	76,70,
	0x0004,
	0x0003,
	0x0001,
	(APTR)&Picture1Image,
	(APTR)NULL,
	(struct IntuiText *)NULL,
	NULL,
	(APTR)NULL,
	NULL,
	(APTR)NULL,

	WBPROJECT,
	(char *)NULL,
	PictureToolTypes,
	NO_ICON_POSITION,
	NO_ICON_POSITION,
	(struct DrawerData *)NULL,
	(char *)NULL,
	20000
};

	/* Run-dump of the anim icon image data. */

UWORD Anim1Data[700] =
{
	0x0000,0x0000,0x0000,0x0000,0x0010,0x0000,0x0000,0x0000,
	0x0000,0x0030,0x0000,0x0000,0x0000,0x0000,0x0030,0x0000,
	0x0000,0x0000,0x0000,0x0030,0x0000,0x0000,0x0000,0x0000,
	0x0030,0x0000,0x0000,0x0000,0x4000,0x0030,0x0000,0x0000,
	0x0000,0x0000,0x0030,0x0000,0x0000,0x0000,0x0400,0x0030,
	0x0000,0x0000,0x0000,0x1C00,0x0030,0x0000,0x0000,0x0000,
	0x0000,0x0030,0x0000,0x0000,0x0000,0x4800,0x0030,0x0000,
	0x0000,0x0000,0xC000,0x0030,0x0000,0x0000,0x0005,0x8000,
	0x0030,0x0000,0x0000,0x0001,0x0300,0x0030,0x0000,0x0000,
	0x0001,0x0200,0x0030,0x0000,0x0000,0x0001,0x0200,0x0030,
	0x0000,0x0000,0x0100,0x0020,0x0030,0x0000,0x0000,0x002F,
	0x8E80,0x8030,0x0000,0x0000,0x0328,0x0080,0x0030,0x0000,
	0x0000,0x0260,0x002E,0xC030,0x0003,0xFFD0,0x0200,0x0008,
	0x8030,0x0002,0x0010,0x0200,0x0000,0x8030,0x0002,0x0010,
	0x1600,0x0000,0x0030,0x0002,0x0010,0x1400,0x0000,0x8030,
	0x0002,0x2010,0x1000,0x0000,0x0030,0x0002,0x0010,0x3000,
	0x0000,0x9030,0x0002,0x4010,0x2000,0x0000,0x3030,0x0002,
	0x0010,0x6000,0x0000,0x6030,0x0002,0x0F10,0x4000,0x0000,
	0x0030,0x0002,0x0810,0xC000,0x0000,0x2030,0x0002,0x0810,
	0x0000,0x0000,0x6030,0x0002,0x0010,0xC000,0x0000,0x4030,
	0x0000,0x0010,0x8000,0x0000,0x4030,0x0003,0xFFF0,0x8000,
	0x0000,0xC030,0x0003,0x6000,0x0000,0x0000,0x8030,0x0F82,
	0x0000,0x0000,0x0001,0x0030,0x0020,0x0000,0x0000,0x0000,
	0x0030,0x0008,0x0000,0x0000,0x0000,0x0030,0x0000,0x2000,
	0x0000,0x0000,0x0030,0x0003,0x8000,0x8000,0x0000,0x4030,
	0x0006,0x0000,0x8000,0x0000,0x4030,0x0000,0x4800,0x0000,
	0x0000,0x0030,0x0000,0x0802,0x4000,0x0000,0x2030,0x0000,
	0x0006,0x4000,0x0000,0x6030,0x0000,0x060C,0x0000,0x0000,
	0x0030,0x0000,0x3C78,0x0000,0x0000,0x0030,0x0000,0x20C0,
	0x0000,0x0000,0x1030,0x0000,0x2000,0x1800,0x0000,0xF030,
	0x0000,0x400C,0x0000,0x0C01,0x8030,0x0000,0x001C,0x2000,
	0x1E01,0x0030,0x0000,0x001C,0x0200,0x0C00,0x0030,0x0000,
	0x001E,0x0200,0x0000,0x0030,0x0000,0x003E,0x0DF0,0x7C1F,
	0xF830,0x0000,0x003E,0x0FF8,0xFC1F,0xFC30,0x0000,0x0037,
	0x0F18,0x0D1D,0xCC30,0x0000,0x0073,0x0E19,0x8D59,0xCC30,
	0x0000,0x0073,0x0E19,0x0C19,0xCC30,0x0000,0x0063,0x8C19,
	0x0C19,0xCC30,0x0000,0x00E1,0x8C18,0x0C19,0xCC30,0x0000,
	0x00FF,0x8C18,0x0D19,0xCC30,0x0000,0x00FF,0xCC19,0x0C19,
	0xCC30,0x0000,0x01C1,0xCC19,0x0C19,0xCC30,0x0000,0x01C0,
	0xCC18,0x0C19,0xCC30,0x0000,0x0180,0xEC18,0x2C19,0xCC30,
	0x0000,0x0180,0xEC18,0x2C19,0xCC30,0x0000,0x0100,0x6C18,
	0x2C18,0x8C30,0x0000,0x0000,0x0000,0x0000,0x0030,0x0000,
	0x0000,0x0000,0x0000,0x0030,0x0000,0x0000,0x0000,0x0000,
	0x0030,0x7FFF,0xFFFF,0xFFFF,0xFFFF,0xFFF0,

	0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFE0,0xD555,0x5555,0x5555,
	0x5555,0x5540,0xD555,0x5555,0x5555,0x5555,0x5540,0xD555,
	0x5555,0x5555,0x5555,0x5540,0xD555,0x5555,0x5555,0x5555,
	0x5540,0xD555,0x5555,0x5555,0x1555,0x5540,0xD555,0x5555,
	0x5555,0x3555,0x5540,0xD555,0x5555,0x5555,0x8155,0x5540,
	0xD555,0x5555,0x5555,0x8155,0x5540,0xD555,0x5555,0x5557,
	0x0555,0x5540,0xD555,0x5555,0x555C,0x0555,0x5540,0xD555,
	0x5555,0x5550,0x0155,0x5540,0xD555,0x5555,0x5558,0x5155,
	0x5540,0xD555,0x5555,0x5558,0x5855,0x5540,0xD555,0x5555,
	0x7558,0x59D5,0x5540,0xD555,0x5555,0x6DF8,0x7197,0x5540,
	0xD555,0x5555,0x6080,0x401E,0x5540,0xD555,0x5555,0x4000,
	0x5010,0x5540,0xD55F,0xFFC5,0x5015,0x5500,0x5540,0xD558,
	0x0005,0x5115,0x5540,0x1540,0xD558,0x0005,0xF155,0x5554,
	0x5540,0xD559,0x5545,0x8155,0x5551,0x5540,0xD559,0xD545,
	0x8155,0x5554,0x5540,0xD559,0x1545,0x8155,0x5556,0x5540,
	0xD559,0x9745,0x8555,0x5554,0x1540,0xD559,0x1E45,0x0555,
	0x5555,0x0540,0xD559,0x0045,0x1555,0x5555,0x0540,0xD559,
	0x4045,0x1555,0x5555,0x1540,0xD559,0xC040,0x1555,0x5555,
	0x1540,0xD559,0x0540,0x1555,0x5555,0x1540,0xD559,0x8544,
	0x5555,0x5555,0x1540,0xD559,0xFFC6,0x1555,0x5557,0x1540,
	0xD550,0x0004,0x5555,0x5556,0x1540,0xFFF0,0x0005,0x5555,
	0x5554,0x1540,0xC000,0x1555,0x5555,0x555D,0x5540,0xD001,
	0x5555,0x5555,0x5550,0x5540,0xD541,0xD555,0x5555,0x5555,
	0x5540,0xD550,0x1557,0x5555,0x5554,0x5540,0xD558,0x1556,
	0x5555,0x5555,0x5540,0xD550,0x1556,0x5555,0x5555,0x1540,
	0xD551,0x055C,0x5555,0x5555,0x1540,0xD555,0x05F0,0x5555,
	0x5555,0x9540,0xD555,0x4580,0x1555,0x5555,0x1540,0xD555,
	0xC000,0x1555,0x5555,0x1540,0xD555,0x8001,0x1555,0x5555,
	0x9540,0xD555,0x0005,0x1555,0x5557,0x1540,0xD555,0x1515,
	0x8555,0x5556,0x0540,0xD555,0x5551,0x8555,0x515C,0x0540,
	0xD555,0x1545,0x0555,0x4150,0x5540,0xD555,0x5551,0x4155,
	0x5274,0x5540,0xD555,0x5551,0x5155,0x4D64,0x5540,0xD555,
	0x5542,0x7005,0x01C0,0x0540,0xD555,0x5528,0x4D15,0x7D9C,
	0xC140,0xD555,0x5528,0x6248,0x8002,0x1140,0xD555,0x5505,
	0x4100,0x0005,0x0140,0xD555,0x5550,0x4040,0x4001,0x4140,
	0xD555,0x5550,0x4240,0x4141,0x4140,0xD555,0x5502,0x8140,
	0x4141,0x4140,0xD555,0x54A0,0x0140,0x6141,0x4140,0xD555,
	0x5480,0x0140,0x6041,0x4140,0xD555,0x543F,0x4140,0x0141,
	0x4140,0xD555,0x5501,0x0140,0x0141,0x4140,0xD555,0x5554,
	0x0141,0x8141,0x4140,0xD555,0x5414,0xA141,0x8141,0x4140,
	0xD555,0x5414,0x8141,0x0141,0x4140,0xD555,0x5555,0x6D59,
	0x4D58,0x8D40,0xD555,0x5455,0x1145,0x5145,0x5140,0xD555,
	0x5555,0x5555,0x5555,0x5540,0xD555,0x5555,0x5555,0x5555,
	0x5540,0x8000,0x0000,0x0000,0x0000,0x0000
};

	/* Definitions for the anim icon image. */

struct Image Anim1Image =
{
	0,0,
	76,70,2,
	(UWORD *)&Anim1Data[0],
	0x03,0x00,
	(struct Image *)NULL
};

	/* Tool types for the anim project icon. */

char *AnimToolTypes[] =
{
	"FILETYPE=ANIM|ILBM",
	NULL
};

	/* Definitions for the anim project icon. */

struct DiskObject AnimIcon =
{
	WB_DISKMAGIC,
	WB_DISKVERSION,

	(struct Gadget *)NULL,
	23,15,
	76,70,
	0x0004,
	0x0003,
	0x0001,
	(APTR)&Anim1Image,
	(APTR)NULL,
	(struct IntuiText *)NULL,
	NULL,
	(APTR)NULL,
	NULL,
	(APTR)NULL,

	WBPROJECT,
	(char *)NULL,
	AnimToolTypes,
	NO_ICON_POSITION,
	NO_ICON_POSITION,
	(struct DrawerData *)NULL,
	(char *)NULL,
	20000
};

	/* CycleColours(BYTE Forward):
	 *
	 *	Tiny subroutine, handles the actual colour cycling.
	 */

VOID __regargs
CycleColours(BYTE Forward)
{
	if(Forward)
	{
		ULONG	Size,
			Top,
			i;

		Size = GetPaletteSize(Palette);

		Top = GetPaletteEntry(Palette,Size - 1);

		for(i = Size - 1 ; i > 2 ; i--)
			SetPaletteEntry(Palette,GetPaletteEntry(Palette,i - 1),i);

		SetPaletteEntry(Palette,Top,2);
	}
	else
	{
		ULONG	Bottom,
			Size,
			i;

		Size = GetPaletteSize(Palette);

		Bottom = GetPaletteEntry(Palette,2);

		for(i = 2 ; i < Size - 1 ; i++)
			SetPaletteEntry(Palette,GetPaletteEntry(Palette,i + 1),i);

		SetPaletteEntry(Palette,Bottom,Size - 1);
	}

	LoadPalette(Screen,Palette,0);
}

	/* SetProperties(ULONG ID):
	 *
	 *	Queries the properties of a certain display mode and adjusts
	 *	the program parameters accordingly.
	 */

BYTE __regargs
SetProperties(ULONG ID)
{
	struct DisplayInfo DisplayInfo;

		/* Query the properties... */

	if(GetDisplayInfoData(NULL,(APTR)&DisplayInfo,sizeof(struct DisplayInfo),DTAG_DISP,ID))
	{
			/* More information available? */

		if(Is39)
		{
				/* Does this mode support double-bufferin? */

			if(DisplayInfo . PropertyFlags & DIPF_IS_DBUFFER)
				UseDouble = TRUE;
			else
				UseDouble = FALSE;

				/* Is this a native Amiga display mode? */

			if(DisplayInfo . PropertyFlags & DIPF_IS_FOREIGN)
				IsNative = FALSE;
			else
				IsNative = TRUE;

				/* Determine maximum available colour range. */

			MaxRange = 8;

			if(DisplayInfo . RedBits < MaxRange)
				MaxRange = DisplayInfo . RedBits;

			if(DisplayInfo . GreenBits < MaxRange)
				MaxRange = DisplayInfo . GreenBits;

			if(DisplayInfo . BlueBits < MaxRange)
				MaxRange = DisplayInfo . BlueBits;

			MaxRange = 1 << MaxRange;
		}
		else
		{
			UseDouble	= TRUE;
			IsNative	= TRUE;

			MaxRange	= 16;
		}

		return(TRUE);
	}
	else
		return(FALSE);
}

	/* GetModeSize(ULONG ID):
	 *
	 *	Return the approriate screen size of a certain display mode.
	 */

LONG __regargs
GetModeSize(ULONG ID)
{
	struct DimensionInfo DimensionInfo;

	if(GetDisplayInfoData(NULL,(APTR)&DimensionInfo,sizeof(struct DimensionInfo),DTAG_DIMS,ID))
	{
		LONG	ScanWidth,
			ScanHeight,

			Width	= 4,
			Height	= 4;

		ScanWidth	= DimensionInfo . StdOScan . MaxX - DimensionInfo . StdOScan . MinX + 1;
		ScanHeight	= DimensionInfo . StdOScan . MaxY - DimensionInfo . StdOScan . MinY + 1;

		while(Width < ScanWidth)
			Width *= 2;

		if(Width > ScanWidth)
			Width /= 2;

		while(Height < ScanHeight)
			Height *= 2;

		if(Height > ScanHeight)
			Height /= 2;

		if(Height < Width)
			Width = Height;

		if(Width < 256 || Width < DimensionInfo . MinRasterWidth || Height < DimensionInfo . MinRasterHeight)
			return(0);
		else
			return(Width);
	}
	else
		return(0);
}

	/* ModeFilter(register __a1 ULONG ID):
	 *
	 *	Called by SelectDisplayMode(), filters out unapproriate display mode IDs.
	 */

LONG __saveds __asm
ModeFilter(register __a1 ULONG ID)
{
	struct DimensionInfo	DimensionInfo;
	LONG			Result = TRUE;

	if(GetDisplayInfoData(NULL,(APTR)&DimensionInfo,sizeof(struct DimensionInfo),DTAG_DIMS,ID))
	{
			/* Minimum are 4 bitplanes. */

		if(DimensionInfo . MaxDepth < 4)
			Result = FALSE;

		if(Result)
		{
				/* Does the screen size fit? */

			if(!GetModeSize(ID))
				Result = FALSE;
		}
	}

	if(Result)
	{
		struct DisplayInfo DisplayInfo;

		if(GetDisplayInfoData(NULL,(APTR)&DisplayInfo,sizeof(struct DisplayInfo),DTAG_DISP,ID))
		{
				/* 4096 colours are minimum. */

			if(DisplayInfo . PaletteRange < 4096)
				Result = FALSE;

			if(Is39)
			{
					/* Only eight bits per component are supported so far. */

				if(DisplayInfo . RedBits > 8 || DisplayInfo . GreenBits > 8 || DisplayInfo . BlueBits > 8)
					Result = FALSE;
			}
		}

		if(Result)
		{
			if(DisplayInfo . Resolution . x)
			{
					/* The display aspect ration should be 1:1. */

				if(DisplayInfo . Resolution . y / DisplayInfo . Resolution . x > 1)
					Result = FALSE;
			}
		}
	}

	return(Result);
}

	/* SelectDisplayMode(struct Window *Window,ULONG *Mode):
	 *
	 *	Select a new screen display mode.
	 */

BYTE __regargs
SelectDisplayMode(struct Window *Window,ULONG *Mode)
{
	struct ScreenModeRequester	*Request;
	struct Hook			 FilterHook;
	BYTE				 Result = FALSE;

	FilterHook . h_Entry	= ModeFilter;
	FilterHook . h_SubEntry	= NULL;
	FilterHook . h_Data	= NULL;

	if(Request = (struct ScreenModeRequester *)AllocAslRequestTags(ASL_ScreenModeRequest,
		ASLSM_Window,		Window,
		ASLSM_FilterFunc,	&FilterHook,
		ASLSM_InitialDisplayID,	*Mode,
		ASLSM_PropertyFlags,	0,
		ASLSM_PropertyMask,	(DIPF_IS_HAM|DIPF_IS_DUALPF|DIPF_IS_PF2PRI|DIPF_IS_EXTRAHALFBRITE),
	TAG_DONE))
	{
		if(AslRequestTags(Request,TAG_DONE))
		{
			struct DimensionInfo DimensionInfo;

			if(GetDisplayInfoData(NULL,(APTR)&DimensionInfo,sizeof(struct DimensionInfo),DTAG_DIMS,Request -> sm_DisplayID))
			{
				if(DimensionInfo . MaxDepth > 8)
					Depth = 8;
				else
					Depth = DimensionInfo . MaxDepth;

				*Mode = Request -> sm_DisplayID;

				Result = TRUE;
			}
		}

		FreeAslRequest(Request);
	}

	return(Result);
}

	/* DrawSquare():
	 *
	 *	Draws a square rectangle at a given position.
	 */

VOID __regargs
DrawSquare(struct RastPort *RPort,WORD X,WORD Y,WORD Width)
{
	Move(RPort,X,Y);
	Draw(RPort,X + Width - 1,Y);
	Draw(RPort,X + Width - 1,Y + Width - 1);
	Draw(RPort,X,Y + Width - 1);
	Draw(RPort,X,Y);
}

	/* DrawLine():
	 *
	 *	Draw a simple line from a to b.
	 */

VOID __regargs
DrawLine(struct RastPort *RPort,UWORD FromX,UWORD FromY,UWORD ToX,UWORD ToY)
{
	Move(RPort,FromX,FromY);
	Draw(RPort,ToX,ToY);
}

	/* IterationSetup():
	 *
	 *	Sets up the iterations menu, checkmarks the currently
	 *	selected number of iterations, uncheckmarks all the
	 *	others.
	 */

VOID
IterationSetup()
{
	struct MenuItem	*Item;
	WORD		 i;

	Window -> Flags |= WFLG_RMBTRAP;

	for(i = 0 ; i < 10 ; i++)
	{
		Item = ItemAddress(Menu,FULLMENUNUM(2,i,NOSUB));

		if((1 << (ULONG)GTMENUITEM_USERDATA(Item)) == MaxIteration + 1)
			Item -> Flags |= CHECKED;
		else
			Item -> Flags &= ~CHECKED;
	}

	Window -> Flags &= ~WFLG_RMBTRAP;
}

	/* CycleServer():
	 *
	 *	The interrupt server routine which handles the
	 *	colour cycling.
	 */

VOID __saveds
CycleServer()
{
	struct MsgPort		*TimePort;
	struct timerequest	*TimeRequest;
	ULONG			 Signals;

	if(TimePort = CreateMsgPort())
	{
		if(TimeRequest = CreateIORequest(TimePort,sizeof(struct timerequest)))
		{
			if(!OpenDevice(TIMERNAME,UNIT_VBLANK,TimeRequest,NULL))
			{
				Signal(ThisProcess,SIG_HANDSHAKE);

				FOREVER
				{
					TimeRequest -> tr_node . io_Command	= TR_ADDREQUEST;
					TimeRequest -> tr_time . tv_secs	= 0;
					TimeRequest -> tr_time . tv_micro	= SECOND / 20;

					SendIO(TimeRequest);

					Signals = Wait(SIG_TIMER | SIG_KILL);

					if(Signals & SIG_KILL)
					{
						WaitIO(TimeRequest);

						CloseDevice(TimeRequest);

						DeleteIORequest(TimeRequest);

						DeleteMsgPort(TimePort);

						Forbid();

						Signal(ThisProcess,SIG_HANDSHAKE);

						RemTask(CycleTask = NULL);
					}

					if(Signals & SIG_TIMER)
					{
						WaitIO(TimeRequest);

						if(DoCycle && !HoldIt)
							CycleColours(Forward);
					}
				}
			}

			DeleteIORequest(TimeRequest);
		}

		DeleteMsgPort(TimePort);
	}

	Forbid();

	CycleTask = NULL;

	Signal(ThisProcess,SIG_HANDSHAKE);
}

	/* CloseWindowSafely(struct Window *Window):
	 *
	 *	Close a window freeing all messages pending at
	 *	its user port (taken from example source code
	 *	published once upon a time in Amiga Mail).
	 */

VOID
CloseWindowSafely(struct Window *Window)
{
	struct IntuiMessage	*IntuiMessage;
	struct Node		*Successor;

	Forbid();

	IntuiMessage = (struct IntuiMessage *)Window -> UserPort -> mp_MsgList . lh_Head;

	while(Successor = IntuiMessage -> ExecMessage . mn_Node . ln_Succ)
	{
		if(IntuiMessage -> IDCMPWindow == Window)
		{
			Remove(IntuiMessage);

			ReplyMsg((struct Message *)IntuiMessage);
		}

		IntuiMessage = (struct IntuiMessage *)Successor;
	}

	Window -> UserPort = NULL;

	ModifyIDCMP(Window,NULL);

	Permit();

	CloseWindow(Window);
}

	/* GfxCleanup():
	 *
	 *	Frees the graphics resources allocated by the
	 *	program.
	 */

VOID
GfxCleanup()
{
	DoCycle = FALSE;

	Delay(5);

	if(Screen)
		ScreenToBack(Screen);

	if(Window)
	{
		ClearMenuStrip(Window);

		CloseWindow(Window);

		Window = NULL;
	}

	if(Menu)
	{
		FreeMenus(Menu);

		Menu = NULL;
	}

	if(VisualInfo)
	{
		FreeVisualInfo(VisualInfo);

		VisualInfo = NULL;
	}

	if(Screen)
	{
		CloseScreen(Screen);

		Screen = NULL;
	}

	if(Palette)
	{
		FreePalette(Palette);

		Palette = NULL;
	}

	if(Range)
	{
		FreeVec(Range);

		Range = NULL;
	}

	ThisProcess -> pr_WindowPtr = OldPtr;

	if(LineBitMap . Planes[0])
	{
		FreeVec(LineBitMap . Planes[0]);

		LineBitMap . Planes[0] = NULL;
	}
}

	/* GfxSetup():
	 *
	 *	Allocates the graphics resources the program requires.
	 */

LONG
GfxSetup()
{
	struct Rectangle	 DisplayClip;
	LONG			 Differ;
	WORD			 i;
	double			 a,p;
	UBYTE			 Wrap = (1 << Depth) - 2;
	struct ColorSpec	*ColorSpec;

	if(Depth > 5 && !LevelPatched)
	{
		WORD LevelMax = (1 << Depth) - 1;

		LevelPatched = TRUE;

		RedLevel	= (LevelMax * RedLevel) / 31;
		GreenLevel	= (LevelMax * GreenLevel) / 31;
		BlueLevel	= (LevelMax * BlueLevel) / 31;
	}

	if(Depth <= 5)
		MaxRange = 16;

	if(!(Palette = AllocPalette(1 << Depth,Depth == 8)))
		return(FALSE);

	if(!(Range = (UBYTE *)AllocVec(1 << Depth,MEMF_ANY)))
		return(FALSE);

	a = PI / (double)((1 << Depth) - 2);

		/* Build a range of colour intensities mapped to the
		 * sine wave amplitude.
		 */

	for(p = 0.0, i = 0 ; (p < PI) && (i < (1 << Depth) - 2) ; p += a, i++)
		Range[i] = (UBYTE)(sin(p) * (double)(MaxRange - 1) - 0.3);

	if(Depth == 8)
	{
		SetPaletteTriplet(Palette,0x00,0x00,0x00,0);
		SetPaletteTriplet(Palette,0xFF,0xFF,0xFF,1);
	}
	else
	{
		SetPaletteTriplet(Palette,0x0,0x0,0x0,0);
		SetPaletteTriplet(Palette,0xF,0xF,0xF,1);
	}

		/* Combine all three colour ranges into a single palette. */

	for(i = 0 ; i < Wrap ; i++)
		SetPaletteTriplet(Palette,Range[(RedLevel + i) % Wrap],Range[(GreenLevel + i) % Wrap],Range[(BlueLevel + i) % Wrap],2 + i);

		/* Create the colour transition table. */

	for(i = 0 ; i < 16384 ; i++)
		Wave[i] = 2 + (i % ((1 << Depth) - 2));

		/* Create the main screen, first query how wide the
		 * current overscan settings allow a screen to be.
		 */

	if(!QueryOverscan(Mode,&DisplayClip,OSCAN_VIDEO))
		return(RETURN_FAIL + 12);

		/* Open a centered screen. */

	Differ = ((DisplayClip . MaxX - DisplayClip . MinX + 1) - Square) / 2;

	DisplayClip . MinX += Differ;
	DisplayClip . MaxX -= Differ;

		/* Allocate a couple of colour specification structures. */

	if(!(ColorSpec = (struct ColorSpec *)AllocVec(sizeof(struct ColorSpec) * ((1 << Depth) + 1),MEMF_ANY | MEMF_CLEAR)))
	{
			/* Open a screen with monochrome system imagery. */

		if(!(Screen = OpenScreenTags(NULL,
			SA_Width,	Square,
			SA_Height,	Square,
			SA_DClip,	&DisplayClip,
			SA_Left,	DisplayClip . MinX,
			SA_Depth,	Depth,
			SA_DisplayID,	Mode,
			SA_Font,	&DefaultFont,
			SA_ShowTitle,	FALSE,
			SA_Quiet,	TRUE,
			SA_AutoScroll,	TRUE,
			SA_Behind,	TRUE,
		TAG_END)))
			return(RETURN_FAIL + 13);
	}
	else
	{
			/* Set up the colour specifications to open
			 * an entirely black screen.
			 */

		for(i = 0 ; i < (1 << Depth) ; i++)
			ColorSpec[i] . ColorIndex = i;

		ColorSpec[i] . ColorIndex = -1;

			/* Open a screen with monochrome system imagery. */

		if(!(Screen = OpenScreenTags(NULL,
			SA_Width,	Square,
			SA_Height,	Square,
			SA_DClip,	&DisplayClip,
			SA_Left,	DisplayClip . MinX,
			SA_Depth,	Depth,
			SA_DisplayID,	Mode,
			SA_Font,	&DefaultFont,
			SA_ShowTitle,	FALSE,
			SA_Quiet,	TRUE,
			SA_AutoScroll,	TRUE,
			SA_Behind,	TRUE,
			SA_Colors,	ColorSpec,
		TAG_END)))
		{
			FreeVec(ColorSpec);

			return(RETURN_FAIL + 13);
		}
		else
			FreeVec(ColorSpec);
	}

		/* Set up the remaining data. */

	if(!(VisualInfo = GetVisualInfo(Screen,TAG_DONE)))
		return(RETURN_FAIL + 14);

	if(!(Menu = CreateMenus(MandelbrotMenu,
		GTMN_FrontPen, 0,
	TAG_DONE)))
		return(RETURN_FAIL + 15);

	if(!LayoutMenus(Menu,	VisualInfo,
		GTMN_TextAttr,	&DefaultFont,
	TAG_DONE))
		return(RETURN_FAIL + 16);

	if(!(Window = OpenWindowTags(NULL,
		WA_Width,	Screen -> Width,
		WA_Height,	Screen -> Height,
		WA_Borderless,	TRUE,
		WA_Backdrop,	TRUE,
		WA_IDCMP,	IDCMP_MOUSEBUTTONS | IDCMP_MOUSEMOVE | IDCMP_MENUPICK | IDCMP_RAWKEY | IDCMP_VANILLAKEY | IDCMP_MENUVERIFY,
		WA_RMBTrap,	TRUE,
		WA_CustomScreen,Screen,
	TAG_END)))
		return(RETURN_FAIL + 17);

		/* Create a bitmap which is exactly as wide as the
		 * main screen, but only a single line high.
		 */

	InitBitMap(&LineBitMap,Depth,Square,1);

		/* Allocate space for the bitmap, note that the
		 * bitplanes are to reside in fast ram.
		 */

	if(LineBitMap . Planes[0] = AllocVec(LineBitMap . BytesPerRow * LineBitMap . Depth,MEMF_FAST | MEMF_CLEAR))
	{
		for(i = 1 ; i < LineBitMap . Depth ; i++)
			LineBitMap . Planes[i] = &LineBitMap . Planes[i - 1][LineBitMap . BytesPerRow];
	}

	SetMenuStrip(Window,Menu);

	OffMenu(Window,FULLMENUNUM(0,9,NOSUB));

	if(!UseDouble)
	{
		OffMenu(Window,FULLMENUNUM(0, 6,NOSUB));
		OffMenu(Window,FULLMENUNUM(0, 8,NOSUB));
		OffMenu(Window,FULLMENUNUM(0, 9,NOSUB));
		OffMenu(Window,FULLMENUNUM(0,10,NOSUB));
		OffMenu(Window,FULLMENUNUM(0,11,NOSUB));
		OffMenu(Window,FULLMENUNUM(0,12,NOSUB));
	}

	RPort = &Screen -> RastPort;
	VPort = &Screen -> ViewPort;

	SetRast(RPort,0);

	Window -> Flags &= ~WFLG_RMBTRAP;

	ThisProcess -> pr_WindowPtr = (APTR)Window;

	return(0);
}

	/* CloseAll(LONG ReturnCode):
	 *
	 *	Close all remaining resources and terminate the program.
	 */

VOID __regargs
CloseAll(LONG ReturnCode)
{
	SetTaskPri(ThisProcess,OldPri);

	StopAnim();

	if(CycleTask)
	{
		DoCycle = FALSE;

		Signal(CycleTask,SIG_KILL);

		Wait(SIG_HANDSHAKE);
	}

	if(GlobalBitMap)
		FreeCustomBitMap(GlobalBitMap);

	if(GlobalPalette)
		FreePalette(GlobalPalette);

	if(Wave)
		FreeVec(Wave);

	GfxCleanup();

	if(BackupBitMap)
		FreeCustomBitMap(BackupBitMap);

	if(TimeRequest)
	{
		if(TimeRequest -> tr_node . io_Device)
			CloseDevice(TimeRequest);

		DeleteIORequest(TimeRequest);
	}

	if(TimePort)
		DeleteMsgPort(TimePort);

	if(IconBase)
		CloseLibrary(IconBase);

	if(AslBase)
		CloseLibrary(AslBase);

	if(IFFParseBase)
		CloseLibrary(IFFParseBase);

	if(GadToolsBase)
		CloseLibrary(GadToolsBase);

	if(GfxBase)
		CloseLibrary(GfxBase);

	if(IntuitionBase)
		CloseLibrary(IntuitionBase);

	exit(ReturnCode);
}

	/* OpenAll():
	 *
	 *	Open libraries and other data which does not
	 *	require changing when the display modes are
	 *	altered.
	 */

VOID
OpenAll()
{
	ThisProcess = (struct Process *)FindTask(NULL);

	OldPri = SetTaskPri(ThisProcess,0);

	OldPtr = ThisProcess -> pr_WindowPtr;

	if(!(IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library",37)))
		CloseAll(RETURN_FAIL + 0);

	if(IntuitionBase -> LibNode . lib_Version >= 39)
		Is39 = TRUE;
	else
		Is39 = FALSE;

	if(!(GfxBase = (struct GfxBase *)OpenLibrary("graphics.library",37)))
		CloseAll(RETURN_FAIL + 1);

	if(!(GadToolsBase = OpenLibrary("gadtools.library",37)))
		CloseAll(RETURN_FAIL + 2);

	if(!(IFFParseBase = OpenLibrary("iffparse.library",38)))
		CloseAll(RETURN_FAIL + 3);

	if(!(AslBase = OpenLibrary("asl.library",38)))
		CloseAll(RETURN_FAIL + 4);

	if(!(TimePort = (struct MsgPort *)CreateMsgPort()))
		CloseAll(RETURN_FAIL + 5);

	if(!(TimeRequest = (struct timerequest *)CreateIORequest(TimePort,sizeof(struct timerequest))))
		CloseAll(RETURN_FAIL + 6);

	if(OpenDevice(TIMERNAME,UNIT_VBLANK,TimeRequest,0))
		CloseAll(RETURN_FAIL + 7);

	if(!(BackupBitMap = AllocCustomBitMap(8,25 * 8,8)))
		CloseAll(RETURN_FAIL + 8);

	if(!(Wave = (BYTE *)AllocVec(16384,MEMF_ANY | MEMF_CLEAR)))
		CloseAll(RETURN_FAIL + 9);

	if(!(IconBase = OpenLibrary("icon.library",37)))
		CloseAll(RETURN_FAIL + 10);

	TimerBase = &TimeRequest -> tr_node . io_Device -> dd_Library;

	if(!(CycleTask = CreateTask("Mandel² cycling",10,CycleServer,4096)))
		CloseAll(RETURN_FAIL + 11);
	else
	{
		Wait(SIG_HANDSHAKE);

		if(!CycleTask)
			CloseAll(RETURN_FAIL + 11);
	}
}

	/* CopyLine(struct BitMap *BitMap,WORD Line):
	 *
	 *	Copies the contents of the single line bitmap to a given
	 *	line on screen.
	 */

VOID __regargs
CopyLine(struct BitMap *BitMap,WORD Line)
{
	WORD i;

	Line *= BitMap -> BytesPerRow;

	for(i = 0 ; i < BitMap -> Depth ; i++)
		CopyMemQuick(BitMap -> Planes[i],&RPort -> BitMap -> Planes[i][Line],BitMap -> BytesPerRow);
}

	/* GetTime():
	 *
	 *	Asks the timer.device for the current system time.
	 */

VOID
GetTime()
{
	TimeRequest -> tr_node . io_Command = TR_GETSYSTIME;

	DoIO(TimeRequest);
}

	/* BlockWindow():
	 *
	 *	Blocks the menu bar of the window and sets the busy
	 *	mouse pointer.
	 */

VOID
BlockWindow()
{
	HoldIt = TRUE;

	SetWait(Window);

	Window -> Flags |= WFLG_RMBTRAP;
}

	/* ReleaseWindow():
	 *
	 *	Reenables the menu bar of the window and resets the
	 *	mouse pointer to the default mouse pointer.
	 */

VOID
ReleaseWindow()
{
	ClearPointer(Window);

	Window -> Flags &= ~WFLG_RMBTRAP;

	HoldIt = FALSE;
}

	/* GetFile():
	 *
	 *	Get a file name using the asl.library file requester.
	 */

BYTE
GetFile(UBYTE *Title,UBYTE *Directory,UBYTE *Name,UBYTE *Buffer,UBYTE *Pattern,BYTE SaveFlag)
{
	struct FileRequester	*AslFileRequest;
	BYTE			 Result = FALSE;
	LONG			 Flags;

	if(!Directory[0])
		NameFromLock(ThisProcess -> pr_CurrentDir,Directory,256);

	if(Pattern)
	{
		Flags = FILF_PATGAD;

		if(!Pattern[0])
			Pattern = "~(#?.info)";
	}
	else
	{
		Flags = 0;

		Pattern = "~(#?.info)";
	}

	if(SaveFlag)
		Flags |= FILF_SAVE;

	if(AslFileRequest = (struct FileRequester *)AllocAslRequestTags(ASL_FileRequest,
		ASL_Window,	Window,
		ASL_File,	Name,
		ASL_Dir,	Directory,
		ASL_Hail,	Title,
		ASL_FuncFlags,	Flags,
		ASL_Pattern,	Pattern,
		ASL_OKText,	SaveFlag ? "Save" : "Open",
	TAG_DONE))
	{
		if(RequestFile(AslFileRequest))
		{
			if(AslFileRequest -> rf_File[0])
			{
				strcpy(Buffer,AslFileRequest -> rf_Dir);

				AddPart((UBYTE *)Buffer,(UBYTE *)AslFileRequest -> rf_File,256);

				Result = TRUE;
			}
		}
	}

	FreeFileRequest(AslFileRequest);

	return(Result);
}

	/* MyEasyRequest():
	 *
	 *	EasyRequestArgs stub routine.
	 */

WORD __stdargs
MyEasyRequest(UBYTE *Gadgets,UBYTE *Text,...)
{
	struct EasyStruct	Easy;
	WORD			Result;
	ULONG			IDCMP = NULL;
	va_list	 		VarArgs;

	Easy . es_StructSize	= sizeof(struct EasyStruct);
	Easy . es_Flags		= NULL;
	Easy . es_Title		= (UBYTE *)"Mandel²";
	Easy . es_TextFormat	= (UBYTE *)Text;
	Easy . es_GadgetFormat	= (UBYTE *)Gadgets;

	va_start(VarArgs,Text);
	Result = EasyRequestArgs(Window,&Easy,&IDCMP,VarArgs);
	va_end(VarArgs);

	return(Result);
}

	/* Random(LONG MaxValue)
	 *
	 *	Small random number generator.
	 */

ULONG __regargs
Random(LONG MaxValue)
{
	STATIC ULONG RandomSeed = 213567657;

	RandomSeed = RandomSeed * custom . vhposr + 8754331;

	return(RandomSeed % MaxValue);
}

	/* CreateAllCoordsGadgets():
	 *
	 *	Creates all gadgets required by the coordinates control panel.
	 */

struct Gadget *
CreateAllCoordsGadgets(struct Gadget **GadgetArray,struct Gadget **GadgetList,APTR VisualInfo,UWORD TopEdge)
{
	struct Gadget		*Gadget;
	struct NewGadget	 NewGadget;
	UWORD			 Counter = 0;
	UBYTE			 Buffer[40];

	if(Gadget = CreateContext(GadgetList))
	{
		NewGadget . ng_Width		= COORDS_WIDTH - 20;
		NewGadget . ng_Height		= 14;
		NewGadget . ng_GadgetText	= "Real part (x-coordinate)";
		NewGadget . ng_TextAttr		= &DefaultFont;
		NewGadget . ng_VisualInfo	= VisualInfo;
		NewGadget . ng_GadgetID		= Counter;
		NewGadget . ng_Flags		= PLACETEXT_ABOVE;
		NewGadget . ng_LeftEdge		= 10;
		NewGadget . ng_TopEdge		= 2 + TopEdge + 14;

		sprintf(Buffer,"%g",MinReal);

		GadgetArray[Counter++] = Gadget = CreateGadget(STRING_KIND,Gadget,&NewGadget,
			GTST_MaxChars,		40,
			GTST_String,		Buffer,
			STRINGA_Justification,	STRINGCENTER,
		TAG_DONE);

		NewGadget . ng_GadgetText	= "Imaginary part (y-coordinate)";
		NewGadget . ng_GadgetID		= Counter;
		NewGadget . ng_TopEdge		= NewGadget . ng_TopEdge + NewGadget . ng_Height + 16;

		sprintf(Buffer,"%g",MinImaginary);

		GadgetArray[Counter++] = Gadget = CreateGadget(STRING_KIND,Gadget,&NewGadget,
			GTST_MaxChars,		40,
			GTST_String,		Buffer,
			STRINGA_Justification,	STRINGCENTER,
		TAG_DONE);

		NewGadget . ng_GadgetText	= "Size";
		NewGadget . ng_GadgetID		= Counter;
		NewGadget . ng_TopEdge		= NewGadget . ng_TopEdge + NewGadget . ng_Height + 16;

		sprintf(Buffer,"%g",RealWidth);

		GadgetArray[Counter++] = Gadget = CreateGadget(STRING_KIND,Gadget,&NewGadget,
			GTST_MaxChars,		40,
			GTST_String,		Buffer,
			STRINGA_Justification,	STRINGCENTER,
		TAG_DONE);

		NewGadget . ng_Width		= 68;
		NewGadget . ng_Height		= 12;
		NewGadget . ng_GadgetText	= "Accept";
		NewGadget . ng_GadgetID		= Counter;
		NewGadget . ng_Flags		= 0;
		NewGadget . ng_TopEdge		= COORDS_HEIGHT - 3 - NewGadget . ng_Height;

		GadgetArray[Counter++] = Gadget = CreateGadget(BUTTON_KIND,Gadget,&NewGadget,
			TAG_DONE);

		NewGadget . ng_GadgetText	= "Cancel";
		NewGadget . ng_GadgetID		= Counter;
		NewGadget . ng_LeftEdge		= COORDS_WIDTH - 10 - NewGadget . ng_Width;

		GadgetArray[Counter] = Gadget = CreateGadget(BUTTON_KIND,Gadget,&NewGadget,
			TAG_DONE);
	}

	return(Gadget);
}

STATIC STRPTR __saveds
ShowOffset(struct Gadget *SomeGadget,WORD Level)
{
	STATIC UBYTE LevelBuffer[10];

	UWORD Mid = ((1 << Depth) - 3) / 2;

	if(Level == Mid)
		strcpy(LevelBuffer,"(   0)");
	else
	{
		if(Level < Mid)
			sprintf(LevelBuffer,"(-%3d)",Mid - Level);
		else
			sprintf(LevelBuffer,"(+%3d)",Level - Mid);
	}

	return(LevelBuffer);
}


	/* CreateAllPaletteGadgets():
	 *
	 *	Creates all gadgets required by the palette control panel.
	 */

struct Gadget *
CreateAllPaletteGadgets(struct Gadget **GadgetArray,struct Gadget **GadgetList,APTR VisualInfo,UWORD TopEdge)
{
	struct Gadget		*Gadget;
	struct NewGadget	 NewGadget;
	UWORD			 Counter = 0;

	if(Gadget = CreateContext(GadgetList))
	{
		NewGadget . ng_Width		= 134;
		NewGadget . ng_Height		= 12;
		NewGadget . ng_GadgetText	= "Red        ";
		NewGadget . ng_TextAttr		= &DefaultFont;
		NewGadget . ng_VisualInfo	= VisualInfo;
		NewGadget . ng_GadgetID		= Counter;
		NewGadget . ng_Flags		= PLACETEXT_LEFT;
		NewGadget . ng_LeftEdge		= 18 + 8 * strlen(NewGadget . ng_GadgetText);
		NewGadget . ng_TopEdge		= 1 + TopEdge;

		GadgetArray[Counter++] = Gadget = CreateGadget(SLIDER_KIND,Gadget,&NewGadget,
			GTSL_Min,		0,
			GTSL_Max,		(1 << Depth) - 3,
			GTSL_Level,		RedLevel,
			GTSL_DispFunc,		ShowOffset,
			GTSL_LevelFormat,	"%s",
			GTSL_MaxLevelLen,	6,
		TAG_DONE);

		NewGadget . ng_GadgetText	= "Green      ";
		NewGadget . ng_GadgetID		= Counter;
		NewGadget . ng_TopEdge		= NewGadget . ng_TopEdge + NewGadget . ng_Height + 1;

		GadgetArray[Counter++] = Gadget = CreateGadget(SLIDER_KIND,Gadget,&NewGadget,
			GTSL_Min,		0,
			GTSL_Max,		(1 << Depth) - 3,
			GTSL_Level,		GreenLevel,
			GTSL_DispFunc,		ShowOffset,
			GTSL_LevelFormat,	"%s",
			GTSL_MaxLevelLen,	6,
		TAG_DONE);

		NewGadget . ng_GadgetText	= "Blue       ";
		NewGadget . ng_GadgetID		= Counter;
		NewGadget . ng_TopEdge		= NewGadget . ng_TopEdge + NewGadget . ng_Height + 1;

		GadgetArray[Counter] = Gadget = CreateGadget(SLIDER_KIND,Gadget,&NewGadget,
			GTSL_Min,		0,
			GTSL_Max,		(1 << Depth) - 3,
			GTSL_Level,		BlueLevel,
			GTSL_DispFunc,		ShowOffset,
			GTSL_LevelFormat,	"%s",
			GTSL_MaxLevelLen,	6,
		TAG_DONE);
	}

	return(Gadget);
}

	/* CreateAllScrollGadgets():
	 *
	 *	Creates all the gadgets required by the scroll amount
	 *	control panel.
	 */

struct Gadget *
CreateAllScrollGadgets(struct Gadget **GadgetArray,struct Gadget **GadgetList,APTR VisualInfo,UWORD TopEdge)
{
	struct Gadget		*Gadget;
	struct NewGadget	 NewGadget;
	UWORD			 Counter = 0;

	if(Gadget = CreateContext(GadgetList))
	{
		NewGadget . ng_Width		= 44;
		NewGadget . ng_Height		= 14;
		NewGadget . ng_GadgetText	= "Scroll amount";
		NewGadget . ng_TextAttr		= &DefaultFont;
		NewGadget . ng_VisualInfo	= VisualInfo;
		NewGadget . ng_GadgetID		= Counter;
		NewGadget . ng_Flags		= 0;
		NewGadget . ng_LeftEdge		= (strlen(NewGadget . ng_GadgetText) + 2) * 8 + 1;
		NewGadget . ng_TopEdge		= 1 + TopEdge;

		GadgetArray[Counter++] = Gadget = CreateGadget(INTEGER_KIND,Gadget,&NewGadget,
			GTIN_MaxChars,	3,
			GTIN_Number,	(LONG)MaxScroll,
		TAG_DONE);

		NewGadget . ng_Width		= 52;
		NewGadget . ng_Height		= 12;
		NewGadget . ng_GadgetText	= "Accept";
		NewGadget . ng_GadgetID		= Counter;
		NewGadget . ng_Flags		= 0;
		NewGadget . ng_LeftEdge		= 10;
		NewGadget . ng_TopEdge		= SCROLL_HEIGHT - 3 - NewGadget . ng_Height;

		GadgetArray[Counter++] = Gadget = CreateGadget(BUTTON_KIND,Gadget,&NewGadget,
			TAG_DONE);

		NewGadget . ng_GadgetText	= "Cancel";
		NewGadget . ng_GadgetID		= Counter;
		NewGadget . ng_LeftEdge		= SCROLL_WIDTH - 10 - NewGadget . ng_Width;

		GadgetArray[Counter] = Gadget = CreateGadget(BUTTON_KIND,Gadget,&NewGadget,
			TAG_DONE);
	}

	return(Gadget);
}

	/* InfoText():
	 *
	 *	Renders a small position and size information text into
	 *	the left bottom corner of the screen.
	 */

VOID
InfoText()
{
	struct TextFont	*OldFont;
	UBYTE		 Buffer[80];
	WORD		 i;
	struct RastPort	*RPort = Window -> RPort;

	BlockWindow();

	OldFont = RPort -> Font;

	SetFont(RPort,&DigitFont);
	SetAPen(RPort,CurrentPen = 1);

	sprintf(Buffer,"(/%1.04e )/%1.04e %1.04e*%1.04e",MinReal,MinImaginary,RealWidth,ImaginaryHeight);

	for(i = 0 ; i < strlen(Buffer) ; i++)
	{
		if(Buffer[i] == 'e')
			Buffer[i] = '$';
	}

	SetDrMd(RPort,JAM1);

	Move(RPort,1,Window -> Height - 2);
	Text(RPort,Buffer,strlen(Buffer));

	SetFont(RPort,OldFont);
	SetDrMd(RPort,JAM2);
}

	/* CalculateFrames():
	 *
	 *	Precalculates the number of pictures to be generated
	 *	when zooming to a specific location in the Mandelbrot
	 *	set.
	 */

WORD
CalculateFrames(double MinReal,double MinImaginary,double Size)
{
	double	Data[3],
		Div,
		Value,
		FourPixels,
		GlobalSize,
		GlobalMinReal,
		GlobalMinImaginary;
	BYTE	Render,
		i;
	WORD	Count = 0;

	GlobalSize		= LastSize;
	GlobalMinReal		= LastMinReal;
	GlobalMinImaginary	= LastMinImaginary;

	do
	{
		Data[0]	= GlobalSize - Size;
		Data[1]	= MinReal - GlobalMinReal;
		Data[2] = MinImaginary - GlobalMinImaginary;

		Value	= fabs(Data[0]);
		Render	= FALSE;

		for(i = 0 ; i < 3 ; i++)
		{
			if(Data[i] != 0.0)
				Render = TRUE;

			if(fabs(Data[i]) > Value)
				Value = fabs(Data[i]);
		}

		if(Render)
		{
			FourPixels	= MaxScroll * (GlobalSize / (double)Screen -> Width);
			Div		= Value;

			if(Value > FourPixels)
				Value = FourPixels;

			Div /= Value;

			GlobalSize		-= Data[0] / Div;
			GlobalMinReal		+= Data[1] / Div;
			GlobalMinImaginary	+= Data[2] / Div;

			Count++;
		}
	}
	while(Render);

	return(Count);
}

	/* CheckAbort():
	 *
	 *	Provides a safe callback routine to allow the anim
	 *	player to check for a user abort.
	 */

BYTE
CheckAbort()
{
	if(CheckSignal(SIG_WINDOW))
	{
		struct IntuiMessage	*Massage;
		ULONG			 Class,Code;

		while(Massage = (struct IntuiMessage *)GT_GetIMsg(Window -> UserPort))
		{
			Class	= Massage -> Class;
			Code	= Massage -> Code;

			GT_ReplyIMsg(Massage);

			if((Class == IDCMP_MOUSEBUTTONS && !(Code & IECODE_UP_PREFIX)) || Class == IDCMP_VANILLAKEY)
				return(TRUE);
		}
	}
	else
		return(FALSE);
}

	/* ReplayAnim():
	 *
	 *	Loads and replays an animation file.
	 */

BYTE
ReplayAnim()
{
	BYTE	 Success = FALSE;
	UBYTE	*DummyChar;

	strcpy(DummyBuffer,AnimPlayName);

	DummyChar = PathPart(DummyBuffer);

	*DummyChar = 0;

	if(GetFile("Replay animation",DummyBuffer,FilePart(AnimPlayName),DummyBuffer,"~(#?.info)",FALSE))
	{
		struct MinList *List;

		if(List = LoadAnim(DummyBuffer))
		{
			LONG Error = ValidateAnim(List,Screen);

			if(!Error)
			{
				strcpy(AnimPlayName,DummyBuffer);

				SetClear(Window);

				if(!PlayAnim(List,Screen,CheckAbort))
					MyEasyRequest("Continue","Not enough memory for\ndisplay buffer.");
				else
					Success = TRUE;

				SetWait(Window);
			}
			else
				MyEasyRequest("Continue","Error validating animation\n(%s)",AnimErrors[Error]);

			DeleteAnim(List);
		}
		else
			MyEasyRequest("Continue","Could not read animation file\n%s.",FilePart(DummyBuffer));
	}

	return(Success);
}

	/* RunAnim():
	 *
	 *	This routine calculates the single animation frames and
	 *	stores them in the anim file.
	 */

VOID
RunAnim()
{
	double	Data[3],
		Value,
		Div,
		SomePixels;
	BYTE	Render,
		i;

	LastSize		= RealWidth;
	LastMinReal		= MinReal;
	LastMinImaginary	= MinImaginary;

	NewCoords = FALSE;

/*	Coords();*/

	while(AnimRunning && !NewCoords)
		Zoom();

	if(NewCoords)
	{
		if(!AnimInfo)
		{
			UBYTE *DummyChar;

			AnimRunning = FALSE;

			strcpy(DummyBuffer,AnimSaveName);

			DummyChar = PathPart(DummyBuffer);

			*DummyChar = 0;

			if(GetFile("Begin animation",DummyBuffer,FilePart(AnimSaveName),DummyBuffer,"~(#?.info)",TRUE))
			{
				if(Frame)
				{
					WORD	X,Y,
						Width;
					double	Pixel;

					Pixel	= LastSize / (double)Screen -> Width;

					X	= (WORD)(fabs(LastMinReal - MainMinReal) / Pixel);
					Y	= (WORD)(fabs(LastMinImaginary - MainMinImaginary) / Pixel);
					Width	= (WORD)(MainRealWidth / Pixel);

					if(X || Y || Width != Screen -> Width)
					{
						SetAPen(Window -> RPort,CurrentPen = 1);
						DrawSquare(Window -> RPort,X,Y,Width);
					}
				}

				if(Include)
					InfoText();

				if(AnimInfo = OpenAnim(DummyBuffer,Screen))
				{
					if(!ThisProcess -> pr_CLI)
						PutDiskObject(DummyBuffer,&AnimIcon);

					OffMenu(Window,FULLMENUNUM(0, 0,NOSUB));
					OffMenu(Window,FULLMENUNUM(0, 1,NOSUB));
					OffMenu(Window,FULLMENUNUM(0, 2,NOSUB));
					OffMenu(Window,FULLMENUNUM(0, 6,NOSUB));
					OffMenu(Window,FULLMENUNUM(0, 8,NOSUB));
					OnMenu (Window,FULLMENUNUM(0, 9,NOSUB));
					OffMenu(Window,FULLMENUNUM(0,11,NOSUB));
					OffMenu(Window,FULLMENUNUM(0,12,NOSUB));
					OffMenu(Window,FULLMENUNUM(0,14,NOSUB));

					OffMenu(Window,FULLMENUNUM(1, 0,NOSUB));
					OffMenu(Window,FULLMENUNUM(1, 1,NOSUB));
					OffMenu(Window,FULLMENUNUM(1, 2,NOSUB));
					OffMenu(Window,FULLMENUNUM(1, 5,NOSUB));
					OffMenu(Window,FULLMENUNUM(1, 7,NOSUB));
					OffMenu(Window,FULLMENUNUM(1, 9,NOSUB));

					AnimRunning = TRUE;

					strcpy(AnimSaveName,DummyBuffer);
				}
			}
		}

		if(AnimRunning)
		{
			RealWidth		= LastSize;
			ImaginaryHeight		= LastSize;

			MinReal			= LastMinReal;
			MinImaginary		= LastMinImaginary;

			LastSize		= MainRealWidth;
			LastMinReal		= MainMinReal;
			LastMinImaginary	= MainMinImaginary;

			do
			{
				Data[0]	= RealWidth - LastSize;
				Data[1]	= LastMinReal - MinReal;
				Data[2] = LastMinImaginary - MinImaginary;

				Value	= fabs(Data[0]);
				Render	= FALSE;

				for(i = 0 ; i < 3 ; i++)
				{
					if(Data[i] != 0.0)
						Render = TRUE;

					if(fabs(Data[i]) > Value)
						Value = fabs(Data[i]);
				}

				if(Render)
				{
					SomePixels	= MaxScroll * (RealWidth / (double)Screen -> Width);
					Div		= Value;

					if(Value > SomePixels)
						Value = SomePixels;

					Div /= Value;

					RealWidth	-= Data[0] / Div;
					ImaginaryHeight	-= Data[0] / Div;
					MinReal		+= Data[1] / Div;
					MinImaginary	+= Data[2] / Div;

					RealStep	= RealWidth / (double)Window -> Width;
					ImaginaryStep	= ImaginaryHeight / (double)Window -> Height;

					AreaActive	= FALSE;
					Running		= TRUE;

					FullPicture	= TRUE;

					SetRast(Window -> RPort,0);

					Mandelbrot(MinReal,MinImaginary,RealStep,ImaginaryStep,0,0,Window -> Width >> 1,Window -> Height >> 1);
					Mandelbrot(MinReal,MinImaginary,RealStep,ImaginaryStep,Window -> Width >> 1,0,Window -> Width >> 1,Window -> Height >> 1);

					Mandelbrot(MinReal,MinImaginary,RealStep,ImaginaryStep,0,Window -> Height >> 1,Window -> Width >> 1,Window -> Height >> 1);
					Mandelbrot(MinReal,MinImaginary,RealStep,ImaginaryStep,Window -> Width >> 1,Window -> Height >> 1,Window -> Width >> 1,Window -> Height >> 1);

					if(FullPicture)
					{
						BlockWindow();

						if(Frame)
						{
							WORD	X,Y,Width;
							double	Pixel;

							Pixel	= RealWidth / (double)Screen -> Width;

							X	= (WORD)(fabs(MinReal - LastMinReal) / Pixel);
							Y	= (WORD)(fabs(MinImaginary - LastMinImaginary) / Pixel);
							Width	= (WORD)(LastSize / Pixel);

							if(X || Y || Width != Screen -> Width)
							{
								SetAPen(Window -> RPort,CurrentPen = 1);
								DrawSquare(Window -> RPort,X,Y,Width);
							}
						}

						if(Include)
							InfoText();

						AddAnim(AnimInfo);

						ReleaseWindow();
					}

					if(!AnimRunning)
						break;
				}
			}
			while(Render);

			if(!Render)
			{
				MoveScreen(Screen,0,-Screen -> TopEdge);

				ScreenToFront(Screen);

				ActivateWindow(Window);

				Delay(10);

				DisplayBeep(Screen);
			}
		}

		AreaActive	= FALSE;
		Running		= FALSE;
	}
	else
		StopAnim();
}

	/* StopAnim():
	 *
	 *	Stops the animation generation.
	 */

VOID
StopAnim()
{
	if(AnimRunning)
	{
		AnimRunning = FALSE;

		OnMenu (Window,FULLMENUNUM(0, 0,NOSUB));
		OnMenu (Window,FULLMENUNUM(0, 1,NOSUB));
		OnMenu (Window,FULLMENUNUM(0, 2,NOSUB));
		OnMenu (Window,FULLMENUNUM(0,14,NOSUB));

		if(UseDouble)
		{
			OnMenu (Window,FULLMENUNUM(0, 6,NOSUB));
			OnMenu (Window,FULLMENUNUM(0, 8,NOSUB));
			OffMenu(Window,FULLMENUNUM(0, 9,NOSUB));
			OnMenu (Window,FULLMENUNUM(0,11,NOSUB));
			OnMenu (Window,FULLMENUNUM(0,12,NOSUB));
		}
		else
		{
			OffMenu(Window,FULLMENUNUM(0, 6,NOSUB));
			OffMenu(Window,FULLMENUNUM(0, 8,NOSUB));
			OffMenu(Window,FULLMENUNUM(0, 9,NOSUB));
			OffMenu(Window,FULLMENUNUM(0,10,NOSUB));
			OffMenu(Window,FULLMENUNUM(0,11,NOSUB));
			OffMenu(Window,FULLMENUNUM(0,12,NOSUB));
		}

		OnMenu (Window,FULLMENUNUM(1, 0,NOSUB));
		OnMenu (Window,FULLMENUNUM(1, 1,NOSUB));
		OnMenu (Window,FULLMENUNUM(1, 2,NOSUB));
		OnMenu (Window,FULLMENUNUM(1, 5,NOSUB));
		OnMenu (Window,FULLMENUNUM(1, 7,NOSUB));
		OnMenu (Window,FULLMENUNUM(1, 9,NOSUB));

		if(AnimInfo)
		{
			CloseAnim(AnimInfo,TRUE);

			AnimInfo = NULL;
		}
	}
}

	/* Coords():
	 *
	 *	This routine implements the coordinates control panel.
	 */

VOID
Coords()
{
	struct Gadget	*GadgetList = NULL;
	struct Gadget	*GadgetArray[5];
	struct Window	*CoordsWindow;

	double		 TempMinReal,
			 TempMinImaginary,
			 TempRealWidth,
			 TempImaginaryHeight;

	BYTE		 MadeChanges = FALSE;

	TempMinReal		= MinReal;
	TempMinImaginary	= MinImaginary;
	TempRealWidth		= RealWidth;
	TempImaginaryHeight	= ImaginaryHeight;

	if(CreateAllCoordsGadgets(&GadgetArray[0],&GadgetList,VisualInfo,Screen -> WBorTop + Screen -> Font -> ta_YSize + 1))
	{
		if(CoordsWindow = OpenWindowTags(NULL,
			WA_Width,	COORDS_WIDTH,
			WA_Height,	COORDS_HEIGHT,
			WA_Top,		(Screen -> Height - COORDS_HEIGHT) >> 1,
			WA_Left,	(Screen -> Width - COORDS_WIDTH) >> 1,
			WA_Activate,	TRUE,
			WA_CloseGadget,	TRUE,
			WA_RMBTrap,	TRUE,
			WA_DragBar,	TRUE,
			WA_IDCMP,	IDCMP_CLOSEWINDOW | BUTTONIDCMP | STRINGIDCMP,
			WA_Title,	"Coordinates",
			WA_CustomScreen,Screen,
		TAG_DONE))
		{
			struct IntuiMessage	*Massage;
			ULONG			 Class;
			struct Gadget		*Gadget;
			BYTE			 Terminated = FALSE;

			AddGList(CoordsWindow,GadgetList,(UWORD)-1,(UWORD)-1,NULL);
			RefreshGList(GadgetList,CoordsWindow,NULL,(UWORD)-1);
			GT_RefreshWindow(CoordsWindow,NULL);

			ActivateGadget(GadgetArray[0],CoordsWindow,NULL);

			while(!Terminated)
			{
				WaitPort(CoordsWindow -> UserPort);

				while(!Terminated && (Massage = (struct IntuiMessage *)GT_GetIMsg(CoordsWindow -> UserPort)))
				{
					Class	= Massage -> Class;
					Gadget	= (struct Gadget *)Massage -> IAddress;

					GT_ReplyIMsg(Massage);

					if(Class == IDCMP_CLOSEWINDOW)
						Terminated = TRUE;

					if(Class == IDCMP_GADGETUP)
					{
						double Value;

						switch(Gadget -> GadgetID)
						{
							case 3:	if(MadeChanges)
								{
									MinReal		= TempMinReal;
									MinImaginary	= TempMinImaginary;
									RealWidth	= TempRealWidth;
									ImaginaryHeight	= TempImaginaryHeight;

									RealStep	= RealWidth / (double)Window -> Width;
									ImaginaryStep	= ImaginaryHeight / (double)Window -> Height;

									MainMinReal		= MinReal;
									MainMinImaginary	= MinImaginary;
									MainRealWidth		= RealWidth;
									MainImaginaryHeight	= ImaginaryHeight;

									NewCoords	= TRUE;
									Running		= FALSE;
									AreaActive	= FALSE;
								}

								Terminated = TRUE;
								break;

							case 4:	Terminated = TRUE;
								break;

							default:Value = atof(((struct StringInfo *)Gadget -> SpecialInfo) -> Buffer);

								MadeChanges = TRUE;

								switch(Gadget -> GadgetID)
								{
									case 0:	TempMinReal = Value;
										ActivateGadget(GadgetArray[1],CoordsWindow,NULL);
										break;

									case 1:	TempMinImaginary = Value;
										ActivateGadget(GadgetArray[2],CoordsWindow,NULL);
										break;

									case 2:	TempRealWidth		= Value;
										TempImaginaryHeight	= Value;

										ActivateGadget(GadgetArray[0],CoordsWindow,NULL);
										break;
								}

								break;
						}
					}
				}
			}

			CloseWindow(CoordsWindow);
		}
	}

	FreeGadgets(GadgetList);
}

	/* AdjustPalette():
	 *
	 *	This routine implements the palette control panel.
	 */

VOID
AdjustPalette()
{
	struct Gadget	*GadgetList = NULL;
	struct Gadget	*GadgetArray[3];
	struct Window	*Window;

	if(CreateAllPaletteGadgets(&GadgetArray[0],&GadgetList,VisualInfo,Screen -> WBorTop + Screen -> Font -> ta_YSize + 1))
	{
		if(Window = OpenWindowTags(NULL,
			WA_Width,	PALETTE_WIDTH,
			WA_Height,	PALETTE_HEIGHT,

			WA_Top,		(Screen -> Height - PALETTE_HEIGHT) >> 1,
			WA_Left,	(Screen -> Width - PALETTE_WIDTH) >> 1,

			WA_Activate,	TRUE,
			WA_DragBar,	TRUE,
			WA_CloseGadget, TRUE,
			WA_RMBTrap,	TRUE,

			WA_IDCMP,	IDCMP_CLOSEWINDOW | SLIDERIDCMP,
			WA_CustomScreen,Screen,

			WA_Title,	"Palette",
		TAG_DONE))
		{
			struct IntuiMessage	*Massage;
			ULONG			 Class,Code;
			struct Gadget		*Gadget;
			BYTE			 Terminated = FALSE;
			WORD			 i;

			AddGList(Window,GadgetList,(UWORD)-1,(UWORD)-1,NULL);
			RefreshGList(GadgetList,Window,NULL,(UWORD)-1);
			GT_RefreshWindow(Window,NULL);

			if(DoCycle)
			{
				DoCycle = FALSE;

				LoadPalette(Screen,Palette,0);
			}
			else
				DoCycle = FALSE;

			while(!Terminated)
			{
				WaitPort(Window -> UserPort);

				while(!Terminated && (Massage = (struct IntuiMessage *)GT_GetIMsg(Window -> UserPort)))
				{
					Class	= Massage -> Class;
					Code	= Massage -> Code;
					Gadget	= (struct Gadget *)Massage -> IAddress;

					GT_ReplyIMsg(Massage);

					if(Class == IDCMP_CLOSEWINDOW)
						Terminated = TRUE;

					if(Class == IDCMP_MOUSEMOVE)
					{
						WORD Wrap = (1 << Depth) - 2;

						switch(Gadget -> GadgetID)
						{
							case 0:	RedLevel = Code;
								break;

							case 1:	GreenLevel = Code;
								break;

							case 2:	BlueLevel = Code;
								break;
						}

							/* Combine all three colour ranges into a single palette. */

						for(i = 0 ; i < Wrap ; i++)
							SetPaletteTriplet(Palette,Range[(RedLevel + i) % Wrap],Range[(GreenLevel + i) % Wrap],Range[(BlueLevel + i) % Wrap],2 + i);

						LoadPalette(Screen,Palette,0);
					}
				}
			}

			CloseWindow(Window);
		}
	}

	FreeGadgets(GadgetList);
}

	/* MaxScrollPanel():
	 *
	 *	This routine implements the scroll amount control panel.
	 */

VOID
MaxScrollPanel()
{
	struct Gadget	*GadgetList = NULL;
	struct Gadget	*GadgetArray[3];
	struct Window	*ScrollWindow;

	if(CreateAllScrollGadgets(&GadgetArray[0],&GadgetList,VisualInfo,Screen -> WBorTop + Screen -> Font -> ta_YSize + 1))
	{
		if(ScrollWindow = OpenWindowTags(NULL,
			WA_Width,	SCROLL_WIDTH,
			WA_Height,	SCROLL_HEIGHT,

			WA_Top,		(Screen -> Height - SCROLL_HEIGHT) >> 1,
			WA_Left,	(Screen -> Width - SCROLL_WIDTH) >> 1,

			WA_Activate,	TRUE,
			WA_DragBar,	TRUE,
			WA_DepthGadget,	TRUE,
			WA_CloseGadget,	TRUE,
			WA_RMBTrap,	TRUE,
			WA_CustomScreen,Screen,

			WA_IDCMP,	IDCMP_CLOSEWINDOW | IDCMP_ACTIVEWINDOW | BUTTONIDCMP | INTEGERIDCMP,

			WA_Title,	"Set scroll amount",
		TAG_DONE))
		{
			struct IntuiMessage	*Massage;
			ULONG			 Class;
			struct Gadget		*Gadget;
			BYTE			 Terminated = FALSE;

			AddGList(ScrollWindow,GadgetList,(UWORD)-1,(UWORD)-1,NULL);
			RefreshGList(GadgetList,ScrollWindow,NULL,(UWORD)-1);
			GT_RefreshWindow(ScrollWindow,NULL);

			ActivateGadget(GadgetArray[0],ScrollWindow,NULL);

			while(!Terminated)
			{
				WaitPort(ScrollWindow -> UserPort);

				while(!Terminated && (Massage = (struct IntuiMessage *)GT_GetIMsg(ScrollWindow -> UserPort)))
				{
					Class	= Massage -> Class;
					Gadget	= (struct Gadget *)Massage -> IAddress;

					GT_ReplyIMsg(Massage);

					if(Class == IDCMP_CLOSEWINDOW)
						Terminated = TRUE;

					if(Class == IDCMP_ACTIVEWINDOW)
						ActivateGadget(GadgetArray[0],ScrollWindow,NULL);

					if(Class == IDCMP_GADGETUP)
					{
						if(Gadget -> GadgetID == 2)
							Terminated = TRUE;
						else
						{
							LONG Value = ((struct StringInfo *)GadgetArray[0] -> SpecialInfo) -> LongInt;
							BYTE GoodValue = TRUE;

							if(Value < 1)
							{
								GoodValue	= FALSE;
								Value		= 1;
							}

							if(Value >= Screen -> Width)
							{
								GoodValue	= FALSE;
								Value		= Screen -> Width - 1;
							}

							if(GoodValue)
							{
								MaxScroll	= (double)Value;
								Terminated	= TRUE;
							}
							else
							{
								GT_SetGadgetAttrs(GadgetArray[0],ScrollWindow,NULL,
									GTIN_Number,Value,
								TAG_DONE);

								DisplayBeep(Screen);

								ActivateGadget(GadgetArray[0],ScrollWindow,NULL);
							}
						}
					}
				}
			}

			CloseWindow(ScrollWindow);
		}
	}

	FreeGadgets(GadgetList);
}

	/* AreaZoom():
	 *
	 *	This routine handles the selection of an area on screen
	 *	to recalculate later.
	 */

VOID
AreaZoom()
{
	struct IntuiMessage	*Massage;
	ULONG			 Class,Code;
	UWORD			 X,Y,
				 LastX,LastY;

	SetClear(Window);

	Code = NULL;

	Window -> Flags |= WFLG_RMBTRAP;

	ReportMouse(TRUE,Window);

	SetAPen(RPort,CurrentPen = (1 << Depth) - 1);
	SetDrMd(RPort,COMPLEMENT);

	LastX	= Window -> MouseX;
	LastY	= Window -> MouseY;

	DrawLine(RPort,LastX,0,LastX,Screen -> Height - 1);
	DrawLine(RPort,0,LastY,Screen -> Width - 1,LastY);

	do
	{
		WaitPort(Window -> UserPort);

		if(Massage = (struct IntuiMessage *)GetMsg(Window -> UserPort))
		{
			Class	= Massage -> Class;
			Code	= Massage -> Code;

			X	= Massage -> MouseX;
			Y	= Massage -> MouseY;

			ReplyMsg(&Massage -> ExecMessage);

			if(Class == IDCMP_MOUSEMOVE)
			{
				if(LastX != X)
				{
					DrawLine(RPort,LastX,0,LastX,Screen -> Height - 1);
					DrawLine(RPort,X,0,X,Screen -> Height - 1);
				}

				if(LastY != Y)
				{
					DrawLine(RPort,0,LastY,Screen -> Width - 1,LastY);
					DrawLine(RPort,0,Y,Screen -> Width - 1,Y);
				}

				LastX = X;
				LastY = Y;
			}

			if(Class == IDCMP_MOUSEBUTTONS)
			{
				if(Code == SELECTDOWN)
				{
					UWORD	Left,Top,Width = 0,Height = 0;
					BYTE	DidBox = FALSE;

					Left	= X;
					Top	= Y;

					DrawLine(RPort,LastX,0,LastX,Screen -> Height - 1);
					DrawLine(RPort,0,LastY,Screen -> Width - 1,LastY);

					while(Code != SELECTUP)
					{
						WaitPort(Window -> UserPort);

						while(Massage = (struct IntuiMessage *)GetMsg(Window -> UserPort))
						{
							Class	= Massage -> Class;
							Code	= Massage -> Code;

							Y	= Massage -> MouseY;
							X	= Massage -> MouseX;

							ReplyMsg(&Massage -> ExecMessage);

							if(Class == IDCMP_MOUSEMOVE)
							{
								if(Y > Top && X > Left)
								{
									if(DidBox)
									{
										Move(RPort,Left,Top);
										Draw(RPort,Left + Width - 1,Top);
										Draw(RPort,Left + Width - 1,Top + Height - 1);
										Draw(RPort,Left,Top + Height - 1);
										Draw(RPort,Left,Top + 1);
									}

									Width	= X - Left + 1;
									Height	= Y - Top + 1;

									Move(RPort,Left,Top);
									Draw(RPort,Left + Width - 1,Top);
									Draw(RPort,Left + Width - 1,Top + Height - 1);
									Draw(RPort,Left,Top + Height - 1);
									Draw(RPort,Left,Top + 1);

									DidBox = TRUE;
								}

								Code = NULL;
							}
						}
					}

					if(DidBox)
					{
						Move(RPort,Left,Top);
						Draw(RPort,Left + Width - 1,Top);
						Draw(RPort,Left + Width - 1,Top + Height - 1);
						Draw(RPort,Left,Top + Height - 1);
						Draw(RPort,Left,Top + 1);
					}

					SetDrMd(RPort,JAM1);

					if(Width >= 2 && Height >= 2)
					{
						AreaLeft	= Left;
						AreaTop		= Top;

						AreaWidth	= Width;
						AreaHeight	= Height;

						NewCoords	= TRUE;
						Running		= FALSE;
						AreaActive	= TRUE;
					}
				}
			}
		}
	}
	while(Code != SELECTUP);

	ReportMouse(FALSE,Window);

	Window -> Flags &= ~WFLG_RMBTRAP;

	ClearPointer(Window);
}

	/* Zoom():
	 *
	 *	This routine handles the selection of an area on screen
	 *	to zoom to later. Note that the area will be square.
	 */

VOID
Zoom()
{
	struct IntuiMessage	*Massage;
	ULONG			 Class,Code;
	UWORD			 X,Y,
				 LastX,LastY;

	SetClear(Window);

	Code = NULL;

	Window -> Flags |= WFLG_RMBTRAP;

	ReportMouse(TRUE,Window);

	SetAPen(RPort,CurrentPen = (1 << Depth) - 1);
	SetDrMd(RPort,COMPLEMENT);

	LastX	= Window -> MouseX;
	LastY	= Window -> MouseY;

	DrawLine(RPort,LastX,0,LastX,Screen -> Height - 1);
	DrawLine(RPort,0,LastY,Screen -> Width - 1,LastY);

	while(Code != SELECTUP)
	{
		WaitPort(Window -> UserPort);

		if(Massage = (struct IntuiMessage *)GetMsg(Window -> UserPort))
		{
			Class	= Massage -> Class;
			Code	= Massage -> Code;

			X	= Massage -> MouseX;
			Y	= Massage -> MouseY;

			ReplyMsg(&Massage -> ExecMessage);

			if(Class == IDCMP_MOUSEMOVE)
			{
				if(LastX != X)
				{
					DrawLine(RPort,LastX,0,LastX,Screen -> Height - 1);
					DrawLine(RPort,X,0,X,Screen -> Height - 1);
				}

				if(LastY != Y)
				{
					DrawLine(RPort,0,LastY,Screen -> Width - 1,LastY);
					DrawLine(RPort,0,Y,Screen -> Width - 1,Y);
				}

				LastX = X;
				LastY = Y;
			}

			if(Class == IDCMP_MOUSEBUTTONS)
			{
				if(Code == SELECTDOWN)
				{
					UWORD	Left,Top,
						Width = 0,Height = 0;
					BYTE	DidBox = FALSE;

					DrawLine(RPort,LastX,0,LastX,Screen -> Height - 1);
					DrawLine(RPort,0,LastY,Screen -> Width - 1,LastY);

					Left	= X;
					Top	= Y;

					SetAPen(RPort,CurrentPen = (1 << Depth) - 1);
					SetDrMd(RPort,COMPLEMENT);

					while(Code != SELECTUP)
					{
						WaitPort(Window -> UserPort);

						while(Massage = (struct IntuiMessage *)GetMsg(Window -> UserPort))
						{
							Class	= Massage -> Class;
							Code	= Massage -> Code;

							Y	= Massage -> MouseY;

							ReplyMsg(&Massage -> ExecMessage);

							if(Class == IDCMP_MOUSEMOVE)
							{
								if(Y > Top)
								{
									if(DidBox)
									{
										Move(RPort,Left,Top);
										Draw(RPort,Left + Width - 1,Top);
										Draw(RPort,Left + Width - 1,Top + Height - 1);
										Draw(RPort,Left,Top + Height - 1);
										Draw(RPort,Left,Top + 1);
									}

									Width = Height = Y - Top + 1;

									Move(RPort,Left,Top);
									Draw(RPort,Left + Width - 1,Top);
									Draw(RPort,Left + Width - 1,Top + Height - 1);
									Draw(RPort,Left,Top + Height - 1);
									Draw(RPort,Left,Top + 1);

									DidBox = TRUE;
								}

								Code = NULL;
							}
						}
					}

					if(DidBox)
					{
						Move(RPort,Left,Top);
						Draw(RPort,Left + Width - 1,Top);
						Draw(RPort,Left + Width - 1,Top + Height - 1);
						Draw(RPort,Left,Top + Height - 1);
						Draw(RPort,Left,Top + 1);
					}

					SetDrMd(RPort,JAM1);

					if(Width >= 2 && Height >= 2)
					{
						BYTE Val;

						SetWait(Window);

						if(AnimRunning)
							Val = MyEasyRequest("Yes|Abort|No","Transition will consist\nof %ld images.\n\nDo you wish to use the\ncurrent coordinates?",CalculateFrames(MinReal + (double)Left * RealStep,MinImaginary + (double)Top * ImaginaryStep,(double)Width * RealStep));
						else
							Val = MyEasyRequest("Yes|No","Do you wish to use the\ncurrent coordinates?");

						switch(Val)
						{
							case 0:	break;

							case 1:	MinReal		= MinReal + (double)Left * RealStep;
								MinImaginary	= MinImaginary + (double)Top * ImaginaryStep;

								RealWidth	= (double)Width * RealStep;
								ImaginaryHeight	= (double)Height * ImaginaryStep;

								RealStep	= RealWidth / (double)Window -> Width;
								ImaginaryStep	= ImaginaryHeight / (double)Window -> Height;

								MainMinReal		= MinReal;
								MainMinImaginary	= MinImaginary;
								MainRealWidth		= RealWidth;
								MainImaginaryHeight	= ImaginaryHeight;

								NewCoords	= TRUE;
								Running		= FALSE;
								AreaActive	= FALSE;
								break;

							case 2:	Running = FALSE;
								StopAnim();
								break;
						}
					}
				}
			}
		}
	}

	ReportMouse(FALSE,Window);

	Window -> Flags &= ~WFLG_RMBTRAP;

	ClearPointer(Window);
}

	/* HandleEvent():
	 *
	 *	The global input event (menus, key, mouse, etc.) handler.
	 */

VOID
HandleEvent()
{
	struct IntuiMessage	*Massage;
	struct MenuItem		*Item;
	ULONG			 Class,
				 Code,
				 MenuItem,
				 Selected;
	UBYTE			*DummyChar;
	BYTE			 Waiting = FALSE;
	MandelHeader		 Mandel;

	do
	{
		while(Massage = (struct IntuiMessage *)GetMsg(Window -> UserPort))
		{
			Class	= Massage -> Class;
			Code	= Massage -> Code;

			ReplyMsg(&Massage -> ExecMessage);

			if(Class == IDCMP_MENUVERIFY)
				Waiting = HoldIt = TRUE;

			if(Class == IDCMP_VANILLAKEY)
			{
				if(Code == '\t')
				{
					if(!DoCycle)
					{
						Forward = TRUE;

						DoCycle = TRUE;
					}
					else
						DoCycle = FALSE;
				}
			}

			if(Class == IDCMP_RAWKEY)
			{
				if(Code == CURSORLEFT)
					CycleColours(TRUE);

				if(Code == CURSORRIGHT)
					CycleColours(FALSE);

				if(Code == CURSORUP)
				{
					Forward = TRUE;

					DoCycle = TRUE;
				}

				if(Code == CURSORDOWN)
				{
					Forward = FALSE;

					DoCycle = TRUE;
				}
			}

			if(Class == IDCMP_MENUPICK)
			{
				Waiting = HoldIt = FALSE;

				if(GotClip)
				{
					BltBitMap(BackupBitMap,0,0,RPort -> BitMap,(Square - 25 * 8) >> 1,(Square - 8) >> 1,8 * 25,8,0xC0,0xFF,NULL);

					GotClip = FALSE;
				}

				MenuItem = Code;

				while(MenuItem != MENUNULL)
				{
					if(Item = ItemAddress(Menu,MenuItem))
					{
						Selected = (ULONG)MENU_USERDATA(Item);

						switch(Selected)
						{
							case MEN_OPEN:	BlockWindow();

									strcpy(NameBuffer,LastName);

									DummyChar = PathPart(NameBuffer);

									*DummyChar = 0;

									if(GetFile("Open picture",NameBuffer,FilePart(LastName),NameBuffer,"~(#?.info)",FALSE))
									{
										struct PictureInfo *Info;

										if(Info = LoadPicture(NameBuffer))
										{
											if(ModeNotAvailable(Info -> ViewModes) || (Info -> BitMapHeader . nPlanes > 5 && !Is39))
												MyEasyRequest("Continue","Cannot display this image.");
											else
											{
												if(Info -> BitMapHeader . w == Screen -> Width && Info -> BitMapHeader . h == Screen -> Height && Info -> BitMapHeader . nPlanes == Depth)
												{
													LONG i,Size;
	
													DoCycle = FALSE;
	
													Size = GetPaletteSize(Palette);
	
													for(i = 0 ; i < Size ; i++)
														SetPaletteEntry(Palette,0,i);
	
													LoadPalette(Screen,Palette,0);
	
													BltBitMapRastPort(Info -> BitMap,0,0,RPort,0,0,Square,Square,0xC0);
	
													LoadPalette(Screen,Info -> Palette,0);
													GetPalette(Screen,Palette);
	
													if(Info -> MandelHeader . Iterations)
													{
														MainMinReal		= MinReal		= Info -> MandelHeader . MinReal;
														MainMinImaginary	= MinImaginary		= Info -> MandelHeader . MinImaginary;
														MainRealWidth		= RealWidth		= Info -> MandelHeader . RealWidth;
														MainImaginaryHeight	= ImaginaryHeight	= Info -> MandelHeader . ImaginaryHeight;
														MaxIteration					= Info -> MandelHeader . Iterations - 1;
	
														IterationSetup();
	
														RealStep	= RealWidth / (double)Window -> Width;
														ImaginaryStep	= ImaginaryHeight / (double)Window -> Height;
													}
													else
														MyEasyRequest("Continue","Could not read image\ncoordinates, previous values\nremain untouched.");
	
													strcpy(LastName,NameBuffer);
	
													Running = FALSE;
	
													AreaActive = FALSE;
												}
												else
												{
													if(Info -> BitMapHeader . w == Info -> BitMapHeader . h && Info -> MandelHeader . Iterations)
													{
														MainMinReal		= MinReal		= Info -> MandelHeader . MinReal;
														MainMinImaginary	= MinImaginary		= Info -> MandelHeader . MinImaginary;
														MainRealWidth		= RealWidth		= Info -> MandelHeader . RealWidth;
														MainImaginaryHeight	= ImaginaryHeight	= Info -> MandelHeader . ImaginaryHeight;
														MaxIteration					= Info -> MandelHeader . Iterations - 1;
	
														RealStep		= RealWidth / (double)Window -> Width;
														ImaginaryStep		= ImaginaryHeight / (double)Window -> Height;
	
														strcpy(LastName,NameBuffer);
	
														Running		= FALSE;
														AreaActive	= FALSE;
														NewMode		= TRUE;
														DoCycle		= FALSE;
	
														GlobalBitMap	= Info -> BitMap;
														GlobalSize	= Info -> BitMapHeader . w;
														GlobalDepth	= Info -> BitMapHeader . nPlanes;
														GlobalMode	= Info -> ViewModes;
														GlobalPalette	= Info -> Palette;
	
														Info -> BitMap	= NULL;
														Info -> Palette	= NULL;
													}
													else
														MyEasyRequest("Continue","Picture has wrong size!");
												}
											}

											FreePicture(Info);
										}
										else
											MyEasyRequest("Continue","Error reading file\n%s.",FilePart(NameBuffer));
									}

									ReleaseWindow();

									break;

							case MEN_SAVE:	BlockWindow();

									if(NameBuffer[0])
										goto SaveIt;

							case MEN_SAVEAS:BlockWindow();

									strcpy(NameBuffer,LastName);

									DummyChar = PathPart(NameBuffer);

									*DummyChar = 0;

									if(GetFile("Save picture as",NameBuffer,FilePart(LastName),NameBuffer,"~(#?.info)",TRUE))
									{
SaveIt:										Mandel . MinReal		= MinReal;
										Mandel . MinImaginary		= MinImaginary;
										Mandel . RealWidth		= RealWidth;
										Mandel . ImaginaryHeight	= ImaginaryHeight;
										Mandel . Iterations		= MaxIteration + 1;

										if(!SavePicture(NameBuffer,Screen,&Mandel))
											MyEasyRequest("Continue","Could not save file\n%s.",FilePart(NameBuffer));
										else
										{
											if(!ThisProcess -> pr_CLI)
												PutDiskObject(NameBuffer,&PictureIcon);

											strcpy(LastName,NameBuffer);
										}
									}

									ReleaseWindow();
									break;

							case MEN_PRI0:	SetTaskPri(ThisProcess,-5);
									break;

							case MEN_PRI1:	SetTaskPri(ThisProcess,0);
									break;

							case MEN_PRI2:	SetTaskPri(ThisProcess,5);
									break;

							case MEN_PLAY:	BlockWindow();

									ReplayAnim();

									ReleaseWindow();

									break;

							case MEN_START:	if(!AnimRunning)
									{
										Running		= FALSE;
										FullPicture	= FALSE;
										AnimRunning	= TRUE;
									}

									break;

							case MEN_STOP:	BlockWindow();

									StopAnim();

									Running = FALSE;

									FullPicture = FALSE;

									ReleaseWindow();

									break;

							case MEN_SCROLL:BlockWindow();

									MaxScrollPanel();

									ReleaseWindow();

									break;

							case MEN_FRAME:	if(Item -> Flags & CHECKED)
										Frame = TRUE;
									else
										Frame = FALSE;

									break;

							case MEN_INCLUDE:
									if(Item -> Flags & CHECKED)
										Include = TRUE;
									else
										Include = FALSE;

									break;

							case MEN_RESOLUTION:
									if(AslBase -> lib_Version >= 38)
									{
										ULONG DisplayMode = GetVPModeID(&Screen -> ViewPort);

										BlockWindow();

										if(SelectDisplayMode(Window,&DisplayMode))
										{
											if(DisplayMode != GetVPModeID(&Screen -> ViewPort))
											{
												Mode	= DisplayMode;
												NewMode = TRUE;
												Running = FALSE;
												DoCycle = FALSE;
											}
										}

										ReleaseWindow();
									}
									else
									{
										NewMode = TRUE;
										Running = FALSE;
										DoCycle = FALSE;

										if(Mode == LORES_KEY)
										{
											Mode	= HIRESLACE_KEY;
											Depth	= 4;
										}
										else
										{
											Mode	= LORES_KEY;
											Depth	= 5;
										}
									}

									if(NewMode)
									{
										MainMinReal		= MinReal;
										MainMinImaginary	= MinImaginary;
										MainRealWidth		= RealWidth;
										MainImaginaryHeight	= ImaginaryHeight;
									}

									break;

							case MEN_ABOUT:	BlockWindow();

									MyEasyRequest("So what?","         Mandel²\n\nYet another Mandelbrot set\n   exploration program\n\n        Written by\n   Olaf `Olsen' Barthel\n\n  © Copyright 1991-1992\n");

									ReleaseWindow();
									break;

							case MEN_QUIT:	BlockWindow();

									if(MyEasyRequest("Yes|No","Do you really wish to\nleave this program?"))
										CloseAll(RETURN_OK);

									ReleaseWindow();
									break;

							case MEN_ZOOM:	Zoom();
									break;

							case MEN_CLIP:	AreaZoom();
									Faster = TRUE;
									break;

							case MEN_RERUN:	AreaZoom();
									Faster = FALSE;
									break;

							case MEN_PRECISE:
									if(Item -> Flags & CHECKED)
										Precise = TRUE;
									else
										Precise = FALSE;

									break;

							case MEN_COORDS:BlockWindow();
									Coords();
									ReleaseWindow();
									break;

							case MEN_PALETTE:
									BlockWindow();
									AdjustPalette();
									ReleaseWindow();
									break;

							case MEN_RUN:	Running ^= TRUE;

									if(!Running)
										FullPicture = FALSE;

									break;

							default:	if(Selected >= 1 && Selected <= 16)
										MaxIteration = (1 << Selected) - 1;

									break;
						}

						MenuItem = Item -> NextSelect;
					}
					else
						break;
				}
			}
		}

		if(Waiting)
			WaitPort(Window -> UserPort);
	}
	while(Waiting);
}

	/* Iterate(double RealValue,double ImaginaryValue):
	 *
	 *	The routine to handle the final calculation.
	 */

/*UBYTE __inline
Iterate(double RealValue,double ImaginaryValue,LONG MaxIterations)
{
	double	Real		= RealValue,		RealTemp,
		Imaginary	= ImaginaryValue,	ImaginaryTemp;
	WORD	i		= 1;

	do
	{
		RealTemp	= Real * Real;
		ImaginaryTemp	= Imaginary * Imaginary;

		if(fabs(RealTemp + ImaginaryTemp) > 4.0)
			return(Wave[i]);
		else
		{
			Imaginary	= (Real + Real) * Imaginary + ImaginaryValue;
			Real		= RealTemp - ImaginaryTemp + RealValue;
		}
	}
	while(++i < MaxIteration);

	return(0);
}
*/
	/* Mandelbrot():
	 *
	 *	The recursive image generation routine.
	 */

VOID
Mandelbrot(double MinReal,double MinImaginary,double RealStep,double ImaginaryStep,LONG Left,LONG Top,LONG Width,LONG Height)
{
	if(CheckSignal(SIG_WINDOW))
		HandleEvent();

	if(Running)
	{
		BYTE	Colours[4],
			LastColour,
			Ident = TRUE;
		WORD	Positions[4][2],
			i;

			/* Set up pixel positions. */

		Positions[0][0] = Left;
		Positions[0][1] = Top;

		Positions[1][0] = Left + Width - 1;
		Positions[1][1] = Top;

		Positions[2][0] = Left + Width - 1;
		Positions[2][1] = Top + Height - 1;

		Positions[3][0] = Left;
		Positions[3][1] = Top + Height - 1;

		for(i = 0 ; i < 4 ; i++)
		{
				/* Calculate colour values for each pixel. */

			Colours[i] = Iterate(MinReal + RealStep * (double)Positions[i][0],MinImaginary + ImaginaryStep * (double)Positions[i][1]);

				/* Are all colour identical? */

			if(Ident)
			{
				if(!i)
					LastColour = Colours[i];
				else
				{
					if(Colours[i] != LastColour)
						Ident = FALSE;
					else
						LastColour = Colours[i];
				}
			}
		}

			/* If all colour happen to be identical, fill the
			 * are in the given colour.
			 */

		if(Ident)
		{
				/* If extra precision is selected, also check
				 * if all the pixels on the margin of the
				 * current square are in the same colour.
				 */

			if(Precise)
			{
				WORD	PositionBackup[4][2],
					j;

				CopyMem(Positions,PositionBackup,sizeof(PositionBackup));

				for(i = 0 ; i < Width - 1 ; i++)
				{
					PositionBackup[0][0]++;
					PositionBackup[1][1]++;
					PositionBackup[2][0]--;
					PositionBackup[3][1]--;

					for(j = 0 ; j < 4 ; j++)
					{
						if(Iterate(MinReal + RealStep * (double)PositionBackup[j][0],MinImaginary + ImaginaryStep * (double)PositionBackup[j][1]) != Colours[0])
							goto Full;
					}
				}
			}

				/* If necessary fill the square. */

			if(Colours[0])
			{
				if(CurrentPen != Colours[0])
					SetAPen(RPort,CurrentPen = Colours[0]);

				RectFill(RPort,Left,Top,Left + Width - 1,Top + Height - 1);
			}
		}
		else
		{
				/* If not already down at minimum level,
				 * split the square again.
				 */

Full:			if(Width > 2 && Height > 2)
			{
				Mandelbrot(MinReal,MinImaginary,RealStep,ImaginaryStep,Left,Top,Width >> 1,Height >> 1);
				Mandelbrot(MinReal,MinImaginary,RealStep,ImaginaryStep,Left + (Width >> 1),Top,Width >> 1,Height >> 1);

				Mandelbrot(MinReal,MinImaginary,RealStep,ImaginaryStep,Left,Top + (Height >> 1),Width >> 1,Height >> 1);
				Mandelbrot(MinReal,MinImaginary,RealStep,ImaginaryStep,Left + (Width >> 1),Top + (Height >> 1),Width >> 1,Height >> 1);
			}
			else
			{
				if(IsNative)
				{
					register struct BitMap *BitMap = RPort -> BitMap;

						/* Set the pixels. */

					for(i = 0 ; i < 4 ; i++)
					{
						if(Colours[i])
							Plot(BitMap,Positions[i][0],Positions[i][1],Colours[i]);
					}
				}
				else
				{
						/* Set the pixels. */

					for(i = 0 ; i < 4 ; i++)
					{
						if(Colours[i])
						{
							if(CurrentPen != Colours[i])
								SetAPen(RPort,CurrentPen = Colours[i]);

							WritePixel(RPort,Positions[i][0],Positions[i][1]);
						}
					}
				}
			}
		}
	}
}

	/* RunMandelbrot():
	 *
	 *	This is the recursive image generation driver, it
	 *	remembers the time it took to generate the image and
	 *	calls the recursive image generation routine.
	 */

VOID
RunMandelbrot(double MinReal,double MaxReal,double MinImaginary,double MaxImaginary,LONG Width,LONG Height)
{
	double		RealStep,ImaginaryStep;
	UBYTE		TimeBuffer[40];
	struct timeval	TimeVal;

	GetTime();

	TimeVal = TimeRequest -> tr_time;

	RealStep	= (MaxReal - MinReal) / (double)Width;
	ImaginaryStep	= (MaxImaginary - MinImaginary) / (double)Height;

	if(Is39)
		CurrentPen = GetAPen(RPort);
	else
		CurrentPen = RPort -> FgPen;

	FullPicture = TRUE;

	Mandelbrot(MinReal,MinImaginary,RealStep,ImaginaryStep,0,0,Window -> Width >> 1,Window -> Height >> 1);
	Mandelbrot(MinReal,MinImaginary,RealStep,ImaginaryStep,Window -> Width >> 1,0,Window -> Width >> 1,Window -> Height >> 1);

	Mandelbrot(MinReal,MinImaginary,RealStep,ImaginaryStep,0,Window -> Height >> 1,Window -> Width >> 1,Window -> Height >> 1);
	Mandelbrot(MinReal,MinImaginary,RealStep,ImaginaryStep,Window -> Width >> 1,Window -> Height >> 1,Window -> Width >> 1,Window -> Height >> 1);

	GetTime();

	SubTime(&TimeRequest -> tr_time,&TimeVal);

	BltBitMap(RPort -> BitMap,(Square - 25 * 8) >> 1,(Square - 8) >> 1,BackupBitMap,0,0,8 * 25,8,0xC0,0xFF,NULL);

	GotClip = TRUE;

	SetAPen(RPort,CurrentPen = 1);
	SetDrMd(RPort,JAM1);

	TimeVal = TimeRequest -> tr_time;

	sprintf(TimeBuffer,"Duration %2d:%02d:%02d Minutes",(TimeVal . tv_secs % 86400) / 3600,(TimeVal . tv_secs % 3600) / 60,TimeVal . tv_secs % 60);

	Move(RPort,(Square - 25 * 8) >> 1,((Square - 8) >> 1) + 6);
	Text(RPort,TimeBuffer,25);

	Running = FALSE;
}

	/* RunAreaMandelbrot(double RealStep,double ImaginaryStep):
	 *
	 *	This is the iterative image generation routine,
	 *	unlike the routines above it generates the image
	 *	pixel by pixel and is not limited to a square
	 *	drawing area.
	 */

VOID
RunAreaMandelbrot(double RealStep,double ImaginaryStep)
{
	double		RealValue,
			ImaginaryValue;
	WORD		x,y;
	UBYTE		TimeBuffer[40];
	struct timeval	TimeVal;
	BYTE		Colour;

	if(Is39)
		CurrentPen = GetAPen(RPort);
	else
		CurrentPen = RPort -> FgPen;

	GetTime();

	TimeVal = TimeRequest -> tr_time;

	ImaginaryValue = MinImaginary + ImaginaryStep * (double)AreaTop;

		/* `Faster' actually only means that before a pixel
		 * is drawn the approriate spot is checked to see
		 * if there is not already a pixel in place.
		 */

	if(IsNative)
	{
		struct BitMap *BitMap = RPort -> BitMap;

		if(Faster)
		{
			for(y = 0 ; (y < AreaHeight) && Running ; y++)
			{
				RealValue = MinReal + RealStep * (double)AreaLeft;
	
				for(x = 0 ; (x < AreaWidth) && Running ; x++)
				{
					if(!Test(BitMap,AreaLeft + x,AreaTop + y))
					{
						if((Colour = Iterate(RealValue,ImaginaryValue)))
							Plot(BitMap,AreaLeft + x,AreaTop + y,Colour);
					}
	
					RealValue += RealStep;
	
					if(CheckSignal(SIG_WINDOW))
						HandleEvent();
				}
	
				ImaginaryValue += ImaginaryStep;
			}
		}
		else
		{
				/* These loops will calculate the image pixel
				 * by pixel without skipping already set areas.
				 * The first check insures that the drawing area
				 * is as wide as the screen which means that
				 * we are able to generate the pixels in fast
				 * ram and to copy them to chip ram when done.
				 */
	
			if(AreaWidth == Window -> Width && LineBitMap . Planes[0])
			{
				BitMap = &LineBitMap;
	
				for(y = 0 ; (y < AreaHeight) && Running ; y++)
				{
					RealValue = MinReal + RealStep * (double)AreaLeft;
	
					for(x = 0 ; (x < AreaWidth) && Running ; x++)
					{
						if((Colour = Iterate(RealValue,ImaginaryValue)))
							Plot(BitMap,AreaLeft + x,0,Colour);
						else
							Plot(BitMap,AreaLeft + x,0,0);
	
						RealValue += RealStep;
	
						if(CheckSignal(SIG_WINDOW))
							HandleEvent();
					}
	
					if(Running)
						CopyLine(BitMap,AreaTop + y);
	
					ImaginaryValue += ImaginaryStep;
				}
			}
			else
			{
				for(y = 0 ; (y < AreaHeight) && Running ; y++)
				{
					RealValue = MinReal + RealStep * (double)AreaLeft;
	
					for(x = 0 ; (x < AreaWidth) && Running ; x++)
					{
						if((Colour = Iterate(RealValue,ImaginaryValue)))
							Plot(BitMap,AreaLeft + x,AreaTop + y,Colour);
	
						RealValue += RealStep;
	
						if(CheckSignal(SIG_WINDOW))
							HandleEvent();
					}
	
					ImaginaryValue += ImaginaryStep;
				}
			}
		}
	}
	else
	{
		if(Faster)
		{
			for(y = 0 ; (y < AreaHeight) && Running ; y++)
			{
				RealValue = MinReal + RealStep * (double)AreaLeft;
	
				for(x = 0 ; (x < AreaWidth) && Running ; x++)
				{
					if(!ReadPixel(RPort,AreaLeft + x,AreaTop + y))
					{
						if((Colour = Iterate(RealValue,ImaginaryValue)))
						{
							if(CurrentPen != Colour)
								SetAPen(RPort,CurrentPen = Colour);

							WritePixel(RPort,AreaLeft + x,AreaTop + y);
						}
					}
	
					RealValue += RealStep;
	
					if(CheckSignal(SIG_WINDOW))
						HandleEvent();
				}
	
				ImaginaryValue += ImaginaryStep;
			}
		}
		else
		{
			for(y = 0 ; (y < AreaHeight) && Running ; y++)
			{
				RealValue = MinReal + RealStep * (double)AreaLeft;

				for(x = 0 ; (x < AreaWidth) && Running ; x++)
				{
					if((Colour = Iterate(RealValue,ImaginaryValue)))
					{
						if(CurrentPen != Colour)
							SetAPen(RPort,CurrentPen = Colour);

						WritePixel(RPort,AreaLeft + x,AreaTop + y);
					}

					RealValue += RealStep;

					if(CheckSignal(SIG_WINDOW))
						HandleEvent();
				}

				ImaginaryValue += ImaginaryStep;
			}
		}
	}

	GetTime();

	SubTime(&TimeRequest -> tr_time,&TimeVal);

	BltBitMap(RPort -> BitMap,(Square - 25 * 8) >> 1,(Square - 8) >> 1,BackupBitMap,0,0,8 * 25,8,0xC0,0xFF,NULL);

	GotClip = TRUE;

	SetAPen(RPort,CurrentPen = 1);
	SetDrMd(RPort,JAM1);

	TimeVal = TimeRequest -> tr_time;

	sprintf(TimeBuffer,"Duration %2d:%02d:%02d Minutes",(TimeVal . tv_secs % 86400) / 3600,(TimeVal . tv_secs % 3600) / 60,TimeVal . tv_secs % 60);

	Move(RPort,(Square - 25 * 8) >> 1,((Square - 8) >> 1) + 6);
	Text(RPort,TimeBuffer,25);

	Running = FALSE;

	Faster = TRUE;
}

	/* main():
	 *
	 *	The notorious main routine.
	 */

VOID __stdargs
main()
{
	struct PictureInfo	*Info;
	BYTE			 GotImage	= FALSE,
				 IsAnim		= FALSE;

		/* Open the required resources. */

	OpenAll();

	MinReal		= -RealWidth / 2;
	MinImaginary	= -ImaginaryHeight / 2;

	DummyBuffer[0] = 0;

		/* Pick up any selected images if run from Workbench. */

	if(!ThisProcess -> pr_CLI)
	{
		extern struct WBStartup *WBenchMsg;

		DefaultTool[0] = 0;

			/* Build new default tool name. */

		if(NameFromLock(WBenchMsg -> sm_ArgList[0] . wa_Lock,DefaultTool,256))
		{
			if(!AddPart(DefaultTool,WBenchMsg -> sm_ArgList[0] . wa_Name,256))
				DefaultTool[0] = 0;
		}

		if(!DefaultTool[0])
			strcpy(DefaultTool,"MandelSquare");

		PictureIcon . do_DefaultTool	= DefaultTool;

		AnimIcon . do_DefaultTool	= DefaultTool;

			/* Called with Workbench arguments? */

		if(WBenchMsg -> sm_NumArgs > 1)
		{
				/* Construct the full name. */

			if(NameFromLock(WBenchMsg -> sm_ArgList[1] . wa_Lock,DummyBuffer,256))
			{
				if(!AddPart(DummyBuffer,WBenchMsg -> sm_ArgList[1] . wa_Name,256))
					DummyBuffer[0] = 0;
				else
				{
					struct DiskObject *Icon;

						/* Read the icon and try to figure
						 * out whether the corresponding
						 * file is an animation or just
						 * a plain image.
						 */

					if(Icon = GetDiskObject(DummyBuffer))
					{
						UBYTE *Value;

						if(Value = FindToolType(Icon -> do_ToolTypes,"FILETYPE"))
						{
							if(MatchToolValue(Value,"ANIM"))
								IsAnim = TRUE;
						}

						FreeDiskObject(Icon);
					}
				}
			}
			else
				DummyBuffer[0] = 0;
		}
	}

		/* No chance, use the default image. */

	if(!DummyBuffer[0])
		strcpy(DummyBuffer,"MandelSquare.Title");

		/* Try to read the selected image. */

	if(Info = LoadPicture(DummyBuffer))
	{
		LONG Error;

		if(Info -> MandelHeader . Iterations)
		{
			MinReal		= Info -> MandelHeader . MinReal;
			MinImaginary	= Info -> MandelHeader . MinImaginary;
			RealWidth	= Info -> MandelHeader . RealWidth;
			ImaginaryHeight	= Info -> MandelHeader . ImaginaryHeight;

			MaxIteration	= Info -> MandelHeader . Iterations - 1;
		}

		if(ModeNotAvailable(Info -> ViewModes) || (Info -> BitMapHeader . nPlanes > 5 && IntuitionBase -> LibNode . lib_Version < 39))
		{
			FreePicture(Info);

			Info = NULL;
		}
		else
		{
			if(Info -> BitMapHeader . w == Info -> BitMapHeader . h && !(Info -> BitMapHeader . h & 3))
			{
				Square	= Info -> BitMapHeader . w;
				Mode	= Info -> ViewModes;
				Depth	= Info -> BitMapHeader . nPlanes;

				SetProperties(Mode);
			}
			else
			{
				FreePicture(Info);

				Info = NULL;
			}
		}

		if(Error = GfxSetup())
		{
			if(Info)
				FreePicture(Info);

			CloseAll(Error);
		}
		else
		{
			if(Info)
			{
				BltBitMapRastPort(Info -> BitMap,0,0,RPort,0,0,Screen -> Width,Screen -> Height,0xC0);

				LoadPalette(Screen,Info -> Palette,0);

				GetPalette(Screen,Palette);

				GotImage = TRUE;
			}
		}

		IterationSetup();

		if(Info)
			FreePicture(Info);
	}
	else
	{
		LONG	Error,
			Size;

		if(Size = GetModeSize(Mode))
		{
			struct DimensionInfo DimensionInfo;

			if(GetDisplayInfoData(NULL,(APTR)&DimensionInfo,sizeof(struct DimensionInfo),DTAG_DIMS,Mode))
			{
				if(DimensionInfo . MaxDepth > 8)
					Depth = 8;
				else
					Depth = DimensionInfo . MaxDepth;
			}

			Square = Size;

			SetProperties(Mode);
		}
		else
			CloseAll(RETURN_FAIL + 17);

		if(Error = GfxSetup())
			CloseAll(Error);
	}

		/* Are we supposed to load and replay an animation file? */

	if(IsAnim)
	{
		struct MinList *List;

		SetWait(Window);

		Window -> Flags |= WFLG_RMBTRAP;

		if(List = LoadAnim(DummyBuffer))
		{
			LONG Error = ValidateAnim(List,Screen);

			if(!Error)
			{
				strcpy(AnimPlayName,DummyBuffer);

				SetClear(Window);

				ScreenToFront(Screen);

				ActivateWindow(Window);

				PlayAnim(List,Screen,CheckAbort);

				SetWait(Window);
			}

			DeleteAnim(List);
		}

		Window -> Flags &= ~WFLG_RMBTRAP;

		ClearPointer(Window);

		LoadPalette(Screen,Palette,0);
	}
	else
	{
		if(!GotImage)
		{
				/* Set up working data. */

			MainMinReal		= MinReal;
			MainMinImaginary	= MinImaginary;
			MainRealWidth		= RealWidth;
			MainImaginaryHeight	= ImaginaryHeight;

				/* Present the screen. */

			LoadPalette(Screen,Palette,0);

			ScreenToFront(Screen);

			ActivateWindow(Window);

			Running		= TRUE;
			NewCoords	= FALSE;

				/* Generate the image. */

			RunMandelbrot(MinReal,MinReal + RealWidth,MinImaginary,MinImaginary + ImaginaryHeight,Window -> Width,Window -> Height);
		}
		else
		{
			strcpy(LastName,DummyBuffer);

				/* Present the screen. */

			ScreenToFront(Screen);

			BlockWindow();

			ActivateWindow(Window);

			ReleaseWindow();
		}

		RealStep	= (RealWidth) / (double)Window -> Width;
		ImaginaryStep	= (ImaginaryHeight) / (double)Window -> Height;
	}

		/* The main input loop. */

	FOREVER
	{
		WaitPort(Window -> UserPort);

		HandleEvent();

			/* New display mode? */

		if(NewMode)
		{
			LONG	Error,
				Size;

				/* We have a bitmap to copy to the
				 * screen.
				 */

			if(GlobalBitMap)
			{
				GfxCleanup();

				Mode	= GlobalMode;
				Depth	= GlobalDepth;
				Square	= GlobalSize;

				SetProperties(Mode);

				NewMode	= FALSE;

				if(Error = GfxSetup())
					CloseAll(Error);
				else
				{
					BltBitMapRastPort(GlobalBitMap,0,0,RPort,0,0,Screen -> Width,Screen -> Height,0xC0);

					FreeCustomBitMap(GlobalBitMap);

					GlobalBitMap = NULL;

					ScreenToFront(Screen);

					BlockWindow();

					ActivateWindow(Window);

					LoadPalette(Screen,GlobalPalette,0);
					GetPalette(Screen,Palette);

					FreePalette(GlobalPalette);

					GlobalPalette	= NULL;

					NewCoords	= FALSE;
					Running		= FALSE;
					AreaActive	= FALSE;

					IterationSetup();

					ReleaseWindow();
				}
			}
			else
			{
				if(Size = GetModeSize(Mode))
				{
					Square = Size;

					SetProperties(Mode);
				}
				else
					CloseAll(RETURN_FAIL + 17);

				NewMode	= FALSE;

				SetProperties(Mode);

				GfxCleanup();

				if(Error = GfxSetup())
					CloseAll(Error);
				else
				{
					LoadPalette(Screen,Palette,0);

					ScreenToFront(Screen);

					ActivateWindow(Window);

					MinReal		= MainMinReal;
					MinImaginary	= MainMinImaginary;
					RealWidth	= MainRealWidth;
					ImaginaryHeight	= MainImaginaryHeight;

					RealStep	= (RealWidth) / (double)Window -> Width;
					ImaginaryStep	= (ImaginaryHeight) / (double)Window -> Height;

					NewCoords	= TRUE;
					AreaActive	= FALSE;

					IterationSetup();
				}
			}
		}

			/* Run the animation routine? */

		while(AnimRunning)
		{
			RunAnim();

			if(!AnimRunning)
			{
				Running		= FALSE;
				NewCoords	= FALSE;
			}
		}

			/* Restart with new coordinates. */

		if(NewCoords)
		{
			Running		= TRUE;
			NewCoords	= FALSE;
		}

			/* Run the image generation routines. */

		if(Running)
		{
			if(AreaActive)
				RunAreaMandelbrot(RealStep,ImaginaryStep);
			else
			{
				SetRast(RPort,0);

				RunMandelbrot(MinReal,MinReal + RealWidth,MinImaginary,MinImaginary + ImaginaryHeight,Window -> Width,Window -> Height);
			}
		}
	}
}
