// TSMorph - Amiga Morphing program
// Copyright (C) © 1993  Topicsave Limited

// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

// mpaddock@cix.compulink.co.uk

//	$Author: M_J_Paddock $
//	$Date: 1993/09/11 21:55:48 $
//	$Revision: 1.16 $

/* Amiga headers */
#define INTUI_V36_NAMES_ONLY
#define INTUITION_IOBSOLETE_H

// Include correct math library
#ifdef MY68881
#include <m68881.h>
#else
#include <math.h>
#endif

// prevent inclusion of another math library
#define LIBRARIES_MATHFFP_H

// other headers, define some non 2.02 header stuff
#include <exec/types.h>
#include <exec/memory.h>
#include <intuition/intuition.h>
#include <intuition/gadgetclass.h>
#ifndef PGA_NewLook
#define PGA_NewLook (PGA_Dummy + 0x000A)
#endif
#include <intuition/imageclass.h>
#include <intuition/icclass.h>
#include <devices/input.h>
#include <workbench/workbench.h>
#include <workbench/startup.h>
#include <libraries/gadtools.h>
#ifndef GTMN_FullMenu
#define GTMN_FullMenu GT_TagBase+62
#endif
#include <libraries/asl.h>

#include <clib/exec_protos.h>
#include <clib/dos_protos.h>
#include <clib/layers_protos.h>
#include <clib/graphics_protos.h>
#include <clib/intuition_protos.h>
#include <clib/input_protos.h>
#include <clib/gadtools_protos.h>
#include <clib/asl_protos.h>
#include <clib/utility_protos.h>

#include <pragmas/exec_pragmas.h>
#include <pragmas/dos_pragmas.h>
#include <pragmas/layers_pragmas.h>
#include <pragmas/graphics_pragmas.h>
#include <pragmas/intuition_pragmas.h>
#include <pragmas/input_pragmas.h>
#include <pragmas/gadtools_pragmas.h>
#include <pragmas/asl_pragmas.h>
#include <pragmas/utility_pragmas.h>

#include <proto/diskfont.h>
#include <proto/icon.h>

#include <devices/timer.h>

#include <clib/timer_protos.h>
#include <pragmas/timer_pragmas.h>

#include <clib/alib_protos.h>

#include <rexx/errors.h>
#include <pragmas/rexxsyslib_pragmas.h>
#include <clib/rexxsyslib_protos.h>

#include <libraries/amigaguide.h>
#include <clib/amigaguide_protos.h>
#include <pragmas/amigaguide_pragmas.h>

#include <libraries/reqtools.h>
#include <clib/reqtools_protos.h>
#include <pragmas/reqtools.h>

#include <opal/opallib.h>

#include <libraries/dctv.h>
#include <clib/dctv_protos.h>
#include <pragmas/dctv_pragmas.h>

#include <stddef.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

/* Below is not in the 2.02 version of clib/utility_protos.h
                                       pragmas/utility_pragmas.h */
#if INCLUDE_VERSION < 37
LONG Stricmp(UBYTE *string1,UBYTE *string2);
#pragma libcall UtilityBase Stricmp A2 9802
#endif

// IFF header
#include "iffp/ILBMapp.h"

// Progress requester
#include "progress.h"

// JPEG load stuff
#include "JPEG_LS/jinclude.h"

// Help nodes
STRPTR context[] = {
	"",
	"Main",
	"Err-IFFBMHD",
	"Err-IFFRaster",
	"Err-IFFBODY",
	"Err-IFFCLIP",
	"Err-IFFILBM",
	"Err-IFFFileS",
	"Err-IFFTop",
	"Err-IFFMemory",
	"Req-Progress",
	"Err-LibraryR",
	"Err-Saving",
	"Err-AllocVec",
	"Err-OpenPoints",
	"Err-Progress",
	"Err-AllocPlanes",
	"Err-24",
	"Err-Load",
	"Err-AllocIFF",
	"Err-AllocILBM",
	"Err-FileFormat",
	"Err-SizeMatch",
	"Err-Range",
	"Err-MemPointsR",
	"Err-CloseFile",
	"Err-3Points",
	"Err-OpenFile",
	"Err-ARexx",
	"Req-Really_quit",
	"Err-OldFormat",
	"Err-OpalVision",
	"Err-NoOpal",
};

#define H_Help 		10
#define H_Library		11
#define H_ESave		12
#define H_AllocVec	13
#define H_OpenPoints	14
#define H_Progress	15
#define H_AllocPlanes 16
#define H_24			17
#define H_Load			18
#define H_AllocIFF	19
#define H_AllocILBM	20
#define H_FileFormat	21
#define H_SizeMatch	22
#define H_Range		23
#define H_MemPointsR	24
#define H_CloseFile	25
#define H_3Points		26
#define H_Open			27
#define H_ARexx		28
#define H_Really		29
#define HE_OldFormat	30
#define HE_OpalVision 31
#define HE_NoOpal		32

// Disable Ctrl-C checking
int CXBRK(void) { return(0); }
int chkabort(void) { return(0); }

// Libraries
extern struct Library *SysBase;
extern struct DOSBase *DOSBase;
struct Library *IntuitionBase, *GfxBase, *LayersBase, *IFFParseBase;
struct Library *GadToolsBase, *AslBase, *UtilityBase;
struct Library *RexxSysBase;
struct Library *AmigaGuideBase;
struct Library *TimerBase=NULL;
struct Library *ReqToolsBase;
struct Library *DCTVBase;

struct OpalBase *OpalBase = NULL;

extern struct List InfoList; // List of messages

// Help stuff
AMIGAGUIDECONTEXT handle;
struct NewAmigaGuide nag = {NULL};
ULONG ASig = 0;

/* Version string for CLI version */
#ifdef MY68881
char *Version = "$VER: TSMorph-render_881 2.3 (6.10.93)";
#else
char *Version = "$VER: TSMorph-render 2.3 (6.10.93)";
#endif

BOOL Pic1_Open = FALSE;				// 1st image open OK
BOOL Pic2_Open = FALSE;				// 2nd image open OK

struct MsgPort		*WMsgPortp;		// Message port for Picture Windows

// Properties for IFF read
LONG props[] = {	ID_ILBM,	ID_BMHD,
						ID_ILBM,	ID_CAMG,
						ID_ILBM, ID_CMAP,
						TAG_DONE };
LONG stops[] = {	ID_ILBM,	ID_BODY,
						TAG_DONE };
LONG nowt[] = { TAG_DONE };

/* Wait pointer */
static USHORT __chip BusyPointerData[] =
{
   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,
};

// Protos
BOOL SaveFile(void);
BOOL Load24bit(char *filename,struct Picture *pic,BOOL GUI);
void Close24bit(struct Picture *pic);
void Error(char *ErrorMessage, char *Gadget,char *extra,ULONG hnum);
void DeleteAllPoints(void);
void __regargs FindPoint(struct MyPoint *RetPoint);
BOOL MyOpen(char *filename,BOOL JustPoints);
UWORD __regargs intersect(struct MyPoint *Pointa,struct MyPoint *Pointb);
void __regargs CalcDiffs(struct MyPoint *a,struct MyPoint *bb,double *ab1,double *ab2,double *ab);
void __regargs Triangle(struct MyPoint *p,struct MyPoint *a,struct MyPoint *bb,struct MyPoint *c);
void __regargs checktriangle(struct MyPoint *,struct MyPoint *,struct MyPoint *,struct MyPoint **,struct MyPoint **,struct MyPoint **,ULONG *,UWORD *);
LONG SendRxMsg(char *msgtxt,BOOL IgnoreError);
extern void MyArgArrayInit(int argc,char **argv);
extern void MyArgArrayDone(void);
extern UBYTE *MyArgString(UBYTE *arg2,UBYTE *arg3,BOOL reopen);
extern LONG MyArgInt(UBYTE *arg2, long arg3,BOOL reopen);
extern BOOL MyFindToolType(UBYTE *arg2,BOOL reopen);
void OpenNewArgs(UBYTE *filename);
void DisableWindow(void);
void EnableWindow(void);
void help(ULONG hnum);
BOOL LoadFrames(BOOL points,BOOL image1,BOOL image2);
void AddMessage(UBYTE *message);

// Args.c
void argArrayDone( void );
char **argArrayInit( LONG argc, char **argv );

// SaveHAMetc.c
BOOL SaveBW16(UBYTE *FileName,USHORT width,USHORT height, UWORD swidth,
				  USHORT pwidth, USHORT pheight, USHORT pmode,
				  UBYTE *red, UBYTE *green, UBYTE *blue, struct RastPort *rp, struct RastPort *trp);
BOOL SaveBW256(UBYTE *FileName,USHORT width,USHORT height, UWORD swidth,
				  USHORT pwidth, USHORT pheight, USHORT pmode,
				  UBYTE *red, UBYTE *green, UBYTE *blue, struct RastPort *rp, struct RastPort *trp);
BOOL SaveHAM6(UBYTE *FileName,USHORT width,USHORT height, UWORD swidth,
				  USHORT pwidth, USHORT pheight, USHORT pmode,
				  UBYTE *red, UBYTE *green, UBYTE *blue, struct RastPort *rp, struct RastPort *trp);
BOOL SaveHAM8(UBYTE *FileName,USHORT width,USHORT height, UWORD swidth,
				  USHORT pwidth, USHORT pheight, USHORT pmode,
				  UBYTE *red, UBYTE *green, UBYTE *blue, struct RastPort *rp, struct RastPort *trp);
BOOL SaveDCTV(UBYTE *FileName,USHORT width,USHORT height, UWORD swidth,
				  USHORT pwidth, USHORT pheight, USHORT pmode,
				  UBYTE *red, UBYTE *green, UBYTE *blue, struct RastPort *rp, struct RastPort *trp);

// some work buffers
char buffer[256]="";
char buffer1[512]="";

// Image structure
struct Picture {
	struct ILBMInfo	*ilbm;		// For IFF read
	UBYTE					*red;			// Read chunky pixels	Must be RGB for Opal OVtoRGB
	UBYTE					*green;		// Green
	UBYTE					*blue;		// Blue
	UBYTE					filename[256];	// filename
	struct OpalScreen *OScrn;		// OpalScreen
	BOOL					rgbonelot;	// Is red,green,blue one AllocVec
};

struct Picture Pic1={0},Pic2={0};// 1st and 2nd image

UBYTE *plane0=NULL;					// Planes for temporary bit maps
UBYTE *plane1=NULL;
UBYTE *plane2=NULL;
UBYTE *plane3=NULL;
UBYTE *plane4=NULL;
UBYTE *plane5=NULL;
UBYTE *plane6=NULL;
UBYTE *plane7=NULL;

// Point structure
struct MyPoint {
	struct MinNode MyNode;	// linked into list
	WORD x,y;		// Coordinates in 1st image
	WORD x1,y1;		// 2nd image
	WORD Cx,Cy;		// Current image
	LONG Cdiff;		// seperation between two points (squared)
	WORD xdiff;		// x difference
	WORD ydiff;		// y difference
	double xd,yd;	// double coords for AntiAlias
	double x1d,y1d;// 2nd image
};

// 24 bitplane bitmap
struct MyBitMap {
	struct BitMap BitMap;
	PLANEPTR xplanes[16];
};

// List of points
struct List			PointList;

// Size of image
USHORT	width, height, pwidth, pheight, pmode;

char AnimName[256];	// Buffer for file name to save

char *Loadscript;		// ARexx load script

WORD x,y;				// Current coords

struct MyPoint **Points = NULL;	// Pointer to points

UWORD	Depth;	// Number of points to check each time

UWORD Mode;		// Mode = 0 use 3 closest if no others, choose 1st 3
					// Mode = 1 leave points stationary, choose 1st 3
					// Mode = 2 choose 3 closet, always use
					// Mode = 3 leave points stationary, choose 3 closest

#define MODE_STAT		1
#define MODE_CLOSEST	2

LONG PointCount = 0;			// Number of points

struct MyPoint BigPoint;	// Point far away

char MyFileName[257];

// Settings stuff
char **ArgArray;
char **ArgArraySettings;

// OpalVision screen
struct OpalScreen *OScrn = NULL;

char 				*OVFormat;	// Format of OpalVision save JPG or IFF (or anything) default
BOOL				AntiAlias;	// AntiAlias points

// Table of parameters
struct {
	LONG xf;				// Current frame
	LONG xFrames;		// Number of frames
	LONG xSingle;		// Warp or Morph
	LONG xmove;			// Movement 1 to 2
	LONG xr;				// red of image 1
	LONG xg;				// green
	LONG xb;				// blue
	LONG xr2;			// red of image 2
	LONG xg2;			// green
	LONG xb2;			// blue
	LONG xDo;			// Produce this image
	LONG xrplus;		// Add red
	LONG xgplus;		// green
	LONG xbplus;		// blue
	LONG xrminus;		// subtract red
	LONG xgminus;		// subtract green
	LONG xbminus;		// subtract blue
	LONG xDX;			// X skip
	LONG xDY;			// Y skip
	LONG xStart;		// Start Frame number
	LONG FILLER[32];	// Filler for future expansion
} Arexx;

#define f Arexx.xf
#define Frames Arexx.xFrames
#define Single Arexx.xSingle
#define move Arexx.xmove
#define r Arexx.xr
#define g Arexx.xg
#define b Arexx.xb
#define r2 Arexx.xr2
#define g2 Arexx.xg2
#define b2 Arexx.xb2
#define Do Arexx.xDo
#define rplus Arexx.xrplus
#define gplus Arexx.xgplus
#define bplus Arexx.xbplus
#define rminus Arexx.xrminus
#define gminus Arexx.xgminus
#define bminus Arexx.xbminus
#define DX Arexx.xDX
#define DY Arexx.xDY
#define Start Arexx.xStart

// Display help if available
void
help(ULONG hnum) {
	if (handle) {
		SetAmigaGuideContext(handle,hnum,NULL);
		SendAmigaGuideContext(handle,NULL);
	}
}

char 					FileName[256];	// File name buffer
long					OVQuality;	// OpalVision JPG quality
BOOL					OVFast;		// OpalVision fast format IFF
BOOL					OVThumb;		// OpalVision Thumbail
BOOL					HAM6;
BOOL					HAM8;
BOOL					BW16;
BOOL					BW256;
BOOL					DCTV3 = FALSE;
BOOL					DCTV4 = FALSE;
UBYTE 				*arrayr,		// Pointers to red,green and blue
 						*arrayg,
 						*arrayb;
BOOL					PPM;			// PPM save ?
UBYTE		*p[3] = {NULL,NULL,NULL};// chunky pointers - order for OpalVision
#define RED p[0]
#define BLUE p[2]
#define GREEN p[1]
UWORD		swidth;		// scaled width (in full words)
struct RastPort 	RP,			// Work Rast Ports
 						TRP;
struct MyBitMap 	MyBitMap;	// Real bit map
PLANEPTR 			Planes[24] = {NULL};	// Bit planes
BOOL					CreateIcons;// Create Icons on pictures?
char 					*Postscript;// ARexx postscript

/* Main program	*/
void
main(int argc,char **argv) {
 BOOL 	OkFlag	= TRUE;	// Is it working ?
 char 	*e			= NULL;	// error message
 struct MyPoint 	MyPoint;		// Points to work with
 struct MyPoint 	*MyPointp;
 BOOL 				Opened;		// has file been opened
 struct WBStartup *argmsg;		// Workbench stuff
 struct WBArg 		*wb_arg;
 char 				*e1;			// The rest of the error message
 struct Window 	*oldWindowPtr;	// Redirect the system requesters
 struct Process 	*process;
 char 				*filename;	// file name
 ULONG 				offset,		// 2d to 1d array offsets
 						offset1;
 char 				*Prescript;	// Rexx script names
 UWORD				myx,			// indexes for skipping
 						myy;
 UBYTE				xr,			// more skipping stuff
 						xg,
 						xb;
 struct AmigaGuideMsg *agm;	// help stuff
 struct timeval	time1,		// To display time per image
 						time2;
 struct timerequest tr;
 BOOL 				t2	= FALSE;	// Set when the times calculated
 char 				tbuffer[30];// buffer for time message
 ULONG				hnum = 0;	// Help number for error
 BOOL					Alloced=FALSE;	// Allocated memory?
 char					*Tstr;		// Temp string for parameter
 UWORD				xbig;
 double				xsmall;		// Int and rest of coords
 UWORD				ybig;
 double				ysmall;		// y coords
 WORD					right,down;	// move right and down?
 UWORD				xbig1;
 double				xsmall1;		// Int and rest of coords
 UWORD				ybig1;
 double				ysmall1;		// y coords
 WORD					right1,down1;// move right and down?
 double				a,bb,c,d,a1,b1,c1,d1;	// AntiAlias stuff

 // Set up BigPoint
 BigPoint.Cdiff = 0x7FFFFFFF;
 // Initialise points list
 NewList(&PointList);
 // Try and open reqtools.library and dctv.library
 ReqToolsBase = OpenLibrary("reqtools.library",38);
 DCTVBase = OpenLibrary("dctv.library",3);
 // Set up amigaguide (if available)
 if (AmigaGuideBase = OpenLibrary ("amigaguide.library", 33L)) {
  nag.nag_BaseName = "TSMorph";
  nag.nag_Name = "TSMorph.guide";
  nag.nag_ClientPort = "TSMorph-render_HELP";
  nag.nag_Context = context;
  if (handle = OpenAmigaGuideAsync (&nag, NULL)) {
   ASig = AmigaGuideSignal(handle);
  }
 }
 // Try and open the timer
 if (!OpenDevice(TIMERNAME,UNIT_MICROHZ,(struct IORequest *) &tr, 0L)) {
  TimerBase = (struct Library *)tr.tr_node.io_Device;
 }
 // Try and open OpalVision library
 OpalBase = (struct OpalBase *)OpenLibrary("opal.library",0L);
 // Open all the libraries
 if (IntuitionBase = OpenLibrary("intuition.library",37L)) {
  if (RexxSysBase = OpenLibrary("rexxsyslib.library",0)) {
   if (IFFParseBase = OpenLibrary("iffparse.library",37L)) {
    if (GfxBase = OpenLibrary("graphics.library",37L)) {
     if (LayersBase = OpenLibrary("layers.library",37L)) {
      if (GadToolsBase = OpenLibrary("gadtools.library",37L)) {
       if (AslBase = OpenLibrary("asl.library",37L)) {
        if (UtilityBase = OpenLibrary("utility.library",37)) {
         if (IconBase = OpenLibrary("icon.library",37L)) {
          if (DiskfontBase = OpenLibrary("diskfont.library",36)) {
            // Read and process the parameters
            MyArgArrayInit(argc,argv);
            PubScreenName = MyArgString("PUBSCREENR",NULL,FALSE);
            // Open the progress window
        	   if ((!SetupScreen())  && (!OpenProgressWindow())) {
        	  	 // redirect requesters
             process = (struct Process *)FindTask(NULL);
             oldWindowPtr = process->pr_WindowPtr;
             process->pr_WindowPtr = ProgressWnd;
             // Open the project file (CLI or WB)
             DisableWindow();
  	    	    AddMessage("Opening points file");
             if (argc) {
 	           if (filename = ArgString(ArgArray,"FILES",NULL)) {
               Opened = MyOpen(filename,FALSE);
              }
              else {
         	   Opened = MyOpen(NULL,FALSE);
              }
             }
             else {
              argmsg = (struct WBStartup *)argv;
			     if (argmsg->sm_NumArgs > 1) {
	            wb_arg = argmsg->sm_ArgList;
   	         wb_arg++;
      	      if (wb_arg->wa_Lock) {
 	             NameFromLock(wb_arg->wa_Lock,buffer,256);
 	            }
     	         if (AddPart(buffer,wb_arg->wa_Name,256)) {
        	       Opened = MyOpen(buffer,FALSE);
               }
              }
              else {
        	      Opened = MyOpen(NULL,FALSE);
        	     }
        	    }
        	    if (Opened) {
        	     // Get a few parameters
        	     Tstr = MyArgString("SAVEFORMAT","ILBM24",FALSE);
        	     if (OpalBase) {
        	      if (MatchToolValue(Tstr,"OPAL24") || MatchToolValue(Tstr,"OPAL24T") ||
        	          MatchToolValue(Tstr,"OPAL24F") || MatchToolValue(Tstr,"OPAL24FT")) {
        	       OVFormat = "IFF";
        	      }
        	      else {
        	       if (MatchToolValue(Tstr,"JPEG") || MatchToolValue(Tstr,"JPEGT")) {
        	        OVFormat = "JPG";
        	       }
        	       else {
        	        OVFormat = NULL;
        	       }
        	      }
        	     }
        	     else {
        	      OVFormat = NULL;
        	     }
        	     PPM = MatchToolValue(Tstr,"PPM");
        	     HAM6 = MatchToolValue(Tstr,"HAM6");
        	     HAM8 = MatchToolValue(Tstr,"HAM8");
        	     BW16 = MatchToolValue(Tstr,"BW16");
        	     BW256 = MatchToolValue(Tstr,"BW256");
        	     if (DCTVBase) {
        	      DCTV3 = MatchToolValue(Tstr,"DCTV3");
        	      DCTV4 = MatchToolValue(Tstr,"DCTV4");
        	     }
              OVQuality = MyArgInt("QUALITY",75,FALSE);
              if (MatchToolValue(Tstr,"OPAL24T") || MatchToolValue(Tstr,"JPEGT")) {
               OVThumb = TRUE;
              }
              else {
               OVThumb = FALSE;
              }
              if (MatchToolValue(Tstr,"OPAL24F") || MatchToolValue(Tstr,"OPAL24FT")) {
               OVFast = TRUE;
              }
              else {
               OVFast = FALSE;
              }
              Prescript = MyArgString("PRESCRIPT","Rexx/Prescript",FALSE);
              Postscript = MyArgString("POSTSCRIPT","Rexx/Postscript",FALSE);
              Loadscript = MyArgString("LOADSCRIPT","Rexx/Loadscript",FALSE);
              DX = MyArgInt("DX",0,FALSE);
              DY = MyArgInt("DY",0,FALSE);
              Depth = max(MyArgInt("DEPTH",2,FALSE),20);
              Mode = MyArgInt("MODE",0,FALSE);
              CreateIcons = MatchToolValue(MyArgString("CREATEICONSR","NO",FALSE),"YES");
              AntiAlias = MatchToolValue(MyArgString("ANTIALIAS","NO",FALSE),"YES");
        	     // File has been opened, cache width in full words
        	     swidth = (((width+15)>>4)<<4);
  	    	     AddMessage("Allocating work memory");
     	    	  // Allocate chunky and ILBM bit maps
        	     if ((RED = AllocVec((swidth*height),0)) &&
  	       	 	   (GREEN = AllocVec((swidth*height),0)) &&
  	       	 	   (BLUE =  AllocVec((swidth*height),0)) &&
  	       	 	   ((OpalBase && OVFormat && !PPM && (OScrn = CreateScreen24(0,width,height))) ||
  	       	 	    PPM ||
  	       	 	    (!OVFormat && !PPM &&
  	       		     (Planes[0] = AllocRaster(width,height)) &&
  	       		     (Planes[1] = AllocRaster(width,height)) &&
  	       		     (Planes[2] = AllocRaster(width,height)) &&
  	       		     (DCTV3 ||
  	       		      ((Planes[3] = AllocRaster(width,height)) &&
  	       		      (DCTV4 || BW16 ||
  	       		       ((Planes[4] = AllocRaster(width,height)) &&
  	       		        (Planes[5] = AllocRaster(width,height)) &&
  	       		        (HAM6 ||
  	       		         ((Planes[6] = AllocRaster(width,height)) &&
  	       		          (Planes[7] = AllocRaster(width,height)) &&
  	       		          (HAM8 || BW256 ||
  	       		           ((Planes[8] = AllocRaster(width,height)) &&
  	       		            (Planes[9] = AllocRaster(width,height)) &&
  	       		            (Planes[10] = AllocRaster(width,height)) &&
  	       		            (Planes[11] = AllocRaster(width,height)) &&
  	       		            (Planes[12] = AllocRaster(width,height)) &&
  	       		            (Planes[13] = AllocRaster(width,height)) &&
  	       		            (Planes[14] = AllocRaster(width,height)) &&
  	       		            (Planes[15] = AllocRaster(width,height)) &&
  	       		            (Planes[16] = AllocRaster(width,height)) &&
  	       		            (Planes[17] = AllocRaster(width,height)) &&
  	       		            (Planes[18] = AllocRaster(width,height)) &&
  	       		            (Planes[19] = AllocRaster(width,height)) &&
  	       		            (Planes[20] = AllocRaster(width,height)) &&
  	       		            (Planes[21] = AllocRaster(width,height)) &&
  	       		            (Planes[22] = AllocRaster(width,height)) &&
  	       		            (Planes[23] = AllocRaster(width,height))))))))))))) { // Looks like LISP!!!!
  	       		Alloced = TRUE;
  	       		// Update the progress
  	       	   EnableWindow();
     	    	   GT_SetGadgetAttrs(ProgressGadgets[GDX_Frame],ProgressWnd,NULL,
     	    	 						 GTSL_Min,1,
     	    							 GTSL_Max,Frames,TAG_END);
     	    	   GT_SetGadgetAttrs(ProgressGadgets[GDX_Line],ProgressWnd,NULL,
     	    	 						 GTSL_Min,0,
     	    							 GTSL_Max,height-1,TAG_END);
				   // Pass to ARexx for processing
					if (strcmp(Prescript,"OFF")) {
    	    	    AddMessage("Prescript for frame 0");
      	    	 f = 0;
      	    	 Do = 0;
					 strcpy(buffer,Prescript);
					 strcat(buffer," %ld");
					 sprintf(buffer1,buffer,&Arexx);
					 OkFlag = !SendRxMsg(buffer1,FALSE);
					 if (!OkFlag) {
					   e = (char *)-1;
					 }
					 if (Do && OkFlag) {
        	    	  OkFlag = LoadFrames(FALSE,TRUE,FALSE);
        	    	  if (!OkFlag) {
        	    	   e = (char *)-1;
        	    	  }
        	    	  else {
    	    	      AddMessage("Saving frame 0");
        	    	   memcpy(RED,Pic1.red,swidth*height);
        	    	   memcpy(GREEN,Pic1.green,swidth*height);
        	    	   memcpy(BLUE,Pic1.blue,swidth*height);
					   OkFlag = SaveFile();
         	    	if (!OkFlag) {
         	    	 e = (char *)-1;
         	    	}
        	    	  }
					 }
					}
     	    	   // Determine start time
     	    	   if (TimerBase) {
     	    	    GetSysTime(&time1);
     	    	   }
     	    	   // Loop thru frames
      	      for (f=1;
      	    		(f < (Frames+1)) && OkFlag;		// no of Frames
        	    		f++) {
        	    	 // Update progress
  	    	       AddMessage("Prescript processing");
        	       GT_SetGadgetAttrs(ProgressGadgets[GDX_Frame],ProgressWnd,NULL,
        	    							GTSL_Level,f,TAG_END);
        	       // calculate default image parameters
        	    	 rplus = gplus = bplus = rminus = gminus = bminus = 0;
					 if ((Single == 1) || (Single == 3)) {
					  move = ((Frames-f) << 10)/Frames;
					  r = g = b = 1024;
					  r2 = g2 = b2 = 0;
					  Do = 1;
					 }
					 else {
					  r = g = b = move = ((Frames-f+1) << 10)/(Frames + 1);
					  r2 = g2 = b2 = (1024 - r);
					  Do = 1;
					 }
					 // Pass to ARexx for processing
					 if (strcmp(Prescript,"OFF")) {
					  strcpy(buffer,Prescript);
					  strcat(buffer," %ld");
					  sprintf(buffer1,buffer,&Arexx);
					  OkFlag = !SendRxMsg(buffer1,FALSE);
					  if (!OkFlag) {
					   e = (char *)-1;
					  }
					 }
        	       if (OkFlag && Do && ((!Pic1_Open) || (Single == 2) || (Single == 3))) {
                 if (Pic2_Open) {
 	               Close24bit(&Pic2);
 	               Pic2_Open = FALSE;
 	              }
	              if (Pic1_Open) {
	               Close24bit(&Pic1);
	               Pic1_Open = FALSE;
	              }
        	    	  OkFlag = LoadFrames(TRUE,TRUE,TRUE);
        	    	  if (!OkFlag) {
        	    	   e = (char *)-1;
        	    	  }
        	    	 }
					 if ((PointCount < 3) && OkFlag) {
					  Error("Must have at least 3 points","Quit",NULL,H_3Points);
					  OkFlag = FALSE;
					 }
					 if (Do && OkFlag) {
        	    	  // If we have calculated time per image the display it, update other gadgets
        	    	  if (t2) {
        	    	   sprintf(tbuffer,"%ld Seconds for last image",time2.tv_secs);
   	    	      AddMessage(tbuffer);
        	    	  }
        	    	  else {
   	    	      AddMessage("Rendering image");
      	    	  }
      	    	  if (TimerBase) {
      	    	   GetSysTime(&time1);
      	    	  }
					  // We want to do this image so calculate coordinates in this image
					  if ((Single == 1) || (Single == 3)) {
					   for (MyPointp = (struct MyPoint *)PointList.lh_Head;
							MyPointp->MyNode.mln_Succ;
				   		MyPointp = (struct MyPoint *)MyPointp->MyNode.mln_Succ) {
		   		   	 MyPointp->Cx = (MyPointp->x1*(1024-move)+MyPointp->x*move)>>10;
			   	   	 MyPointp->Cy = (MyPointp->y1*(1024-move)+MyPointp->y*move)>>10;
		      	   }
		      	  }
		      	  else {
					   for (MyPointp = (struct MyPoint *)PointList.lh_Head;
					 		MyPointp->MyNode.mln_Succ;
				    		MyPointp = (struct MyPoint *)MyPointp->MyNode.mln_Succ) {
		   		    	 MyPointp->Cx = (MyPointp->x1*(1024-move)+MyPointp->x*move)>>10;
			   	    	 MyPointp->Cy = (MyPointp->y1*(1024-move)+MyPointp->y*move)>>10;
		      	   }
		      	  }
		      	  // Initialise chunky pointers
	        	     arrayr=RED;
   	     	     arrayg=GREEN;
      	  	     arrayb=BLUE;
      	  	     // loop thru lines
        		  	  for (y=0;
        	  	      (y<height) && OkFlag;
        	  	      y++) {
        	  	      // Update progress
	        	      GT_SetGadgetAttrs(ProgressGadgets[GDX_Line],ProgressWnd,NULL,
   	     	     							GTSL_Level,(ULONG)y,TAG_END);
   	     	     	// Loop thru columns
      	     	   for (x=0;
       	           x < width;
        	           x++) {
        	          // Determine coordinates on end image(s)
        	          FindPoint(&MyPoint);
        	          if (AntiAlias) {
        	           // Horrible AntiAlias stuff
        	           xbig = floor(MyPoint.xd + 0.5);
        	           xsmall = MyPoint.xd - (double)xbig;
        	           if (xsmall > 0) {
        	            right = 1;
        	           }
        	           else {
        	            xsmall = -xsmall;
        	            right = -1;
        	           }
        	           ybig = floor(MyPoint.yd + 0.5);
        	           ysmall = MyPoint.yd - (double)ybig;
        	           if (ysmall > 0) {
        	            down = swidth;
        	           }
        	           else {
        	            ysmall = -ysmall;
        	            down = -swidth;
        	           }
     	              offset = ybig*swidth+xbig;
     	              a = 1.0 + (xsmall * ysmall) - xsmall - ysmall;
     	              bb = xsmall * (1.0 - ysmall);
     	              c = ysmall * (1.0 - xsmall);
     	              d = xsmall * ysmall;
        	           if ((Single != 1) && (Single != 3)) {
         	         xbig1 = floor(MyPoint.x1d + 0.5);
        	            xsmall1 = MyPoint.x1d - (double)xbig1;
        	            if (xsmall1 > 0) {
        	             right1 = 1;
        	            }
        	            else {
        	             xsmall1 = -xsmall1;
        	             right1 = -1;
        	            }
        	            ybig1 = floor(MyPoint.y1d + 0.5);
        	            ysmall1 = MyPoint.y1d - (double)ybig1;
        	            if (ysmall1 > 0) {
        	             down1 = swidth;
        	            }
        	            else {
        	             ysmall1 = -ysmall1;
        	             down1 = -swidth;
        	            }
     	               offset1 = ybig1*swidth+xbig1;
     	               a1 = 1.0 + (xsmall1 * ysmall1) - xsmall1 - ysmall1;
     	               b1 = xsmall1 * (1.0 - ysmall1);
     	               c1 = ysmall1 * (1.0 - xsmall1);
     	               d1 = xsmall1 * ysmall1;
			            (*arrayr) = min(max((((ULONG)
														((((double)  Pic1.red[offset])*a + ((double)  Pic1.red[offset+right])*bb+
			            							 ((double)  Pic1.red[offset+down])*c+((double)  Pic1.red[offset+down+right])*d)*((double)r) +
														(((double)  Pic2.red[offset1])*a1 + ((double)  Pic2.red[offset1+right1])*b1+
			            							 ((double)  Pic2.red[offset1+down1])*c1+((double)  Pic2.red[offset1+down1+right1])*d1)*((double)r2))
			            							)>>10) + rplus - rminus,0),255);
			            (*arrayg) = min(max((((ULONG)
														((((double)Pic1.green[offset])*a + ((double)Pic1.green[offset+right])*bb+
			            							 ((double)Pic1.green[offset+down])*c+((double)Pic1.green[offset+down+right])*d)*((double)g) +
														(((double)Pic2.green[offset1])*a1 + ((double)Pic2.green[offset1+right1])*b1+
			            							 ((double)Pic2.green[offset1+down1])*c1+((double)Pic2.green[offset1+down1+right1])*d1)*((double)g2))
			            							)>>10) + gplus - gminus,0),255);
			            (*arrayb) = min(max((((ULONG)
														((((double) Pic1.blue[offset])*a + ((double) Pic1.blue[offset+right])*bb+
			            							 ((double) Pic1.blue[offset+down])*c+((double) Pic1.blue[offset+down+right])*d)*((double)b) +
														(((double) Pic2.blue[offset1])*a1 + ((double) Pic2.blue[offset1+right1])*b1+
			            							 ((double) Pic2.blue[offset1+down1])*c1+((double) Pic2.blue[offset1+down1+right1])*d1)*((double)b2))
			            							)>>10) + bplus - bminus,0),255);
        	           }
        	           else {
			            (*arrayr) = min(max(((ULONG)
														(((((double)Pic1.red[offset])*a + ((double)Pic1.red[offset+right])*bb+
			            							 ((double)Pic1.red[offset+down])*c+((double)Pic1.red[offset+down+right])*d)
			            							)*((double)r))>>10) + rplus - rminus,0),255);
      	  	   	   (*arrayg) = min(max(((ULONG)
			            							(((((double)Pic1.green[offset])*a + ((double)Pic1.green[offset+1])*bb+
			            							 ((double)Pic1.green[offset+down])*c+((double)Pic1.green[offset+down+right])*d)
      	  	   	   							)*((double)g))>>10) + gplus - gminus,0),255);
		      	  	   (*arrayb) = min(max(((ULONG)
														(((((double)Pic1.blue[offset])*a + ((double)Pic1.blue[offset+1])*bb+
			            							 ((double)Pic1.blue[offset+down])*c+((double)Pic1.blue[offset+down+right])*d)
		      	  	   							)*((double)b))>>10) + bplus - bminus,0),255);
        	           }
        	          }
        	          else {
        	           // Calculate new colours (without AntiAlias)
     	              offset = MyPoint.y*swidth+MyPoint.x;
        	           if ((Single == 1) || (Single == 3)) {
			            (*arrayr) = min(max(((Pic1.red[offset]*r)>>10) + rplus - rminus,0),255);
      	  	   	   (*arrayg) = min(max(((Pic1.green[offset]*g)>>10) + gplus - gminus,0),255);
		      	  	   (*arrayb) = min(max(((Pic1.blue[offset]*b)>>10) + bplus - bminus,0),255);
      		  	     }
      		  	     else {
      	            offset1 = MyPoint.y1*swidth+MyPoint.x1;
			            (*arrayr) = min(max((((Pic2.red[offset1]*r2) +
   	   		  	    				 (Pic1.red[offset]*r))>>10) + rplus - rminus,0),255);
      	  	   	   (*arrayg) = min(max((((Pic2.green[offset1]*g2) +
        		        				 (Pic1.green[offset]*g))>>10) + gplus - gminus,0),255);
		      	  	   (*arrayb) = min(max((((Pic2.blue[offset1]*b2) +
      		  		    				 (Pic1.blue[offset]*b))>>10) + bplus - bminus,0),255);
      		  	     }
      		  	    }
      		  	    // If we are skipping columns then copy colour forward
      		  	    if (DX) {
     		  	        xr = *arrayr;
      		  	     xg = *arrayg;
      		  	     xb = *arrayb;
      		  	     x++;
      		  	     for (myx = 0;
      		  	     		 (myx < DX) && (x < width);
      		  	     		 x++,myx++) {
 		        	      arrayr++;
      		  	      *arrayr = xr;
         	   	   arrayg++;
      		  	      *arrayg = xg;
      		  	      arrayb++;
      		  	      *arrayb = xb;
      		  	     }
      		  	     x--;
      		  	    }
      		  	    // next column (if not already there)
      		  	    if (x < width) {
		        	     arrayr++;
         	   	  arrayg++;
      		  	     arrayb++;
      		  	    }
        	     	   }
        	     	   // end of line, check 'Stop' and 'Help'
        	     	   OkFlag = HandleProgressIDCMP();
      	         if (handle) {
	      	       while (agm = GetAmigaGuideMsg(handle)) {
   	   		     ReplyAmigaGuideMsg(agm);
      		       }
      		      }
      		      // skip part word at end of line
        	     	   arrayr += (swidth - width);
        	     	   arrayg += (swidth - width);
        	     	   arrayb += (swidth - width);
        	     	   // if skipping lines then copy previous line
        	     	   if (DY) {
        	     	    y++;
        		  	    for (myy = 0;
        	  	            (myy < DY) && (y<height);
        	  	            y++, myy++) {
        	  	        memcpy(arrayr, arrayr - swidth, swidth);
        	  	        arrayr += swidth;
        	  	        memcpy(arrayg, arrayg - swidth, swidth);
        	  	        arrayg += swidth;
        	  	        memcpy(arrayb, arrayb - swidth, swidth);
        	  	        arrayb += swidth;
        	  	       }
        	  	       y--;
        	  	      }
        	    	  }
        	    	  // not ok - already displayed an error
        	    	  if (!OkFlag) {
        	    	   e = (char *)-1;
         	     }
        	    	  else {
        	    	   // Save image
					   OkFlag = SaveFile();
					   // Caclulate time (if we have not already)
          	    	if (TimerBase) {
     	          	 GetSysTime(&time2);
     	          	 t2 = TRUE;
     	          	 SubTime(&time2,&time1);
     	    		   }
     	    		   // Not ok - already displayed error message
				 	   if (!OkFlag) {
					    e = (char *)-1;
					   }
	  	 			  }
		          }
		         }
		         if (OkFlag && ((Single == 0) || (Single == 2))) {
 				    // Pass to ARexx for processing
 					 if (strcmp(Prescript,"OFF")) {
    	    	     AddMessage("Prescript for last frame");
      	    	  f = Frames+1;
      	    	  Do = 0;
					  strcpy(buffer,Prescript);
					  strcat(buffer," %ld");
					  sprintf(buffer1,buffer,&Arexx);
					  OkFlag = !SendRxMsg(buffer1,FALSE);
					  if (!OkFlag) {
					   e = (char *)-1;
					  }
        	        if (OkFlag && Do && ((!Pic1_Open) || (Single == 2))) {
                  if (Pic2_Open) {
 	                Close24bit(&Pic2);
 	                Pic2_Open = FALSE;
 	               }
	               if (Pic1_Open) {
	                Close24bit(&Pic1);
	                Pic1_Open = FALSE;
	               }
        	    	   OkFlag = LoadFrames(FALSE,FALSE,TRUE);
        	    	   if (!OkFlag) {
        	    	    e = (char *)-1;
        	    	   }
        	    	  }
        	    	  if (OkFlag && Do) {
    	    	      AddMessage("Saving last frame");
        	    	   memcpy(RED,Pic2.red,swidth*height);
        	    	   memcpy(GREEN,Pic2.green,swidth*height);
        	    	   memcpy(BLUE,Pic2.blue,swidth*height);
					   OkFlag = SaveFile();
         	    	if (!OkFlag) {
         	    	 e = (char *)-1;
         	    	}
        	    	  }
					 }
					}
		        }
  	    	     AddMessage("Cleaning up");
  	    	     DeleteAllPoints();
  	    	     if (RED) FreeVec(RED);
  	    	     if (GREEN) FreeVec(GREEN);
	           if (BLUE) FreeVec(BLUE);
	           if (OScrn) {
	            FreeScreen24(OScrn);
	           }
	           else {	// Should not need this if else
	           	if (!PPM) {
 	             if (Planes[0]) FreeRaster(Planes[0],width,height);
	             if (Planes[1]) FreeRaster(Planes[1],width,height);
	             if (Planes[2]) FreeRaster(Planes[2],width,height);
	             if (!DCTV3) {
	              if (Planes[3]) FreeRaster(Planes[3],width,height);
	              if (!BW16 && !DCTV4) {
	               if (Planes[4]) FreeRaster(Planes[4],width,height);
	               if (Planes[5]) FreeRaster(Planes[5],width,height);
	               if (!HAM6) {
	                if (Planes[6]) FreeRaster(Planes[6],width,height);
	                if (Planes[7]) FreeRaster(Planes[7],width,height);
	                if (!HAM8 && !BW256) {
	                 if (Planes[8]) FreeRaster(Planes[8],width,height);
	                 if (Planes[9]) FreeRaster(Planes[9],width,height);
	                 if (Planes[10]) FreeRaster(Planes[10],width,height);
	                 if (Planes[11]) FreeRaster(Planes[11],width,height);
	                 if (Planes[12]) FreeRaster(Planes[12],width,height);
	                 if (Planes[13]) FreeRaster(Planes[13],width,height);
	                 if (Planes[14]) FreeRaster(Planes[14],width,height);
	                 if (Planes[15]) FreeRaster(Planes[15],width,height);
	                 if (Planes[16]) FreeRaster(Planes[16],width,height);
	                 if (Planes[17]) FreeRaster(Planes[17],width,height);
	                 if (Planes[18]) FreeRaster(Planes[18],width,height);
	                 if (Planes[19]) FreeRaster(Planes[19],width,height);
	                 if (Planes[20]) FreeRaster(Planes[20],width,height);
	                 if (Planes[21]) FreeRaster(Planes[21],width,height);
	                 if (Planes[22]) FreeRaster(Planes[22],width,height);
	                 if (Planes[23]) FreeRaster(Planes[23],width,height);
	                }
	               }
	              }
	             }
	            }
	           }
		        if (!Alloced && !e) {
		         e = "Error AllocVec for output";
		         e1 = NULL;
		         hnum = H_AllocVec;
		        }
		       }
		       else {
		        e = (char *)-1;
		       }
		       if (OkFlag) {
		        e = (char *)-1;
		       }
		       if (!e) {
 		        e = "Error Opening points file";
		        e1 = NULL;
		        hnum = H_OpenPoints;
		       }
		       if (Pic2_Open) {
 	           Close24bit(&Pic2);
 	           Pic2_Open = FALSE;
 	          }
	          if (Pic1_Open) {
	           Close24bit(&Pic1);
	           Pic1_Open = FALSE;
	          }
			    if (plane0) FreeVec(plane0);
			    if (plane1) FreeVec(plane1);
			    if (plane2) FreeVec(plane2);
			    if (plane3) FreeVec(plane3);
			    if (plane4) FreeVec(plane4);
			    if (plane5) FreeVec(plane5);
			    if (plane6) FreeVec(plane6);
			    if (plane7) FreeVec(plane7);
			    // reset requesters
	          process->pr_WindowPtr = oldWindowPtr;
	         }
	         else {
	          e = "Error opening progress window";
	          e1 = NULL;
	          hnum = H_Progress;
	         }
	         // close down everything
	         CloseProgressWindow();
	         CloseDownScreen();
			   MyArgArrayDone();
            CloseLibrary(DiskfontBase);
          }
          if (!e) {
           e = "Unable to Open %s";
           e1 = "diskfont.library(36)";
           hnum = H_Library;
          }
          CloseLibrary(IconBase);
          }
          if (!e) {
           e = "Unable to Open %s";
           e1 = "icon.library(37)";
           hnum = H_Library;
          }
          CloseLibrary(UtilityBase);
         }
        if (!e) {
         e = "Unable to Open %s";
         e1= "utility.library(37)";
         hnum = H_Library;
        }
        CloseLibrary(AslBase);
       }
       if (!e) {
        e = "Unable to Open %s";
        e1 = "asl.library(37)";
        hnum = H_Library;
       }
       CloseLibrary(GadToolsBase);
      }
      if (!e) {
       e = "Unable to Open %s";
       e1 = "gadtools.library(37)";
       hnum = H_Library;
      }
      CloseLibrary(LayersBase);
     }
     if (!e) {
      e = "Unable to Open %s";
      e1 = "layers.library(37)";
      hnum = H_Library;
     }
     CloseLibrary(GfxBase);
    }
    if (!e) {
     e = "Unable to Open %s";
     e1 = "graphics.library(37)";
     hnum = H_Library;
    }
    CloseLibrary(IFFParseBase);
   }
   if (!e) {
    e = "Unable to Open %s";
    e1 = "iffparse.library(37)";
    hnum = H_Library;
   }
   CloseLibrary(RexxSysBase);
  }
  if (!e) {
   e = "Unable to Open %s";
   e1 = "rexxsyslib.library(0)";
   hnum = H_Library;
  }
  // Display an error message
  if (e != (char *)-1) {
   Error(e,"Quit",e1,hnum);
  }
 }
 if (OpalBase) {
  CloseLibrary((struct Library *)OpalBase);
 }
 if (TimerBase) {
  CloseDevice((struct IORequest *) &tr);
 }
 if (AmigaGuideBase) {
  if (handle) {
 	CloseAmigaGuide(handle);
  }
  CloseLibrary(AmigaGuideBase);
 }
 if (DCTVBase) {
  CloseLibrary(DCTVBase);
 }
 if (ReqToolsBase) {
  CloseLibrary(ReqToolsBase);
 }
}

// save an image
BOOL
SaveFile(void) {
	struct ILBMInfo 	*ilbm;		// IFF stuff to save
	BPTR					fh;			// File handle for PPM
	BOOL OkFlag = TRUE;
	struct BitMap 	TBM;				// Temp bit map
	struct DiskObject *MyDiskObject;	// The Icon
	char					*iconname;	// iconname
	char					*iconname1;	// alternate iconname
	if (!PPM) {
		// Not PPM
		if (OVFormat) {
			// OpalVision so convert RGB to OpalVision
			RGBtoOV(OScrn,p,0,0,swidth,height);
		}
		else {
			// Set up BitMap
			InitBitMap(&MyBitMap,(DCTV3?3:((DCTV4|BW16)?4:(HAM6?6:((HAM8|BW256)?8:24)))),width,height);
			InitRastPort(&RP);
			RP.BitMap = &MyBitMap;
			InitRastPort(&TRP);
			InitBitMap(&TBM,(DCTV3?3:((DCTV4|BW16)?4:(HAM6?6:((HAM8|BW256)?8:8)))),width,1);
			TBM.BytesPerRow = (((width+15)>>4)<<1);
			TBM.Rows = 1;
			TRP.BitMap = &TBM;
			TBM.Planes[0]=plane0;
			TBM.Planes[1]=plane1;
			TBM.Planes[2]=plane2;
			TBM.Planes[3]=plane3;
			TBM.Planes[4]=plane4;
			TBM.Planes[5]=plane5;
			TBM.Planes[6]=plane6;
			TBM.Planes[7]=plane7;
			if (!HAM6 && !HAM8 && !BW16 && !BW256 && !DCTV3 && !DCTV4) {
				// i.e. 24 bit ILBM?
				// so convert chunky to planar
				MyBitMap.BitMap.Depth = 8;
				MyBitMap.BitMap.Planes[0] = Planes[0];
				MyBitMap.BitMap.Planes[1] = Planes[1];
				MyBitMap.BitMap.Planes[2] = Planes[2];
				MyBitMap.BitMap.Planes[3] = Planes[3];
				MyBitMap.BitMap.Planes[4] = Planes[4];
				MyBitMap.BitMap.Planes[5] = Planes[5];
				MyBitMap.BitMap.Planes[6] = Planes[6];
				MyBitMap.BitMap.Planes[7] = Planes[7];
				WritePixelArray8(&RP,0,0,width-1,height-1,RED,&TRP);
				WaitBlit();
				MyBitMap.BitMap.Planes[0] = Planes[8];
				MyBitMap.BitMap.Planes[1] = Planes[9];
				MyBitMap.BitMap.Planes[2] = Planes[10];
				MyBitMap.BitMap.Planes[3] = Planes[11];
				MyBitMap.BitMap.Planes[4] = Planes[12];
				MyBitMap.BitMap.Planes[5] = Planes[13];
				MyBitMap.BitMap.Planes[6] = Planes[14];
				MyBitMap.BitMap.Planes[7] = Planes[15];
				WritePixelArray8(&RP,0,0,width-1,height-1,GREEN,&TRP);
				WaitBlit();
				MyBitMap.BitMap.Planes[0] = Planes[16];
				MyBitMap.BitMap.Planes[1] = Planes[17];
				MyBitMap.BitMap.Planes[2] = Planes[18];
				MyBitMap.BitMap.Planes[3] = Planes[19];
				MyBitMap.BitMap.Planes[4] = Planes[20];
				MyBitMap.BitMap.Planes[5] = Planes[21];
				MyBitMap.BitMap.Planes[6] = Planes[22];
				MyBitMap.BitMap.Planes[7] = Planes[23];
				WritePixelArray8(&RP,0,0,width-1,height-1,BLUE,&TRP);
				WaitBlit();
			}
			// Reset up BitMap
			MyBitMap.BitMap.Depth = (DCTV3?3:((DCTV4|BW16)?4:(HAM6?6:((HAM8|BW256)?8:24))));
			MyBitMap.BitMap.Planes[0] = Planes[0];
			MyBitMap.BitMap.Planes[1] = Planes[1];
			MyBitMap.BitMap.Planes[2] = Planes[2];
			MyBitMap.BitMap.Planes[3] = Planes[3];
			MyBitMap.BitMap.Planes[4] = Planes[4];
			MyBitMap.BitMap.Planes[5] = Planes[5];
			MyBitMap.BitMap.Planes[6] = Planes[6];
			MyBitMap.BitMap.Planes[7] = Planes[7];
			MyBitMap.BitMap.Planes[8] = Planes[8];
			MyBitMap.BitMap.Planes[9] = Planes[9];
			MyBitMap.BitMap.Planes[10] = Planes[10];
			MyBitMap.BitMap.Planes[11] = Planes[11];
			MyBitMap.BitMap.Planes[12] = Planes[12];
			MyBitMap.BitMap.Planes[13] = Planes[13];
			MyBitMap.BitMap.Planes[14] = Planes[14];
			MyBitMap.BitMap.Planes[15] = Planes[15];
			MyBitMap.BitMap.Planes[16] = Planes[16];
			MyBitMap.BitMap.Planes[17] = Planes[17];
			MyBitMap.BitMap.Planes[18] = Planes[18];
			MyBitMap.BitMap.Planes[19] = Planes[19];
			MyBitMap.BitMap.Planes[20] = Planes[20];
			MyBitMap.BitMap.Planes[21] = Planes[21];
			MyBitMap.BitMap.Planes[22] = Planes[22];
			MyBitMap.BitMap.Planes[23] = Planes[23];
		}
	}
	// Set up output file name
   sprintf(FileName,AnimName,f+Start-1);
	if (PPM) {
		// PPM Save is very simple P6 mode
		AddMessage("Saving PPM image");
		if (fh = Open(FileName,MODE_NEWFILE)) {
			sprintf(buffer,"P6\n%ld %ld\n255\n",width,height);
			FPuts(fh,buffer);
			arrayr=RED;
			arrayg=GREEN;
			arrayb=BLUE;
			// loop thru lines
			for (y=0;
				  (y<height) && OkFlag;
				  y++) {
				// Loop thru columns
				for (x=0;
					  x < width;
					  x++) {
					FPutC(fh,*arrayr);
					FPutC(fh,*arrayg);
					FPutC(fh,*arrayb);
					arrayr++;
					arrayg++;
					arrayb++;
				}
				// skip part word at end of line
				arrayr += (swidth - width);
				arrayg += (swidth - width);
				arrayb += (swidth - width);
			}
			if (!Close(fh)) {
				OkFlag = FALSE;
			}
		}
		else {
			OkFlag = FALSE;
		}
	}
	else {
		// Save non PPM
		if (OVFormat) {
			// Save OpalVision
			if (!strcmp(OVFormat,"JPG")) {
				AddMessage("Saving Opal JPEG image");
				// Save JPEG
				OkFlag = !SaveJPEG24(OScrn,FileName,(OVThumb?0:NOTHUMBNAIL),OVQuality);
			}
			else {
				AddMessage("Saving Opal IFF24 image");
				// Save IFF
				OkFlag = !SaveIFF24(OScrn,FileName,NULL,(OVThumb?0:NOTHUMBNAIL)|(OVFast?OVFASTFORMAT:0));
			}
		}
		else {
			if (HAM6) {
				AddMessage("Saving HAM6 image");
				OkFlag = SaveHAM6(FileName,width,height,swidth,
										pwidth,pheight,pmode,
										RED,GREEN,BLUE,&RP,&TRP);
			}
			else {
				if (HAM8) {
					AddMessage("Saving HAM8 image");
					OkFlag = SaveHAM8(FileName,width,height,swidth,
											pwidth,pheight,pmode,
											RED,GREEN,BLUE,&RP,&TRP);
				}
				else {
					if (BW16) {
						AddMessage("Saving BW16 image");
						OkFlag = SaveBW16(FileName,width,height,swidth,
												pwidth,pheight,pmode,
												RED,GREEN,BLUE,&RP,&TRP);
					}
					else {
						if (BW256) {
							AddMessage("Saving BW256 image");
							OkFlag = SaveBW256(FileName,width,height,swidth,
													 pwidth,pheight,pmode,
													 RED,GREEN,BLUE,&RP,&TRP);
						}
						else {
							if (DCTV3 || DCTV4) {
								AddMessage(DCTV3?"Saving DCTV3 image":"Saving DCTV4 image");
								OkFlag = SaveDCTV(FileName,width,height,swidth,
														pwidth,pheight,pmode,
														RED,GREEN,BLUE,&RP,&TRP);
							}
							else {
								AddMessage("Saving ILBM24 image");
								// allocate some IFF stuff
								if (ilbm = (struct ILBMInfo *)AllocMem(sizeof(struct ILBMInfo),MEMF_PUBLIC|MEMF_CLEAR)) {
									if (ilbm->ParseInfo.iff = AllocIFF()) {
										OkFlag = !saveilbm(ilbm, &MyBitMap, pmode,
																 width,  height, pwidth, pheight,
																 NULL, 0, 0,	/* colortable */
																 mskNone, 0,	/* masking, transparent */
																 NULL, NULL, 	/* chunklists */
																 FileName);
										// Close everything down cleanly
										FreeIFF(ilbm->ParseInfo.iff);
									}
									else {
										OkFlag = FALSE;
									}
									FreeMem(ilbm,sizeof(struct ILBMInfo));
								}
								else {
									OkFlag = FALSE;
								}
							}
						}
					}
				}
			}
		}
	}
	if (OkFlag) {
		if (CreateIcons) {
			// try to save an Icon
			if (MyDiskObject = GetDiskObject(FileName)) {
				// Do not overwrite if already present
				FreeDiskObject(MyDiskObject);
			}
			else {
				// Set up two possible names
				if (OVFormat) {
					if (!strcmp(OVFormat,"JPG")) {
						iconname = "ENV:TSMorph/def_jpg";
						iconname1 = "ENV:SYS/def_jpg";
					}
					else {
						iconname = "ENV:TSMorph/def_iff";
						iconname1 = "ENV:SYS/def_iff";
					}
				}
				else {
					if (PPM) {
						iconname = "ENV:TSMorph/def_ppm";
						iconname1 = "ENV:SYS/def_ppm";
					}
					else {
						if (BW16) {
							iconname = "ENV:TSMorph/def_bw16";
							iconname1 = "ENV:SYS/def_bw16";
						}
						else {
							if (BW256) {
								iconname = "ENV:TSMorph/def_bw256";
								iconname1 = "ENV:SYS/def_bw256";
							}
							else {
								if (HAM6) {
									iconname = "ENV:TSMorph/def_ham6";
									iconname1 = "ENV:SYS/def_ham6";
								}
								else {
									if (HAM8) {
										iconname = "ENV:TSMorph/def_ham8";
										iconname1 = "ENV:SYS/def_ham8";
									}
									else {
										if (DCTV3) {
											iconname = "ENV:TSMorph/def_dctv3";
											iconname1 = "ENV:SYS/def_dctv3";
										}
										else {
											if (DCTV4) {
												iconname = "ENV:TSMorph/def_dctv4";
												iconname1 = "ENV:SYS/def_dctv4";
											}
											else {
												iconname = "ENV:TSMorph/def_ilbm";
												iconname1 = "ENV:SYS/def_ilbm";
											}
										}
									}
								}
							}
						}
					}
				}
				// Try two names, as last resort use default project
				if ((MyDiskObject = GetDiskObject(iconname)) ||
					 (MyDiskObject = GetDiskObject(iconname1)) ||
					 (MyDiskObject = GetDefDiskObject(WBPROJECT))) {
					PutDiskObject(FileName,MyDiskObject);
					FreeDiskObject(MyDiskObject);
				}
			}
		}
	}
	if (!OkFlag) {
		Error("Error saving file '%s'","Quit",FileName,H_ESave);
	}
	else {
		// Post image processing
		if (strcmp(Postscript,"OFF")) {
			strcpy(buffer,Postscript);
			strcat(buffer," %ld %ld %ld %s");
			sprintf(buffer1,buffer,f+Start-1,Frames,Single,FileName);
			AddMessage("Postscript processing");
			OkFlag = !SendRxMsg(buffer1,FALSE);
		}
	}
	return OkFlag;
}

extern UWORD FileFormat = 0;

/* recognized file formats */
#define FORMAT_IFF	1
#define FORMAT_JPEG	2
#define FORMAT_GIF	3
#define FORMAT_PPM	4
#define FORMAT_TARGA	5

/* Opens a 24 bit image
 * filename = name of file
 * pic		= structure to update
 * GUI		= TRUE to display error message
 * Returns	  TRUE if ok
 */
BOOL
Load24bit(char *filename,struct Picture *pic,BOOL GUI) {
	char 					*e 	= NULL;	// Error message main text
	char					*e1 	= NULL;	// sub text,
	struct RastPort 	RP,				// Rastport
							TRP;				// Temp rastport
	struct BitMap 		BM,				// Bitmap
							TBM;				// Temp bitmap
	ULONG					hnum	= 0;		// Help on error
	BOOL					OkFlag=TRUE;	// Still OK?
	char					buffer[256],	// For Rexx Scripts
							buffer1[256];
	long					Err;				// Opal error
	BPTR					fh;
	UBYTE zbuffer[5]="\0\0\0\0";
	decompress_info_ptr info;			// Load JPEG stuff
	UWORD i,j;
	LONG penno;
	struct RastPort	Rp;				// Rast port for conversion
	UBYTE *rx,*gx,*bx;
	UBYTE rr,gg,bb;
	UWORD EHB;
	BOOL isHAM6 = FALSE;
	BOOL isHAM8 = FALSE;
	BOOL isEHB = FALSE;
	struct DisplayInfo queryinfo;
	DisplayInfoHandle dhandle = NULL;
	struct DCTVCvtHandle	*chandle=NULL;	// DCTV conversion stuff
	UWORD					*DCTVcolors=NULL;	// DCTV palette

	if (filename) {
   	// Load image processing
		if (strcmp(Loadscript,"OFF")) {
			strcpy(buffer,Loadscript);
			strcat(buffer," %ld %ld %ld %ld %s");
			sprintf(buffer1,buffer,f+Start-1,Frames,Single,(pic==&Pic1)?0:1,filename);
  		   AddMessage("Loadscript processing");
	      OkFlag = !SendRxMsg(buffer1,FALSE);
   	   if (!OkFlag) {
      		e = (char *)-1;
      		GUI = FALSE;	// Do not display error
	      }
	   }
		pic->rgbonelot = FALSE;
	   if (!e) {
	   	// determine file format
			if (fh = Open(filename,MODE_OLDFILE)) {
				FRead(fh,zbuffer,4,1);
				if ((!strcmp(zbuffer,"FORM")) ||
					 (!strcmp(zbuffer,"CAT ")) ||
					 (!strcmp(zbuffer,"LIST"))) {
					FileFormat = FORMAT_IFF;
				}
				else {
					if ((zbuffer[0] == 0xFF) &&
						 (zbuffer[1] == 0xD8)) {
						FileFormat = FORMAT_JPEG;
					}
					else {
						if ((zbuffer[0] == 'G') &&
							 (zbuffer[1] == 'I') &&
							 (zbuffer[2] == 'F')) {
							FileFormat = FORMAT_GIF;
						}
						else {
							if ((zbuffer[0] == 'P') &&
								 ((zbuffer[1] == '2') ||
								  (zbuffer[1] == '3') ||
								  (zbuffer[1] == '5') ||
								  (zbuffer[1] == '6'))) {
								FileFormat = FORMAT_PPM;
							}
							else {
								FileFormat = FORMAT_TARGA;
							}
						}
					}
				}
				Close(fh);
			}
			else {
				e = "Error opening file '%s'";
				e1 = filename;
				hnum = H_Open;
				OkFlag = FALSE;
			}
		}
		if (OkFlag) {
			AddMessage((pic == &Pic2)?"Loading image 2":"Loading image 1");
		}
	   // We have a file name
		if (OkFlag && (FileFormat != FORMAT_IFF)) {
			// Not an IFF file so use JPEG code
    	   AddMessage((FileFormat == FORMAT_JPEG)?"Loading JFIF":
    	 				  (FileFormat == FORMAT_GIF)?"Loading GIF":
    	 				  (FileFormat == FORMAT_PPM)?"Loading PPM":
    	 				  (FileFormat == FORMAT_TARGA)?"Loading Targa?":
    	 				  "Loading ?");
			if ((info = LoadJPEG(filename,0,NULL,NULL,NULL,NULL)) &&
				 (info->output_file)) {
	    	   AddMessage("Allocating work memory");
				pic->rgbonelot = TRUE;
				pic->red = (UBYTE *)info->output_file;
				pic->green = pic->red + (((info->image_width+15)>>4)<<4) * info->image_height;
				pic->blue = pic->green + (((info->image_width+15)>>4)<<4) * info->image_height;
				// Allocate temp bit maps if first image and no OpalVision save
				if ((OVFormat) || (plane0) ||
	  	       		  ((plane0 = AllocVec((((width+15)>>4)<<4),MEMF_CHIP)) &&
	  	       		   (plane1 = AllocVec((((width+15)>>4)<<4),MEMF_CHIP)) &&
	  	       		   (plane2 = AllocVec((((width+15)>>4)<<4),MEMF_CHIP)) &&
	  	       		   (plane3 = AllocVec((((width+15)>>4)<<4),MEMF_CHIP)) &&
	  	       		   (plane4 = AllocVec((((width+15)>>4)<<4),MEMF_CHIP)) &&
	  	       		   (plane5 = AllocVec((((width+15)>>4)<<4),MEMF_CHIP)) &&
	  	       		   (plane6 = AllocVec((((width+15)>>4)<<4),MEMF_CHIP)) &&
	  	       		   (plane7 = AllocVec((((width+15)>>4)<<4),MEMF_CHIP)))) {
	  	     		return TRUE;
	  	   	}
	  	   	else {
		         e = "Unable to %s";
			      e1 = "allocate memory for bitmap";
			      hnum = H_AllocPlanes;
	  	   	}
			}
		}
		if (OkFlag && (FileFormat == FORMAT_IFF)) {
			// Load iff
    	   AddMessage("Loading IFF");
			if (pic->ilbm = (struct ILBMInfo *)AllocMem(sizeof(struct ILBMInfo),MEMF_PUBLIC|MEMF_CLEAR)) {
				// IFF stuff
	   		if (pic->ilbm->ParseInfo.iff = AllocIFF()) {
	   			// more iff stuff
					pic->ilbm->ParseInfo.propchks = props;
					pic->ilbm->ParseInfo.collectchks = nowt;
					pic->ilbm->ParseInfo.stopchks = stops;
					// Load the image using IFF routines
					if (!(loadbrush(pic->ilbm,filename))) {
						// Close the file and validate
						closeifile(&(pic->ilbm->ParseInfo));
						pwidth  = width  < 320 ? 320 : width;
		         	pheight = height < 200 ? 200 : height;
  	     		     	pmode   = pwidth >= 640  ? HIRES : 0L;
            	 	pmode  |= pheight >= 400 ? LACE  : 0L;
	// ??????		width = pic->ilbm->Bmhd.w;
						// Allocate chunky bit maps
			    	   AddMessage("Allocating Bitmaps");
						if ((pic->red = AllocVec(((((pic->ilbm->Bmhd.w+15)>>4)<<4)*pic->ilbm->Bmhd.h),0)) &&
				  	       (pic->green = AllocVec(((((pic->ilbm->Bmhd.w+15)>>4)<<4)*pic->ilbm->Bmhd.h),0)) &&
				  	       (pic->blue = AllocVec(((((pic->ilbm->Bmhd.w+15)>>4)<<4)*pic->ilbm->Bmhd.h),0)) &&
						  	 // Allocate temp bit maps if first image and no OpalVision save
						  	 ((OVFormat && (pic->ilbm->Bmhd.nPlanes != 24)) || (plane0) ||
  			       		  ((plane0 = AllocVec((((width+15)>>4)<<4),MEMF_CHIP)) &&
  	   		    		   (plane1 = AllocVec((((width+15)>>4)<<4),MEMF_CHIP)) &&
  	      		 		   (plane2 = AllocVec((((width+15)>>4)<<4),MEMF_CHIP)) &&
  	       				   (plane3 = AllocVec((((width+15)>>4)<<4),MEMF_CHIP)) &&
  	       				   (plane4 = AllocVec((((width+15)>>4)<<4),MEMF_CHIP)) &&
  	       		   		(plane5 = AllocVec((((width+15)>>4)<<4),MEMF_CHIP)) &&
	  	       		   	(plane6 = AllocVec((((width+15)>>4)<<4),MEMF_CHIP)) &&
		  	       		   (plane7 = AllocVec((((width+15)>>4)<<4),MEMF_CHIP))))) {
							if (pic->ilbm->Bmhd.nPlanes == 24) {
								// Convert 24 bit planar to chunky
					    	   AddMessage("Converting 24 bit to Chunky");
	  	       		   	// Initialise rast ports/bitmaps
								InitRastPort(&RP);
								InitRastPort(&TRP);
						      InitBitMap(&TBM,8,pic->ilbm->Bmhd.w,1);
		       				TRP.BitMap = &TBM;
						      TBM.Planes[0]=plane0;
						      TBM.Planes[1]=plane1;
						      TBM.Planes[2]=plane2;
						      TBM.Planes[3]=plane3;
						      TBM.Planes[4]=plane4;
						      TBM.Planes[5]=plane5;
						      TBM.Planes[6]=plane6;
						      TBM.Planes[7]=plane7;
						      InitBitMap(&BM,8,pic->ilbm->Bmhd.w,pic->ilbm->Bmhd.h);
						      RP.BitMap = &BM;
						  	   BM.Planes[0] = pic->ilbm->brbitmap->Planes[0];
						      BM.Planes[1] = pic->ilbm->brbitmap->Planes[1];
						      BM.Planes[2] = pic->ilbm->brbitmap->Planes[2];
						      BM.Planes[3] = pic->ilbm->brbitmap->Planes[3];
						      BM.Planes[4] = pic->ilbm->brbitmap->Planes[4];
						      BM.Planes[5] = pic->ilbm->brbitmap->Planes[5];
						      BM.Planes[6] = pic->ilbm->brbitmap->Planes[6];
						      BM.Planes[7] = pic->ilbm->brbitmap->Planes[7];
						      // Convert red to chunky
						      ReadPixelArray8(&RP,0,0,pic->ilbm->Bmhd.w-1,pic->ilbm->Bmhd.h-1,pic->red,&TRP);
		             	   WaitBlit();
						 	   BM.Planes[0] = pic->ilbm->brbitmap->Planes[8];
						      BM.Planes[1] = pic->ilbm->brbitmap->Planes[9];
						      BM.Planes[2] = pic->ilbm->brbitmap->Planes[10];
						      BM.Planes[3] = pic->ilbm->brbitmap->Planes[11];
						      BM.Planes[4] = pic->ilbm->brbitmap->Planes[12];
						      BM.Planes[5] = pic->ilbm->brbitmap->Planes[13];
						      BM.Planes[6] = pic->ilbm->brbitmap->Planes[14];
						      BM.Planes[7] = pic->ilbm->brbitmap->Planes[15];
						      // and green
						      ReadPixelArray8(&RP,0,0,pic->ilbm->Bmhd.w-1,pic->ilbm->Bmhd.h-1,pic->green,&TRP);
		             	   WaitBlit();
						      BM.Planes[0] = pic->ilbm->brbitmap->Planes[16];
						      BM.Planes[1] = pic->ilbm->brbitmap->Planes[17];
						      BM.Planes[2] = pic->ilbm->brbitmap->Planes[18];
						      BM.Planes[3] = pic->ilbm->brbitmap->Planes[19];
						      BM.Planes[4] = pic->ilbm->brbitmap->Planes[20];
						      BM.Planes[5] = pic->ilbm->brbitmap->Planes[21];
						      BM.Planes[6] = pic->ilbm->brbitmap->Planes[22];
						      BM.Planes[7] = pic->ilbm->brbitmap->Planes[23];
						      // and blue
						      ReadPixelArray8(&RP,0,0,pic->ilbm->Bmhd.w-1,pic->ilbm->Bmhd.h-1,pic->blue,&TRP);
		             	   WaitBlit();
					    	   AddMessage("Cleaning up");
		             	   // Set up stuff to write image
	// ????					width = pic->ilbm->Bmhd.w;
	//	????					height = pic->ilbm->Bmhd.h;
		            	 	// Unload everything and exit cleanly
								unloadbrush(pic->ilbm);
								FreeIFF(pic->ilbm->ParseInfo.iff);
								FreeMem(pic->ilbm,sizeof(struct ILBMInfo));
						      return TRUE;
						   }
						   else {
								if (DCTVBase && TestDCTVSignature(pic->ilbm->brbitmap)) {
									// Convert DCTV to chunky
									if (DCTVcolors = AllocVec(2L<<pic->ilbm->Bmhd.nPlanes,NULL)) {
										for (i=0; i < (1L<<pic->ilbm->Bmhd.nPlanes); ++i) {
											DCTVcolors[i] = ((pic->ilbm->RGB[i*4+1] & 0xf0) << 4) |
																 ((pic->ilbm->RGB[i*4+2] & 0xf0)) |
																 ((pic->ilbm->RGB[i*4+3] & 0xf0) >> 4);
										}
							    	   AddMessage("Converting DCTV to Chunky");
										if (chandle = AllocDCTVCvtTags(pic->ilbm->brbitmap,
														DCTVCVTA_Type, DCTVCVTT_DCTVtoRGB,
														DCTVCVTA_Width, (((pic->ilbm->Bmhd.w+15)>>4)<<4),
														DCTVCVTA_Height, pic->ilbm->Bmhd.h,
														DCTVCVTA_Flags, ((pic->ilbm->camg & LACE)?DCTVCVTF_Lace:0)|
																			DCTVCVTF_CustomRGBBuf,
														DCTVCVTA_ColorTable, DCTVcolors,
														TAG_END)) {
											chandle->Red = pic->red;
											chandle->Green = pic->green;
											chandle->Blue = pic->blue;
											while (chandle->DstLineNum < chandle->Height) {
												CvtDCTVLine(chandle);
												if ((chandle->DstLineNum > 0) &&
													 (chandle->DstLineNum <= chandle->Height)) {
													chandle->Red += (((pic->ilbm->Bmhd.w+15)>>4)<<4);
													chandle->Green += (((pic->ilbm->Bmhd.w+15)>>4)<<4);
													chandle->Blue += (((pic->ilbm->Bmhd.w+15)>>4)<<4);
												}
											}
											FreeVec(DCTVcolors);
											FreeDCTVCvt(chandle);
					            	 	// Unload everything and exit cleanly
											unloadbrush(pic->ilbm);
											FreeIFF(pic->ilbm->ParseInfo.iff);
											FreeMem(pic->ilbm,sizeof(struct ILBMInfo));
											return TRUE;
										}
										FreeVec(DCTVcolors);
									}
								}
								else {
									// determine special IFF mode
									if ((dhandle = FindDisplayInfo(pic->ilbm->camg)) &&
										 (GetDisplayInfoData(dhandle,(UBYTE *)&queryinfo,sizeof(queryinfo),DTAG_DISP,NULL))) {
										if ((pic->ilbm->Bmhd.nPlanes == 6) && (queryinfo.PropertyFlags & DIPF_IS_HAM)) {
								    	   AddMessage("Converting HAM6 to Chunky");
											isHAM6 = TRUE;
										}
										else {
											if ((pic->ilbm->Bmhd.nPlanes == 8) && (queryinfo.PropertyFlags & DIPF_IS_HAM)) {
												isHAM8 = TRUE;
									    	   AddMessage("Converting HAM8 to Chunky");
											}
											else {
										  		if ((pic->ilbm->Bmhd.nPlanes == 6) && (queryinfo.PropertyFlags & DIPF_IS_EXTRAHALFBRITE)) {
										  			isEHB = TRUE;
										    	   AddMessage("Converting EHB to Chunky");
										  		}
										  		else {
										    	   AddMessage("Converting ILBM to Chunky");
										  		}
											}
										}
									}
									// Convert to chunky
									InitRastPort(&Rp);
									Rp.BitMap = pic->ilbm->brbitmap;
									rx = pic->red;
									gx = pic->green;
									bx = pic->blue;
									// For each line
									for (j = 0;
										 j < height;
										 ++j) {
										// Convert each column
										// Note: this is very ineffecient in that it uses
										// ReadPixel().
										// But the cost is fairly small compared to the render time
										if (isHAM6) {
									  		rr = gg = bb = 0;
									   	for (i = 0;
										  		  i < width;
									   		  ++i) {
									   		penno = ReadPixel(&Rp,i,j);
									   		switch (penno & 0x30) {
									   		case 0:
										   		*rx++ = rr = pic->ilbm->RGB[penno*4+1];
										   		*gx++ = gg = pic->ilbm->RGB[penno*4+2];
									   			*bx++ = bb = pic->ilbm->RGB[penno*4+3];
									   			break;
									   		case 0x10:
									   			*rx++ = rr;
									   			*gx++ = gg;
									   			*bx++ = bb = (penno&0xf)|((penno&0xf)<<4);
									   			break;
									   		case 0x20:
									   			*rx++ = rr = (penno&0xf)|((penno&0xf)<<4);
									   			*gx++ = gg;
									   			*bx++ = bb;
									   			break;
									   		case 0x30:
									   			*rx++ = rr;
									   			*gx++ = gg = (penno&0xf)|((penno&0xf)<<4);
									   			*bx++ = bb;
									   			break;
									   		}
											}
									  	}
									  	else {
									  		if (isHAM8) {
												rr = gg = bb = 0;
										   	for (i = 0;
											  		  i < width;
										   		  ++i) {
										   		penno = ReadPixel(&Rp,i,j);
										   		switch (penno & 0xc0) {
										   		case 0:
											   		*rx++ = rr = pic->ilbm->RGB[penno*4+1];
										   			*gx++ = gg = pic->ilbm->RGB[penno*4+2];
									   				*bx++ = bb = pic->ilbm->RGB[penno*4+3];
									   				break;
										   		case 0x40:
										   			*rx++ = rr;
										   			*gx++ = gg;
										   			*bx++ = bb = (penno&0x3f)<<2;
									   				break;
									   			case 0x80:
									   				*rx++ = rr = (penno&0x3f)<<2;
									   				*gx++ = gg;
									   				*bx++ = bb;
									   				break;
										   		case 0xc0:
										   			*rx++ = rr;
										   			*gx++ = gg = (penno&0x3f)<<2;
										   			*bx++ = bb;
									   				break;
									   			}
												}
									  		}
									  		else {
									  			if (isEHB) {
											   	for (i = 0;
												  		  i < width;
											   		  ++i) {
											   		penno = ReadPixel(&Rp,i,j);
											   		EHB = (penno & 0x20)?1:0;
											   		penno &= 0x1f;
											   		*rx++ = pic->ilbm->RGB[penno*4+1]>>EHB;
											   		*gx++ = pic->ilbm->RGB[penno*4+2]>>EHB;
											   		*bx++ = pic->ilbm->RGB[penno*4+3]>>EHB;
													}
										  		}
										  		else {
											   	for (i = 0;
												  		  i < width;
											   		  ++i) {
											   		penno = ReadPixel(&Rp,i,j);
											   		*rx++ = pic->ilbm->RGB[penno*4+1];
											   		*gx++ = pic->ilbm->RGB[penno*4+2];
											   		*bx++ = pic->ilbm->RGB[penno*4+3];
													}
												}
											}
								   	}
										rx += ((((width+15)>>4)<<4)-width);
										gx += ((((width+15)>>4)<<4)-width);
										bx += ((((width+15)>>4)<<4)-width);
									}
						    	   AddMessage("Cleaning up");
									unloadbrush(pic->ilbm);
									FreeIFF(pic->ilbm->ParseInfo.iff);
									FreeMem(pic->ilbm,sizeof(struct ILBMInfo));
							      return TRUE;
							   }
							}
						}
	  	    			if (!e) {
				         e = "Unable to %s";
					      e1 = "allocate memory for bitmap";
					      hnum = H_AllocPlanes;
					   }
					}
					if (!e) {
				   	e = "Error loading '%s'";
				   	e1 = filename;
				   	hnum = H_Load;
				   }
					unloadbrush(pic->ilbm);
					FreeIFF(pic->ilbm->ParseInfo.iff);
				}
			   if (!e) {
			   	e = "Unable to %s";
					e1 = "AllocIFF";
					hnum = H_AllocIFF;
				}
				FreeMem(pic->ilbm,sizeof(struct ILBMInfo));
			}
			if (!e) {
				e = "Unable to '%s'";
	  	 		e1 = "AllocMem for ILBMInfo";
	  	 		hnum = H_AllocILBM;
   		}
   	}
		if (OkFlag && OpalBase) {
			// If we still have not loaded the image then try opal.library as a last resort
    	   AddMessage("Loading using opal.library");
			Err = LoadImage24(NULL,filename,VIRTUALSCREEN24|FORCE24);
			if (Err < OL_ERR_MAXERR) {
				switch (Err) {
				case OL_ERR_OUTOFMEM:
					e = "Failure - Out of memory - loading '%s'";
					break;
				case OL_ERR_OPENFILE:
					e = "Failure - Opening file - loading '%s'";
					break;
				case OL_ERR_FORMATUNKNOWN:
					e = "Failure - Unknown Format - loading '%s'";
					break;
				case OL_ERR_NOTILBM:
					e = "Failure - Not an ILBM file - loading '%s'";
					break;
				case OL_ERR_FILEREAD:
					e = "Failure - Reading file - loading '%s'";
					break;
				case OL_ERR_BADIFF:
					e = "Failure - Bad IFF Format - loading '%s'";
					break;
				case OL_ERR_CANTCLOSE:
					e = "Failure - Can not Close - loading '%s'";
					break;
				case OL_ERR_BADJPEG:
					e = "Failure - Bad JPEG Format - loading '%s'";
					break;
				case OL_ERR_UNSUPPORTED:
					e = "Failure - Unsupported Format - loading '%s'";
					break;
				case OL_ERR_CTRLC:
				case OL_ERR_NOTHUMBNAIL:
				case OL_ERR_OPENSCREEN:
				case OL_ERR_FILEWRITE:
				default:
					e = "Failure - Unknown problem - loading '%s'";
					break;
				}
				e1 = filename;
				hnum = HE_OpalVision;
			}
			else {
	    	   AddMessage("Allocating Bitmaps");
				pic->OScrn = (struct OpalScreen *)Err;
				// Allocate chunky bit maps
				if ((pic->red = AllocVec(((((pic->OScrn->Width+15)>>4)<<4)*pic->OScrn->Height),0)) &&
		  	       (pic->green = AllocVec(((((pic->OScrn->Width+15)>>4)<<4)*pic->OScrn->Height),0)) &&
	  		       (pic->blue = AllocVec(((((pic->OScrn->Width+15)>>4)<<4)*pic->OScrn->Height),0)) &&
					  	 // Allocate temp bit maps if first image and no OpalVision save
					  	 ((OVFormat) || (plane0) ||
	  	       		  ((plane0 = AllocVec((((width+15)>>4)<<4),MEMF_CHIP)) &&
	  	       		   (plane1 = AllocVec((((width+15)>>4)<<4),MEMF_CHIP)) &&
	  	       		   (plane2 = AllocVec((((width+15)>>4)<<4),MEMF_CHIP)) &&
	  	       		   (plane3 = AllocVec((((width+15)>>4)<<4),MEMF_CHIP)) &&
	  	       		   (plane4 = AllocVec((((width+15)>>4)<<4),MEMF_CHIP)) &&
	  	       		   (plane5 = AllocVec((((width+15)>>4)<<4),MEMF_CHIP)) &&
	  	       		   (plane6 = AllocVec((((width+15)>>4)<<4),MEMF_CHIP)) &&
	  	       		   (plane7 = AllocVec((((width+15)>>4)<<4),MEMF_CHIP))))) {
		    	   AddMessage("Converting Opal to Chunky");
	  		   	OVtoRGB(pic->OScrn,&(pic->red),0,0,((pic->OScrn->Width+15)>>4)<<4,pic->OScrn->Height);
	  	   	}
	  	   	else {
		         e = "Unable to %s";
			      e1 = "allocate memory for bitmap";
			      hnum = H_AllocPlanes;
	  	   	}
				FreeScreen24(pic->OScrn);
				if (!e) {
					return TRUE;
				}
			}
		}
		else {
			if (!e) {
				e = "No opal.library";
				hnum = HE_NoOpal;
			}
		}
	}
	Close24bit(pic);
	// Display an error message if required
	if (GUI)
		Error(e,"Quit",e1,hnum);
	return(FALSE);
}

/* Close an image clearing everything down	*/
void
Close24bit(struct Picture *pic) {
	if (pic) {
		if (pic->rgbonelot) {
			if (pic->red) {
				FreeVec(pic->red);
			}
			pic->red = NULL;
			pic->green = NULL;
			pic->blue = NULL;
		}
		else {
			if (pic->red) {
				FreeVec(pic->red);
				pic->red = NULL;
			}
			if (pic->green) {
				FreeVec(pic->green);
				pic->green = NULL;
			}
			if (pic->blue) {
				FreeVec(pic->blue);
				pic->blue = NULL;
			}
		}
	}
}

/* Display an error message
 * ErrorMessage	: The main text
 * Gadget			: Text for one gadget
 * extra				: more of the text
 * helpnum			: Number of a help node
 *
 * See the TSMorph source code for more of how this actually works
 */
void
Error(char *ErrorMessage,char *Gadget,char *extra,ULONG helpnum) {
   struct EasyStruct EasyStruct = {
      sizeof(struct EasyStruct),
      0,
      NULL,
      NULL,
      NULL
   };
   UBYTE *title = "TSMorph-render Error";
   struct Window *req;
   UBYTE gad[32] = "Help|";
   UBYTE gad1[32] = "_Help|_";
   ULONG ret = CALL_HANDLER;
	struct AmigaGuideMsg *agm;
	ULONG signals;
	struct rtHandlerInfo *rth;
	// Disable the window and display the requester
   DisableWindow();
   if (ReqToolsBase) {
   	if (!handle) {
			strcpy(gad1,"_");
   	}
		strcat(gad1,Gadget);
		while (ret) {
			ret = CALL_HANDLER;
	   	if (rtEZRequestTags(ErrorMessage,gad1,NULL,&extra,
   									RT_ReqHandler, &rth,
   									RT_Window, ProgressWnd,
   									RT_Underscore, '_',
   									RTEZ_ReqTitle, title,
   									TAG_END) == CALL_HANDLER) {
				while (ret == CALL_HANDLER) {
   				if (!rth->DoNotWait) {
   					signals = Wait(rth->WaitMask | ASig);
   				}
					ret = rtReqHandlerA(rth,signals,NULL);
		   		if (ret == 1) {
			   		help(helpnum);
			   	}
		   		if (signals & ASig) {
					  	while (agm = GetAmigaGuideMsg(handle)) {
				 			ReplyAmigaGuideMsg(agm);
				 		}
				 	}
				}
   		}
   		else {
   			ret = 0;
   		}
   	}
   }
   else {
		EasyStruct.es_TextFormat = ErrorMessage;
		EasyStruct.es_Title = title;
	   if (handle) {
   		strcat(gad,Gadget);
	   }
   	else {
   		strcpy(gad,Gadget);
	   }
		EasyStruct.es_GadgetFormat = gad;
		req = BuildEasyRequest(ProgressWnd,&EasyStruct,NULL,extra);
		while (ret) {
			signals  = Wait((1L << req->UserPort->mp_SigBit) | ASig);
			if (signals & (1L << req->UserPort->mp_SigBit)) {
   			ret = SysReqHandler(req,NULL,FALSE);
	   		if (ret == 1) {
		   		help(helpnum);
		   	}
		   }
	   	if (signals & ASig) {
			  	while (agm = GetAmigaGuideMsg(handle)) {
			 		ReplyAmigaGuideMsg(agm);
			 	}
			}
	   }
   	FreeSysRequest(req);
   }
   EnableWindow();
}

/* Open a file
 * Returns	: TRUE if ok
 * filename	: name of file, or NULL for requester
 */
BOOL
MyOpen(char *filename,BOOL JustPoints) {
	char dirname[257];
	char *ifilename=NULL;
	BOOL ok=TRUE;
	BPTR fh;
	char buffer[257];
	LONG w,h;
	LONG xx,x1,yy,y1;
	struct FileRequester *filereq;	// For file requester
	struct MyPoint *MyPoint;
	// If filename supplied then use it otherwise use file requester
	if (filename) {
		ifilename = filename;
	}
	else {
		if (filereq = (struct FileRequest *)AllocFileRequest()) {
			if (AslRequestTags((APTR) filereq,ASL_Hail,(Tag) "TSMorph-render - Pick project",TAG_DONE)) {
				strncpy(dirname,filereq->rf_Dir,256);
				if (AddPart(dirname,filereq->rf_File,256)) {
					ifilename=dirname;
				}
			}
         FreeFileRequest(filereq);
		}
	}
	// If user did not cancel
	if (ifilename) {
		OpenNewArgs(ifilename);		// Load new parameters
		// Open file and skip the WB displayable names
		if (fh=Open(ifilename,MODE_OLDFILE)) {
			FGets(fh,buffer,256);
			if (JustPoints && strcmp(buffer,"TSMorph 2.0\n")) {
				Error("Invalid file format\nLine '%s'","Quit",buffer,H_FileFormat);
				ok = FALSE;
			}
			else {
				if (!JustPoints) {
					Pic1.filename[0]=0;
					Pic2.filename[0]=0;
					// Keep file name
					strcpy(MyFileName,ifilename);
					strcat(MyFileName,".%03ld");
					if (strcmp(buffer,"TSMorph 1.2\n")) {
						Error("Assuming version 1.0 file format","OK",NULL,HE_OldFormat);
					}
					else {
						FGets(fh,buffer,256);
					}
					if (strlen(buffer)) {
						buffer[strlen(buffer)-1]=0;
					}
					strcpy(Pic1.filename,buffer);
					FGets(fh,buffer,256);
					if (strlen(buffer)) {
						buffer[strlen(buffer)-1]=0;
					}
					strcpy(Pic2.filename,buffer);
					// Get file name, set progress and try and load image
					FGets(fh,buffer,256);
					if (strlen(buffer)) {
						buffer[strlen(buffer)-1]=0;
					}
					if (buffer[0]) {
						strcpy(Pic1.filename,buffer);
					}
					FGets(fh,buffer,256);
					if (strlen(buffer)) {
						buffer[strlen(buffer)-1]=0;
					}
					if (buffer[0]) {
						strcpy(Pic2.filename,buffer);
					}
					// Loaded both so get anim name
					FGets(fh,AnimName,256);
					if (strlen(AnimName)) {
						AnimName[strlen(AnimName)-1]=0;
					}
					// Read and validate general information
					FGets(fh,buffer,256);
					if (ok && (sscanf(buffer,"w=%ld,h=%ld,Frames=%ld,Single=%ld,Start=%ld",&w,&h,&Frames,&Single,&Start) != 5)) {
						Error("Invalid file format\nLine '%s'","Quit",buffer,H_FileFormat);
					}
					else {
						width = w;		// ???????
						height = h;    // ???????
						if (ok &&
							((w != width) ||
							 (h != height))) {
							Error("Size does not match image\nLine '%s'","Quit",buffer,H_SizeMatch);
							ok = FALSE;
						}
					}
				}
			}
			if (ok) {
				PointCount = 0;
				// Read and store all the points
				while (ok &&
						FGets(fh,buffer,256) &&
						(sscanf(buffer,"x=%ld,y=%ld,x1=%ld,y1=%ld",&xx,&yy,&x1,&y1) == 4)) {
					if ((xx<0)||(xx>(width-1)) ||
						 (yy<0)||(yy>(height-1))) {
						Error("Point out of range\nLine '%s'","Quit",buffer,H_Range);
						ok = FALSE;
					}
					if (MyPoint = AllocMem(sizeof(struct MyPoint),MEMF_CLEAR)) {
						MyPoint->x = xx;
						MyPoint->y = yy;
						MyPoint->x1 = x1;
						MyPoint->y1 = y1;
						AddTail(&PointList,(struct Node *)MyPoint);
						++PointCount;
					}
					else {
						ok = FALSE;
					}
				}
			}
			if (ok) {	// + 3 should actually be + 1 - but mungwall hits
				if (Points = AllocVec((max(PointCount,23) + 3)*sizeof(struct MyPoint *),MEMF_CLEAR)) {
				}
				else {
					Error("Out of memory for points","Quit",NULL,H_MemPointsR);
					ok = FALSE;
				}
			}
			// Close file and cleanup
			if (!Close(fh)) {
				if (ok) {
					Error("Error closing file '%s'","Quit",filename,H_CloseFile);
					ok = FALSE;
				}
			}
		}
		else {
			Error("Error opening file '%s'","Quit",filename,H_Open);
			ok = FALSE;
		}
	}
	else {
		ok = FALSE;
	}
	return ok;
}

/* Delete all the points from the list	*/
void
DeleteAllPoints(void) {
	struct MyPoint *MyPoint;
	// Loops looking at the head every time and deletes it
	while ((MyPoint =(struct MyPoint *)PointList.lh_Head)->MyNode.mln_Succ) {
		Remove((struct Node *)MyPoint);
		FreeMem(MyPoint,sizeof(struct MyPoint));
	}
	if (Points) {
		FreeVec(Points);
		Points = NULL;
	}
}

// The following is the alogrithm stuff!!!!!
// and is not particularly well commented!

/* Given a point, returns the coordinates in the first and last image	*/
void __regargs
FindPoint(struct MyPoint *RetPoint) {
	UWORD KeepCount;
	struct MyPoint *MyPoint,*MyPoint1,*MyPoint2;
	struct MyPoint **p,**p1,**p2;
	UWORD i1,i2;
	UWORD i,j,k;
	LONG checkdiff;
	BOOL flag;
	for (i = 0;
		  i < (Depth+3);
		  ++i) {
		Points[i] = &BigPoint;
	}
	// Set up differences
	for (MyPoint = (struct MyPoint *)PointList.lh_Head;
			MyPoint->MyNode.mln_Succ;
   		MyPoint = (struct MyPoint *)MyPoint->MyNode.mln_Succ) {
   	if (x < MyPoint->Cx) {
   		MyPoint->xdiff = MyPoint->Cx - x;
	   }
	   else {
	   	MyPoint->xdiff = x - MyPoint->Cx;
	   }
   	if (y < MyPoint->Cy) {
	   	MyPoint->ydiff = MyPoint->Cy - y;
	   }
	   else {
   		MyPoint->ydiff = y - MyPoint->Cy;
   	}
		if ((MyPoint->xdiff + MyPoint->ydiff) < Points[Depth+2]->Cdiff) {
	   	MyPoint->Cdiff = MyPoint->xdiff * MyPoint->xdiff +
								  MyPoint->ydiff * MyPoint->ydiff;
			flag = TRUE;
			for (i = 0;
				  flag && (i < (Depth + 3));
				  ++i) {
				if (Points[i]->Cdiff > MyPoint->Cdiff) {
					MyPoint1 = Points[i];
					Points[i] = MyPoint;
					for (j = i+1;
						  j < (Depth + 3);
						  ++j) {
						MyPoint2 = Points[j];
						Points[j] = MyPoint1;
						MyPoint1 = MyPoint2;
					}
					flag = FALSE;
				}
			}
		}
	}
   // If on an actual point then easy
   if (!Points[0]->Cdiff) {
   	if (AntiAlias) {
			RetPoint->xd = Points[0]->x;
			RetPoint->x1d = Points[0]->x1;
			RetPoint->yd = Points[0]->y;
			RetPoint->y1d = Points[0]->y1;
   	}
   	else {
			RetPoint->x = Points[0]->x;
			RetPoint->x1 = Points[0]->x1;
			RetPoint->y = Points[0]->y;
			RetPoint->y1 = Points[0]->y1;
		}
		return;
   }
   // otherwise try and find a triangle with the other points
  	KeepCount = 0;
  	if (Mode & MODE_CLOSEST) {
		checkdiff = 0x7FFFFFFF;
		for (p = Points, i=0;
			  i<(Depth+1);
			  ++i,++p) {
			if ((*p)->Cdiff < checkdiff) {
				for (p1 = p+1, j=i;
					  j<(Depth+1);
					  ++j,++p1) {
					if (((*p)->Cdiff + (*p1)->Cdiff) < checkdiff) {
						i1 = intersect(*p,*p1);
						for (p2 = p1+1, k=j;
							  k<(Depth+1);
							  ++k,++p2) {
							// loop thru 3 point combinations finding smallest triangle
							if (((*p)->Cdiff + (*p1)->Cdiff + (*p2)->Cdiff) < checkdiff) {
						   	if (i1 + (i2 = intersect(*p1,*p2)) < 2) {
						   		if ((i1 + i2 + intersect(*p2,*p)) == 1) {
							   		KeepCount = 1;
							   		MyPoint = *p;
										MyPoint1 = *p1;
										MyPoint2 = *p2;
										checkdiff = (*p)->Cdiff + (*p1)->Cdiff + (*p2)->Cdiff;
									}
								}
							}
				   	}
			   	}
		   	}
	   	}
  		}
  	}
  	else {
		for (p = Points, i=0;
			  !KeepCount && (i<(Depth+1));
			  ++i,++p) {
			for (p1 = p+1, j=i;
				  !KeepCount && (j<(Depth+1));
				  ++j,++p1) {
				i1 = intersect(*p,*p1);
				for (p2 = p1+1, k=j;
					  !KeepCount && (k<(Depth+1));
					  ++k,++p2) {
					// loop thru 3 point combinations finding smallest triangle
			   	if (i1 + (i2 = intersect(*p1,*p2)) < 2) {
			   		if ((i1 + i2 + intersect(*p2,*p)) == 1) {
				   		MyPoint = *p;
				   		MyPoint1 = *p1;
				   		MyPoint2 = *p2;
				   		KeepCount = 1;
				   	}
			   	}
		   	}
	   	}
  		}
	}
  	// If still no triangle then assume stationary
  	if (!KeepCount && (Mode & MODE_STAT)) {
   	if (AntiAlias) {
			RetPoint->xd = x;
			RetPoint->x1d = x;
			RetPoint->yd = y;
			RetPoint->y1d = y;
   	}
   	else {
	  		RetPoint->x = x;
  			RetPoint->y = y;
  			RetPoint->x1 = x;
	  		RetPoint->y1 = y;
	  	}
  		return;
  	}
  	if (!KeepCount) {
  		MyPoint = Points[0];
  		MyPoint1 = Points[1];
  		MyPoint2 = Points[2];
  	}
	// Otherwise do the triangle calculation
  	Triangle(RetPoint,MyPoint,MyPoint1,MyPoint2);
   // Check the point is actually on the image
  	if (AntiAlias) {
	   if (RetPoint->xd < 0) {
	   	RetPoint->xd = 0;
	   }
	   if (RetPoint->yd < 0) {
	   	RetPoint->yd = 0;
	   }
	   if (RetPoint->x1d < 0) {
	   	RetPoint->x1d = 0;
	   }
	   if (RetPoint->y1d < 0) {
	   	RetPoint->y1d = 0;
	   }
	   if (RetPoint->xd > (width-1)) {
	   	RetPoint->xd = width-1;
	   }
	   if (RetPoint->yd > (height-1)) {
   		RetPoint->yd = height-1;
	   }
	   if (RetPoint->x1d > (width-1)) {
	   	RetPoint->x1d = width-1;
	   }
	   if (RetPoint->y1d > (height-1)) {
	   	RetPoint->y1d = height-1;
	   }
  	}
  	else {
	   if (RetPoint->x < 0) {
	   	RetPoint->x = 0;
	   }
	   if (RetPoint->y < 0) {
	   	RetPoint->y = 0;
	   }
	   if (RetPoint->x1 < 0) {
	   	RetPoint->x1 = 0;
	   }
	   if (RetPoint->y1 < 0) {
	   	RetPoint->y1 = 0;
	   }
	   if (RetPoint->x > (width-1)) {
	   	RetPoint->x = width-1;
	   }
	   if (RetPoint->y > (height-1)) {
   		RetPoint->y = height-1;
	   }
	   if (RetPoint->x1 > (width-1)) {
	   	RetPoint->x1 = width-1;
	   }
	   if (RetPoint->y1 > (height-1)) {
	   	RetPoint->y1 = height-1;
	   }
	}
}

/* Used to determine if a point lies on a line,
 * or in triangle of 3 points
 */
UWORD __regargs
intersect(struct MyPoint *Pointa,struct MyPoint *Pointb) {
// Draws line between a and b and sees if line from x,0 to x,y intersects
// returns 1 if intersects, 0 if not intersect
	if (Pointb->Cx == Pointa->Cx) {								// Vertical a to b
		if ((Pointb->x == x) &&										// x,y on a to b
			 (((Pointb->Cy > y) && (Pointa->Cy < y)) ||
			  ((Pointb->Cy < y) && (Pointa->Cy > y)))) {
			return 1;													// Beteen a and b
		}
		return 0;
	}
	if (((Pointb->Cy > y) && (Pointa->Cy > y)) ||
		 ((Pointb->Cx > x) && (Pointa->Cx > x)) ||
		 ((Pointb->Cx < x) && (Pointa->Cx < x))) {		// Can not possibly cross 
		return 0;
	}
	if ((Pointb->Cy < y) && (Pointa->Cy < y)) {
		return 1;
	}
	if (Pointa->Cx == x) {
		if (Pointa->Cy > y) {
			return 0;
		}
		return 1;
	}
	if (Pointb->Cx == x) {
		if (Pointb->Cy > y) {
			return 0;
		}
		return 1;
	}
	if (Pointb->Cy == Pointa->Cy) {
		if (Pointa->Cy > y) {
			return 0;
		}
		return 1;
	}
	if ((((Pointa->Cx < x)?(x - Pointa->Cx):(Pointa->Cx - x)) * Pointb->Cy +
		  ((Pointb->Cx < x)?(x - Pointb->Cx):(Pointb->Cx - x)) * Pointa->Cy) /
		  ((Pointb->Cx < Pointa->Cx)?(Pointa->Cx - Pointb->Cx):(Pointb->Cx - Pointa->Cx)) > y) {
		return 0;
	}
	return 1;
}

/* Given 3 points (a,b,c) with a point (p) in the triangle
 * determine the coordinates of point (p) on the first and last frame
 */
void __regargs
Triangle(struct MyPoint *p,struct MyPoint *a,struct MyPoint *bb,struct MyPoint *c) {
	double AB1,AB2,BC1,BC2,CA1,CA2;
	double AB,BC,CA;
	double ABBCCA2;
	double ABCA,BCCA,ABBC;
	double BC1BC2,AB1AB2,CA1CA2;
	CalcDiffs(a,bb,&AB1,&AB2,&AB);
	CalcDiffs(bb,c,&BC1,&BC2,&BC);
	CalcDiffs(c,a,&CA1,&CA2,&CA);
	ABBCCA2 = 2 * (AB + BC + CA);
	ABCA = AB+CA;
	BCCA = BC+CA;
	ABBC = AB+BC;
	BC1BC2 = BC1+BC2;
	AB1AB2 = AB1+AB2;
	CA1CA2 = CA1+CA2;
	if (AntiAlias) {
		p->xd = x +
					(ABCA*(BC1*(c->x - c->Cx) + BC2*(bb->x - bb->Cx)) / BC1BC2 +
					 BCCA*(AB1*(bb->x - bb->Cx) + AB2*(a->x - a->Cx)) / AB1AB2 +
					 ABBC*(CA1*(a->x - a->Cx) + CA2*(c->x - c->Cx)) / CA1CA2) /
					ABBCCA2;
		p->x1d = x +
					(ABCA*(BC1*(c->x1 - c->Cx) + BC2*(bb->x1 - bb->Cx)) / BC1BC2 +
					 BCCA*(AB1*(bb->x1 - bb->Cx) + AB2*(a->x1 - a->Cx)) / AB1AB2 +
					 ABBC*(CA1*(a->x1 - a->Cx) + CA2*(c->x1 - c->Cx)) / CA1CA2) /
					ABBCCA2;
		p->y1d = y +
					(ABCA*(BC1*(c->y1 - c->Cy) + BC2*(bb->y1 - bb->Cy)) / BC1BC2 +
					 BCCA*(AB1*(bb->y1 - bb->Cy) + AB2*(a->y1 - a->Cy)) / AB1AB2 +
					 ABBC*(CA1*(a->y1 - a->Cy) + CA2*(c->y1 - c->Cy)) / CA1CA2) /
					ABBCCA2;
		p->yd = y +
					(ABCA*(BC1*(c->y - c->Cy) + BC2*(bb->y - bb->Cy)) / BC1BC2 +
					 BCCA*(AB1*(bb->y - bb->Cy) + AB2*(a->y - a->Cy)) / AB1AB2 +
					 ABBC*(CA1*(a->y - a->Cy) + CA2*(c->y - c->Cy)) / CA1CA2) /
					ABBCCA2;
	}
	else {
		p->x = x +
					(ABCA*(BC1*(c->x - c->Cx) + BC2*(bb->x - bb->Cx)) / BC1BC2 +
					 BCCA*(AB1*(bb->x - bb->Cx) + AB2*(a->x - a->Cx)) / AB1AB2 +
					 ABBC*(CA1*(a->x - a->Cx) + CA2*(c->x - c->Cx)) / CA1CA2) /
					ABBCCA2;
		p->x1 = x +
					(ABCA*(BC1*(c->x1 - c->Cx) + BC2*(bb->x1 - bb->Cx)) / BC1BC2 +
					 BCCA*(AB1*(bb->x1 - bb->Cx) + AB2*(a->x1 - a->Cx)) / AB1AB2 +
					 ABBC*(CA1*(a->x1 - a->Cx) + CA2*(c->x1 - c->Cx)) / CA1CA2) /
					ABBCCA2;
		p->y1 = y +
					(ABCA*(BC1*(c->y1 - c->Cy) + BC2*(bb->y1 - bb->Cy)) / BC1BC2 +
					 BCCA*(AB1*(bb->y1 - bb->Cy) + AB2*(a->y1 - a->Cy)) / AB1AB2 +
					 ABBC*(CA1*(a->y1 - a->Cy) + CA2*(c->y1 - c->Cy)) / CA1CA2) /
					ABBCCA2;
		p->y = y +
					(ABCA*(BC1*(c->y - c->Cy) + BC2*(bb->y - bb->Cy)) / BC1BC2 +
					 BCCA*(AB1*(bb->y - bb->Cy) + AB2*(a->y - a->Cy)) / AB1AB2 +
					 ABBC*(CA1*(a->y - a->Cy) + CA2*(c->y - c->Cy)) / CA1CA2) /
					ABBCCA2;
	}
}

/* Caclulate the distances between various points	*/
void __regargs
CalcDiffs(struct MyPoint *a,struct MyPoint *bb,double *ab1,double *ab2,double *ab) {
	// ab is length x,y to line ab
	// ab1 is length x,y to intersect of ab
	// ab2 is length intersect of ab to bb
	double m;
	double c;		// y = mx+c line a to bb
	double n;
	double d;		// y = nx+d line perp to y=mx+c thru x,y
	double xsolve,ysolve;
	if (a->Cx == bb->Cx) {
		*ab  = a->xdiff;
		*ab1 = a->ydiff;
		*ab2 = bb->ydiff;
	}
	else {
		if (a->Cy == bb->Cy) {
			*ab1 = a->xdiff;
			*ab2 = bb->xdiff;
			*ab  = a->ydiff;
		}
		else {
			m = (double)(bb->Cy - a->Cy) / (double)(bb->Cx - a->Cx);
			c = bb->Cy - (bb->Cx * m);
			n = (-1) / m;
			d = y - n * x;
			xsolve = (d - c)/(m - n);
			ysolve = m * xsolve + c;
			*ab1 = sqrt(((a->Cy - ysolve) * (a->Cy - ysolve) + (a->Cx - xsolve) * (a->Cx - xsolve)));
			*ab2 = sqrt(((bb->Cy - ysolve) * (bb->Cy - ysolve) + (bb->Cx - xsolve) * (bb->Cx - xsolve)));
			*ab  = sqrt(((y - ysolve) * (y - ysolve) + (x - xsolve) * (x - xsolve)));
		}
	}
}

/* Frame clicked - irrelevant	*/
int
FrameClicked(void) {
	return 1;
}


/* Line clicked - irrelevant	*/
int
LineClicked(void) {
	return 1;
}

/* Routine for when Stop clicked	*/
int
StopClicked(void) {
   struct EasyStruct EasyStruct = {
      sizeof(struct EasyStruct),
      0,
      NULL,
      NULL,
      NULL,
   };
	UBYTE *title = "TSMorph-render";
	UBYTE *body = "Really quit?";
   struct Window *req;
   UBYTE *gad;
   ULONG ret = 2;
	struct AmigaGuideMsg *agm;
	ULONG signals;
	struct rtHandlerInfo *rth;
   // Disable the window, display a requester and reenable the window
   DisableWindow();
   if (ReqToolsBase) {
   	if (handle) {
			gad = "_Quit|_Help|_Continue";
   	}
   	else {
			gad = "_Quit|_Continue";
   	}
		while (ret == 2) {
			ret = CALL_HANDLER;
	   	if (rtEZRequestTags(body,gad,NULL,NULL,
   									RT_ReqHandler, &rth,
   									RT_Window, ProgressWnd,
   									RT_Underscore, '_',
   									RTEZ_ReqTitle, title,
   									TAG_END) == CALL_HANDLER) {
				while (ret == CALL_HANDLER) {
   				if (!rth->DoNotWait) {
   					signals = Wait(rth->WaitMask | ASig);
   				}
					ret = rtReqHandlerA(rth,signals,NULL);
		   		if (ret == 2) {
			   		help(H_Really);
			   	}
		   		if (signals & ASig) {
					  	while (agm = GetAmigaGuideMsg(handle)) {
				 			ReplyAmigaGuideMsg(agm);
				 		}
				 	}
				}
   		}
   		else {
   			ret = 0;
   		}
   	}
	}
	else {
		EasyStruct.es_TextFormat = body;
		EasyStruct.es_Title = title;
	   if (handle) {
   		EasyStruct.es_GadgetFormat = "Quit|Help|Continue";
	   }
   	else {
   		EasyStruct.es_GadgetFormat = "Quit|Continue";
	   }
		req = BuildEasyRequest(ProgressWnd,&EasyStruct,NULL,NULL);
	   while ((ret = SysReqHandler(req,NULL,TRUE)) == 2) {
   		help(H_Really);
		  	while (agm = GetAmigaGuideMsg(handle)) {
	 			ReplyAmigaGuideMsg(agm);
	 		}
	   }
   	FreeSysRequest(req);
   }
   EnableWindow();
	return !ret;
}

/* Disables the Progress window
 * Disables the 'Stop' gadget and displays a wait pointer
 */
void
DisableWindow(void) {
	if (ProgressWnd) {
		GT_SetGadgetAttrs(ProgressGadgets[GDX_Stop],ProgressWnd,NULL,GA_Disabled,TRUE,TAG_END);
		SetPointer(ProgressWnd, BusyPointerData, 16, 16, -6, 0);
	}
}

/* Enables the Progress window
 * Enables the 'Stop' gadget and clears the pointer
 */
void
EnableWindow(void) {
	if (ProgressWnd) {
		GT_SetGadgetAttrs(ProgressGadgets[GDX_Stop],ProgressWnd,NULL,GA_Disabled,FALSE,TAG_END);
		ClearPointer(ProgressWnd);
	}
}

/* Tries to execute a Rexx script
 * msgtxt = name of script
 * IgnoreError = TRUE then do not display error message
 * Returns:	error message
 */
LONG
SendRxMsg(char *msgtxt,BOOL IgnoreError) {
	struct MsgPort *reply_port;
	struct MsgPort *rx_port;
	struct RexxMsg *rx_msg;
	LONG 				ret 			= RC_FATAL;

	if (reply_port = CreateMsgPort()) {
   	if (rx_msg = CreateRexxMsg(reply_port,"TSM","REXX")) {
      	rx_msg->rm_Args[0] = msgtxt;
			if (FillRexxMsg(rx_msg,1,0)) {
				rx_msg->rm_Action = RXCOMM;
        		Forbid();
        		if (rx_port = (struct MsgPort *)FindPort("REXX")) {
          		PutMsg(rx_port, (struct Message *)rx_msg);
          		Permit();
          		WaitPort(reply_port);
          		ReplyMsg(GetMsg(reply_port));
          		ret = rx_msg->rm_Result1;
		     	}
        		else {
          		Permit();
        		}
        		ClearRexxMsg(rx_msg, 1);
      	}
     		DeleteRexxMsg(rx_msg);
    	}
    	DeleteMsgPort(reply_port);
  	}
	// Display an error message (if required)
  	if (!IgnoreError && ret) {
  		Error("Error sending ARexx message\n'%s'","Quit",msgtxt,H_ARexx);
  	}
  	return ret;
}

/* Handle 'Help' key	*/
int
ProgressRawKey(void) {
	switch (ProgressMsg.Code) {
	case 0x5f:
		help(H_Help);
	}
	return 1;
}

// Load some image(s) for a frame
BOOL
LoadFrames(BOOL points,BOOL image1,BOOL image2) {
	char buffer[257];
	BOOL ok=TRUE;
	if ((Single == 2) || (Single == 3)) {
		if (points) {
			AddMessage("Loading points");
   	 	DeleteAllPoints();
			sprintf(buffer,MyFileName,f+Start-1);
			ok = MyOpen(buffer,TRUE);
		}
		sprintf(buffer,Pic1.filename,f+Start-1);
	}
	else {
		strcpy(buffer,Pic1.filename);
	}
	if (ok) {
		if (image1) {
			ok = Pic1_Open = Load24bit(buffer,&Pic1,TRUE);
		}
		if (image2 && ok && ((Single == 0) || (Single == 2))) {
			// If we loaded the 1st image then try the second
			if (Single == 2) {
				sprintf(buffer,Pic2.filename,f+Start-1);
			}
			else {
				strcpy(buffer,Pic2.filename);
			}
			ok = Pic2_Open = Load24bit(buffer,&Pic2,TRUE);
		}
	}
	return ok;
}

static UWORD top = 0;

// add a message to the progress list
void
AddMessage(UBYTE *message) {
	struct Node *node;
	if ((node = (struct Node *)malloc(sizeof(struct Node))) &&
		 (node->ln_Name = strdup(message))) {;
		GT_SetGadgetAttrs(ProgressGadgets[GDX_Info],ProgressWnd,NULL,
    						 GTLV_Labels, ~0,
   						 TAG_END);
		AddTail(&InfoList,node);
		GT_SetGadgetAttrs(ProgressGadgets[GDX_Info],ProgressWnd,NULL,
    						 GTLV_Labels, &InfoList,
    						 GTLV_Top, top,
   						 TAG_END);
  		top++;
	}
}
