// 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

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

// Include correct math library
#ifdef MY68881
#include <m68881.h>
extern int rand(void);
#else
#ifdef MY68881_6
#include <math.h>
#include <m68881.h>
#else
#include <math.h>
#endif
#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/intuitionbase.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 <clib/alib_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 <libraries/nofrag.h>
#include <clib/nofrag_protos.h>
#ifdef LIBRARIES_NOFRAG_H
#undef LIBRARIES_NOFRAG_H
#endif
#include <pragmas/nofrag_pragmas.h>
#ifndef LIBRARIES_NOFRAG_H
#define LIBRARIES_NOFRAG_H 1
#endif

#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

/* EGS Stuff	*/
#include <egs/clib/egs_protos.h>
#include <egs/pragmas/egs_pragmas.h>

#include <egs/clib/egsintui_protos.h>
#include <egs/pragmas/egsintui_pragmas.h>

#include <egs/clib/egsgfx_protos.h>
#include <egs/pragmas/egsgfx_pragmas.h>

#include <egs/egsintui.h>

extern struct Library *EGSIntuiBase	= NULL;
extern struct Library *EGSGfxBase	= NULL;
extern struct Library *EGSBase		= NULL;

// 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); }
int __regargs __chkabort(void) { return(0); }

// Libraries
extern struct ExecBase *SysBase;
extern struct DosLibrary *DOSBase;
struct IntuitionBase *IntuitionBase;
struct Library *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 NoFragBase *NoFragBase = NULL;

struct OpalBase *OpalBase = NULL;

extern struct List InfoList={0}; // List of messages

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

/* Version string for CLI version */
#ifdef MY68881
char *Version = "$VER: TSMorph-render_881 3.2 (12.3.95)";
#else
#ifdef _M68040
char *Version = "$VER: TSMorph-render_040 3.2 (12.3.95)";
#else
#ifdef MY68881_6
char *Version = "$VER: TSMorph-render_881 3.2 (12.3.95)";
#else
char *Version = "$VER: TSMorph-render 3.2 (12.3.95)";
#endif
#endif
#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 };

struct EI_NewWindow	EGS_NewWindow = {0};	// EGS New window
struct EI_Window		*EGS_Win=NULL;			// EGS Window
struct E_EBitMap		*EGS_BitMap=NULL;

// Protos
BOOL SaveFile(void);
BOOL Load24bit(char *filename,struct Picture *pic,BOOL GUI);
void Close24bit(struct Picture *pic);
void DeleteAllPoints(void);
static void __regargs FindPoint(struct MyPoint *RetPoint,BOOL Everything);
BOOL MyOpen(char *filename,BOOL JustPoints);
static UWORD __regargs intersect(struct MyPoint *Pointa,struct MyPoint *Pointb);
static void __regargs CalcDiffs(struct MyPoint *a,struct MyPoint *bb,double *ab1,double *ab2,double *ab);
static void __regargs Triangle(struct MyPoint *p,struct MyPoint *a,struct MyPoint *bb,struct MyPoint *c);
static void __regargs Triangle_I(struct MyPoint *p,struct MyPoint *a,struct MyPoint *bb,struct MyPoint *c);
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 help(ULONG hnum);
BOOL LoadFrames(BOOL points,BOOL image1,BOOL image2);
void CopyEGS(int line, UBYTE **er,UBYTE **eg,UBYTE **eb);
int cmpX(struct MyPoint **A,struct MyPoint **B);
int cmpY(struct MyPoint **A,struct MyPoint **B);
int cmpDiff(struct MyPoint **A,struct MyPoint **B);
BOOL	GenerateTriangles(void);
void	FreeTriangles(void);
int   *IntVect(int ncols);
void  MyFreeVecti(int *vectptr);
int   **IntMatrix(int nrows,int ncols);
void  FreeMatrixi(int **matptr);
double **DoubleMatrix(int nrows,int ncols);
void  FreeMatrixd(double **matptr);
BOOL AddTri(int aa,int bb,int cc);
void AddMessage(UBYTE *message);

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

// RenderSub.c
void DisableWindow(void);
void EnableWindow(void);
APTR MyAllocVec(ULONG size,ULONG requirements);
void MyFreeVec(APTR memptr);
APTR MyAllocMem(ULONG size,ULONG requirements);
void MyFreeMem(APTR memptr,ULONG size);
APTR Mymalloc(ULONG size);
APTR Mystrdup(UBYTE *str);
void Error(char *ErrorMessage, char *Gadget,char *extra,ULONG hnum);
LONG SendRxMsg(char *msgtxt,BOOL IgnoreError);

// some work buffers
extern char buffer[256]="";
extern 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

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

// nofrag.library stuff
extern struct MemoryChain *Chain = NULL;
#define MAXMEM 1000

// 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
	BOOL Used;		// Is it being used?
	struct List TList;	// List of triangles;
};

struct Triangle {
	struct MinNode TNode;	// Linked into list
	struct MyPoint *Point1;
	struct MyPoint *Point2;
};

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

// List of points
struct List			PointList;

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

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

char *Loadscript;		// ARexx load script

extern WORD x=0,y=0;				// Current coords

struct MyPoint **Points = NULL;	// Pointer to points
struct MyPoint **PointsAlloc = NULL;	// The actual allocated pointer
struct MyPoint **PointsX = NULL;	// Pointer to points horizontal
struct MyPoint **PointsY = NULL;	// Pointer to points vertical

WORD	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
					// Mode = 4 Binary search ,5,6,7 as above
					// Mode = 8 Delaunay Triangle algorithm
					// Mode = 16 Only calculate points once


								// SET/UNSET
#define MODE_STAT		1	// Stationary if no triangle/Use 3 points anyway
#define MODE_CLOSEST	2	// Choose 3 closest/Choose 1st 3 found
#define MODE_SEARCH	4	// Binary Search/Look at all points
#define MODE_DELAU	8	// Delaunay Triangle/Normal mode (sets DEPTH to 0)
#define MODE_ONCE		16	// Only calculate once

LONG PointCount = 0;			// Number of points

struct MyPoint BigPoint;	// Point far away

char MyFileName[257];

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

// OpalVision screen
extern struct OpalScreen *OScrn = NULL;

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

// Table of parameters
extern 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 = {0};

#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);
	}
}

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

extern char 					*Postscript=NULL;// ARexx postscript
BOOL					Integer = FALSE;

/* Main program	*/
int
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
 UBYTE				*er,*eg,*eb;// EGS pointers

 // Set up BigPoint
 BigPoint.Cdiff = 0x7FFFFFFF;
 // Initialise points list
 NewList(&PointList);
 // Try and open reqtools.library and dctv.library and nofrag.library
 ReqToolsBase = OpenLibrary("reqtools.library",38);
 DCTVBase = OpenLibrary("dctv.library",3);
 if (NoFragBase = (struct NoFragBase *) OpenLibrary("nofrag.library",2L)) {
  if (!(Chain = GetMemoryChain(MAXMEM+20))) {
   CloseLibrary((struct Library *)NoFragBase);
   NoFragBase = NULL;
  }
 }
 // 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 = (struct 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);
            if (MatchToolValue(MyArgString("EGS","NO",FALSE),"YES")) {
					if ((EGSBase = OpenLibrary("egs.library",0)) &&
					  	 (EGSGfxBase = OpenLibrary("egsgfx.library", 0)) &&
					    (EGSIntuiBase = OpenLibrary("egsintui.library", 0))) {
						EGS = TRUE;
					}
				}
            if (MatchToolValue(MyArgString("REQTOOLS","YES",FALSE),"NO")) {
             if (ReqToolsBase) {
              CloseLibrary(ReqToolsBase);
              ReqToolsBase = NULL;
             }
            }
            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 = min(MyArgInt("DEPTH",2,FALSE),20);	Set in Open()
//            Mode = MyArgInt("MODE",0,FALSE);
              CreateIcons = MatchToolValue(MyArgString("CREATEICONSR","NO",FALSE),"YES");
              AntiAlias = MatchToolValue(MyArgString("ANTIALIAS","NO",FALSE),"YES");
              Integer = MatchToolValue(MyArgString("INTEGER","NO",FALSE),"YES");
              if (Integer) {
               AntiAlias = FALSE;
              }
        	     // 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),MEMF_CLEAR)) &&
  	       	 	   (GREEN = AllocVec((swidth*height),MEMF_CLEAR)) &&
  	       	 	   (BLUE =  AllocVec((swidth*height),MEMF_CLEAR)) &&
  	       	 	   ((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;
         	    	}
        	    	  }
					 }
					}
					if ((Mode & MODE_ONCE) && OkFlag) {
    	    	     AddMessage("Precalculating points");
    	    	     Points = PointsAlloc;
					  for (MyPointp = (struct MyPoint *)PointList.lh_Head;
							MyPointp->MyNode.mln_Succ;
				   		MyPointp = (struct MyPoint *)MyPointp->MyNode.mln_Succ) {
		   		    MyPointp->Cx = (MyPointp->x1+MyPointp->x)>>1;
			   	    MyPointp->Cy = (MyPointp->y1+MyPointp->y)>>1;
			   	  }
        		  	  for (y=0;
        	  	      ((y<height) && OkFlag);
        	  	      y++) {
   	     	     	// Loop thru columns
      	     	   for (x=0;
       	           x < width;
        	           x++) {
        	          // Determine coordinates on end image(s)
        	          FindPoint(&MyPoint,FALSE);
        	          Points += (Depth+4);
      		  	    if (DX) {
      		  	     x++;
      		  	     for (myx = 0;
      		  	     		 (myx < DX) && (x < width);
      		  	     		 x++,myx++) {
	        	         Points += (Depth+4);
      		  	     }
      		  	     x--;
      		  	    }
        	     	   }
        	     	   // end of line, check 'Stop' and 'Help'
        	     	   OkFlag = HandleProgressIDCMP();
      	         if (handle) {
	      	       while (agm = GetAmigaGuideMsg(handle)) {
   	   		     ReplyAmigaGuideMsg(agm);
      		       }
      		      }
        	     	   if (DY) {
        	     	    y++;
        		  	    for (myy = 0;
        	  	            (myy < DY) && (y<height);
        	  	            y++, myy++) {
        	           Points += ((Depth+4)*width);
        	  	       }
        	  	       y--;
      		      }
      		     }
        	    	  // not ok - already displayed an error
        	    	  if (!OkFlag) {
        	    	   e = (char *)-1;
         	     }
         	     else {
					   if (Mode & MODE_DELAU) {
   	    	       AddMessage("Calculating triangles");
					    OkFlag = GenerateTriangles();
					    if (!OkFlag) {
                    Error("Out of memory for points","Quit",NULL,H_MemPointsR);
					    }
					   }
					  }
					}
     	    	   // 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;
        	    	  }
        	    	 }
        	    	 else {	// Version 2.4 - Open frame 2 if not yet open
        	    	  if (OkFlag && Do && !Pic2_Open && (Single == 0)) {
        	    	   OkFlag = LoadFrames(FALSE,FALSE,TRUE);
        	    	  }
        	    	 }
					 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.%02ld Seconds for last image",time2.tv_secs,time2.tv_micro/10000);
   	    	      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;
		      	   }
		      	  }
					  if (Mode & MODE_SEARCH) {
   	    	      AddMessage("Sorting points");
						qsort((char *)PointsX,PointCount,sizeof(struct MyPoint *),cmpX);
						qsort((char *)PointsY,PointCount,sizeof(struct MyPoint *),cmpY);
					  }
					  if ((Mode & MODE_DELAU) && (!MODE_ONCE)) {
   	    	      AddMessage("Calculating triangles");
					   OkFlag = GenerateTriangles();
					   if (!OkFlag) {
                   Error("Out of memory for points","Quit",NULL,H_MemPointsR);
					   }
					  }
		      	  // Initialise chunky pointers
	        	     er = arrayr=RED;
   	     	     eg = arrayg=GREEN;
      	  	     eb = arrayb=BLUE;
      	  	     Points = PointsAlloc;
      	  	     // 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,TRUE);
        	          if (Mode & MODE_ONCE) {
	        	          Points += (Depth+4);
	        	       }
        	          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++) {
        	  	         if (Mode & MODE_ONCE) {
		        	         Points += (Depth+4);
		        	      }
 		        	      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 (EGS_Win) {
        	     	   	CopyEGS(y,&er,&eg,&eb);
        	     	   }
        	     	   // 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;
	        	     	  if (EGS_Win) {
   		     	        CopyEGS(y,&er,&eg,&eb);
        		     	  }
        	  	        if (Mode & MODE_ONCE) {
	        	           Points += ((Depth+4)*width);
	        	        }
        	  	       }
        	  	       y--;
        	  	      }
        	    	  }
        	    	  if ((Mode & MODE_DELAU) && (!MODE_ONCE)) {
					   FreeTriangles();
                 }
        	    	  // 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");
     	    	  if ((Mode & MODE_DELAU) && (MODE_ONCE)) {
				   FreeTriangles();
              }
  	    	     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) MyFreeVec(plane0);
			    if (plane1) MyFreeVec(plane1);
			    if (plane2) MyFreeVec(plane2);
			    if (plane3) MyFreeVec(plane3);
			    if (plane4) MyFreeVec(plane4);
			    if (plane5) MyFreeVec(plane5);
			    if (plane6) MyFreeVec(plane6);
			    if (plane7) MyFreeVec(plane7);
			    // reset requesters
	          process->pr_WindowPtr = oldWindowPtr;
	         }
	         else {
	          e = "Error opening progress window";
	          e1 = NULL;
	          hnum = H_Progress;
	         }
	         // close down everything
	         CloseProgressWindow();
	         CloseDownScreen();
			   MyArgArrayDone();
            if (EGS) {
					if (EGS_Win) {
						EI_CloseWindow(EGS_Win);
					}
					if (EGS_BitMap) {
						E_DisposeBitMap(EGS_BitMap);
					}
				}
				if (EGSIntuiBase) {
				 CloseLibrary(EGSIntuiBase);
				}
				if (EGSGfxBase) {
				 CloseLibrary(EGSGfxBase);
				}
				if (EGSBase) {
				 CloseLibrary(EGSBase);
				}
            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 (NoFragBase) {
  FreeMemoryChain(Chain,TRUE);
  CloseLibrary((struct Library *)NoFragBase);
 }
 if (DCTVBase) {
  CloseLibrary(DCTVBase);
 }
 if (ReqToolsBase) {
  CloseLibrary(ReqToolsBase);
 }
 if (IntuitionBase) {
  CloseLibrary((struct Library *)IntuitionBase);
 }
 return 0;
}

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;
	struct DCTVCvtHandle	*chandle;	// DCTV conversion stuff
	UWORD					*DCTVcolors;	// 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 = MyAllocVec((((width+15)>>4)<<4),MEMF_CHIP|MEMF_CLEAR)) &&
	  	       		   (plane1 = MyAllocVec((((width+15)>>4)<<4),MEMF_CHIP|MEMF_CLEAR)) &&
	  	       		   (plane2 = MyAllocVec((((width+15)>>4)<<4),MEMF_CHIP|MEMF_CLEAR)) &&
	  	       		   (plane3 = MyAllocVec((((width+15)>>4)<<4),MEMF_CHIP|MEMF_CLEAR)) &&
	  	       		   (plane4 = MyAllocVec((((width+15)>>4)<<4),MEMF_CHIP|MEMF_CLEAR)) &&
	  	       		   (plane5 = MyAllocVec((((width+15)>>4)<<4),MEMF_CHIP|MEMF_CLEAR)) &&
	  	       		   (plane6 = MyAllocVec((((width+15)>>4)<<4),MEMF_CHIP|MEMF_CLEAR)) &&
	  	       		   (plane7 = MyAllocVec((((width+15)>>4)<<4),MEMF_CHIP|MEMF_CLEAR)))) {
	  	     		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 *)MyAllocMem(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),MEMF_CLEAR)) &&
				  	       (pic->green = AllocVec(((((pic->ilbm->Bmhd.w+15)>>4)<<4)*pic->ilbm->Bmhd.h),MEMF_CLEAR)) &&
				  	       (pic->blue = AllocVec(((((pic->ilbm->Bmhd.w+15)>>4)<<4)*pic->ilbm->Bmhd.h),MEMF_CLEAR)) &&
						  	 // Allocate temp bit maps if first image and no OpalVision save
						  	 ((OVFormat && (pic->ilbm->Bmhd.nPlanes != 24)) || (plane0) ||
  			       		  ((plane0 = MyAllocVec((((width+15)>>4)<<4),MEMF_CHIP|MEMF_CLEAR)) &&
  	   		    		   (plane1 = MyAllocVec((((width+15)>>4)<<4),MEMF_CHIP|MEMF_CLEAR)) &&
  	      		 		   (plane2 = MyAllocVec((((width+15)>>4)<<4),MEMF_CHIP|MEMF_CLEAR)) &&
  	       				   (plane3 = MyAllocVec((((width+15)>>4)<<4),MEMF_CHIP|MEMF_CLEAR)) &&
  	       				   (plane4 = MyAllocVec((((width+15)>>4)<<4),MEMF_CHIP|MEMF_CLEAR)) &&
  	       		   		(plane5 = MyAllocVec((((width+15)>>4)<<4),MEMF_CHIP|MEMF_CLEAR)) &&
	  	       		   	(plane6 = MyAllocVec((((width+15)>>4)<<4),MEMF_CHIP|MEMF_CLEAR)) &&
		  	       		   (plane7 = MyAllocVec((((width+15)>>4)<<4),MEMF_CHIP|MEMF_CLEAR))))) {
							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);
								MyFreeMem(pic->ilbm,sizeof(struct ILBMInfo));
						      return TRUE;
						   }
						   else {
								if (DCTVBase && TestDCTVSignature(pic->ilbm->brbitmap)) {
									// Convert DCTV to chunky
									if (DCTVcolors = MyAllocVec(2L<<pic->ilbm->Bmhd.nPlanes,MEMF_CLEAR)) {
										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);
												}
											}
											MyFreeVec(DCTVcolors);
											FreeDCTVCvt(chandle);
					            	 	// Unload everything and exit cleanly
											unloadbrush(pic->ilbm);
											FreeIFF(pic->ilbm->ParseInfo.iff);
											MyFreeMem(pic->ilbm,sizeof(struct ILBMInfo));
											return TRUE;
										}
										MyFreeVec(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);
									MyFreeMem(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;
				}
				MyFreeMem(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),MEMF_CLEAR)) &&
		  	       (pic->green = AllocVec(((((pic->OScrn->Width+15)>>4)<<4)*pic->OScrn->Height),MEMF_CLEAR)) &&
	  		       (pic->blue = AllocVec(((((pic->OScrn->Width+15)>>4)<<4)*pic->OScrn->Height),MEMF_CLEAR)) &&
					  	 // Allocate temp bit maps if first image and no OpalVision save
					  	 ((OVFormat) || (plane0) ||
	  	       		  ((plane0 = MyAllocVec((((width+15)>>4)<<4),MEMF_CHIP|MEMF_CLEAR)) &&
	  	       		   (plane1 = MyAllocVec((((width+15)>>4)<<4),MEMF_CHIP|MEMF_CLEAR)) &&
	  	       		   (plane2 = MyAllocVec((((width+15)>>4)<<4),MEMF_CHIP|MEMF_CLEAR)) &&
	  	       		   (plane3 = MyAllocVec((((width+15)>>4)<<4),MEMF_CHIP|MEMF_CLEAR)) &&
	  	       		   (plane4 = MyAllocVec((((width+15)>>4)<<4),MEMF_CHIP|MEMF_CLEAR)) &&
	  	       		   (plane5 = MyAllocVec((((width+15)>>4)<<4),MEMF_CHIP|MEMF_CLEAR)) &&
	  	       		   (plane6 = MyAllocVec((((width+15)>>4)<<4),MEMF_CHIP|MEMF_CLEAR)) &&
	  	       		   (plane7 = MyAllocVec((((width+15)>>4)<<4),MEMF_CHIP|MEMF_CLEAR))))) {
		    	   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;
			}
		}
	}
}

/* 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 rtfilename[108] = "\0";	// reqtools filename from requester
	char *ifilename=NULL;
	BOOL ok=TRUE;
	BPTR fh;
	char buffer[257];
	LONG w,h;
	LONG xx,x1,yy,y1;
	UWORD				i;				// General index

	struct FileRequester *filereq;	// For file requester
	struct rtFileRequester	*rtfilereq;
	struct MyPoint *MyPoint;
	// If filename supplied then use it otherwise use file requester
	if (filename) {
		ifilename = filename;
	}
	else {
		if (MatchToolValue(MyArgString("REQTOOLS","YES",FALSE),"ALL") &&
			 (rtfilereq = (struct rtFileRequester *)rtAllocRequestA(RT_FILEREQ,NULL))) {
			if (rtFileRequestA(rtfilereq,rtfilename,"TSMorph-render - Pick project",NULL)) {
				strncpy(dirname,rtfilereq->Dir,256);
				if (AddPart(dirname,rtfilename,256)) {
					ifilename=dirname;
				}
		   }
			rtFreeRequest(rtfilereq);
		}
		else {
			if (filereq = (struct FileRequester *)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 = MyAllocMem(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;
					}
				}
			}
         Depth = MyArgInt("DEPTH",2,FALSE);
         Depth = min(Depth,20);
         Mode = MyArgInt("MODE",0,FALSE);
         if ((Single == 2) || (Single == 3)) {
         	Mode &= ~MODE_ONCE;						// Irrelevant for anims
         }
			if (ok) {
//				if ((PointsAlloc = MyAllocVec((max(PointCount,23) + 3)*sizeof(struct MyPoint *) *
				if ((PointsAlloc = MyAllocVec((Depth + 4)*sizeof(struct MyPoint *) *		// + 1 = NumPoints
										((Mode & MODE_ONCE)?width*height:1),MEMF_CLEAR)) &&
					 (PointsX = MyAllocVec((max(PointCount,23) + 3)*sizeof(struct MyPoint *),MEMF_CLEAR)) &&
					 (PointsY = MyAllocVec((max(PointCount,23) + 3)*sizeof(struct MyPoint *),MEMF_CLEAR))) {
					// Set up x and y order
					Points = PointsAlloc;
					i = 0;
				   for (MyPoint = (struct MyPoint *)PointList.lh_Head;
						  MyPoint->MyNode.mln_Succ;
			   		  MyPoint = (struct MyPoint *)MyPoint->MyNode.mln_Succ) {
						PointsX[i] = MyPoint;
						PointsY[i] = MyPoint;
						++i;
					}
				}
				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);
		MyFreeMem(MyPoint,sizeof(struct MyPoint));
	}
	if (PointsAlloc) {
		MyFreeVec(PointsAlloc);
		PointsAlloc = NULL;
	}
	if (PointsX) {
		MyFreeVec(PointsX);
		PointsX = NULL;
	}
	if (PointsY) {
		MyFreeVec(PointsY);
		PointsY = 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	*/
static void __regargs
FindPoint(struct MyPoint *RetPoint,BOOL Everything) {
	UWORD KeepCount;
	struct MyPoint *MyPoint,*MyPoint1,*MyPoint2;
	struct MyPoint **p,**p1,**p2,**p3;
	UWORD i1,i2;
	UWORD i,j,k;
	LONG checkdiff;
	BOOL flag;
	UWORD NumPoints;
	UWORD add;
	struct Triangle *T;
	UWORD				MinX,MaxX,CurX;	//	Pointers to X array
	UWORD				MinY,MaxY,CurY;	//	Pointers to Y array
	int l;

	if (!(Mode & MODE_ONCE) || !Everything) {
		if (Mode & MODE_SEARCH) {
			p = PointsX;
			for (i = 0;
				  i < PointCount;
				  ++i) {
				(*p)->Used = FALSE;
				++p;
			}
			flag = FALSE;
			CurX = PointCount>>1;
			add = CurX>>1;
			if (!add) {
				add = 1;
			}
			p = &(PointsX[CurX]);
			p2 = &(PointsX[PointCount -1]);
			while (!flag) {
				if ((*p)->Cx == x) {
					flag = TRUE;
					MinX = CurX-1;
					MaxX = CurX+1;
				}
				else {
					if ((*p)->Cx < x) {
						++p;
						if ((*p)->Cx > x) {
							flag = TRUE;
							MinX = CurX;
							MaxX = CurX+1;
						}
						else {
							CurX += add;
							p += (add-1);
							if (p == p2) {
								flag = TRUE;
								MinX = CurX-1;
								MaxX = CurX;
							}
						}
					}
					else {
						CurX -= add;
						if (!CurX) {
							flag = TRUE;
							MinX = 0;
							MaxX = 1;
						}
						else {
							p -= add;
						}
					}
				}
				if (!flag) {
					add >>= 1;
					if (!add) {
						add = 1;
					}
				}
			}
			flag = FALSE;
			CurY = PointCount>>1;
			add = CurY>>1;
			if (!add) {
				add = 1;
			}
			p = &(PointsY[CurY]);
			p2 = &(PointsY[PointCount -1]);
			while (!flag) {
				if ((*p)->Cy == y) {
					flag = TRUE;
					MinY = CurY-1;
					MaxY = CurY+1;
				}
				else {
					if ((*p)->Cy < y) {
						++p;
						if ((*p)->Cy > y) {
							flag = TRUE;
							MinY = CurY;
							MaxY = CurY+1;
						}
						else {
							CurY += add;
							p += (add-1);
							if (p == p2) {
								flag = TRUE;
								MinY = CurY-1;
								MaxY = CurY;
							}
						}
					}
					else {
						CurY -= add;
						if (!CurY) {
							flag = TRUE;
							MinY = 0;
							MaxY = 1;
						}
						else {
							p -= add;
						}
					}
				}
				if (!flag) {
					add >>= 1;
					if (!add) {
						add = 1;
					}
				}
			}
			NumPoints = 0;	// Count of points found so far
			while ((NumPoints < (Depth+3)) && (NumPoints < PointCount)) {
				p = &(PointsX[MinX]);
				for (i = MinX;
					  (i <= MaxX) && (NumPoints < (Depth+3));
					  ++i,++p) {
					if (!(*p)->Used) {
						flag = TRUE;
						MyPoint = *p;
						p1 = &(PointsY[MinY]);
						for (j = MinY;
							  (j <= MaxY) && flag;
							  ++j,++p1) {
							if (MyPoint == *p1) {
								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;
						   	}
						   	MyPoint->Cdiff = MyPoint->xdiff * MyPoint->xdiff +
													  MyPoint->ydiff * MyPoint->ydiff;
								flag = TRUE;
								p2 = Points;
								for (k = 0;
									  flag && (k < NumPoints);
									  ++k,++p2) {
									if ((*p2)->Cdiff > MyPoint->Cdiff) {
										MyPoint1 = *p2;
										*p2 = MyPoint;
										p3 = p2;
										++p3;
										for (l = k+1;
											  l < NumPoints;
											  ++l,++p3) {
											MyPoint2 = *p3;
											*p3 = MyPoint1;
											MyPoint1 = MyPoint2;
										}
										flag = FALSE;
									}
								}
								if (flag) {
									*p2 = MyPoint;
								}
								MyPoint->Used = TRUE;
								NumPoints++;
								flag = FALSE;
							}
						}
					}
				}
				if (MinX > 0) {
					--MinX;
				}
				if (MinY > 0) {
					--MinY;
				}
				if (MaxX < (PointCount -1)) {
					++MaxX;
				}
				if (MaxY < (PointCount -1)) {
					++MaxY;
				}
			}
		}
		else {
			NumPoints = Depth+3;
			p = Points;
			for (i = 0;
				  i < NumPoints;
				  ++i) {
				*(p++) = &BigPoint;
			}
			// Set up differences
			p2 = &(Points[Depth+2]);
			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) < (*p2)->Cdiff) {
			   	MyPoint->Cdiff = MyPoint->xdiff * MyPoint->xdiff +
										  MyPoint->ydiff * MyPoint->ydiff;
					flag = TRUE;
					p = Points;
					for (i = 0;
						  flag && (i < NumPoints);
						  ++i,++p) {
						if ((*p)->Cdiff > MyPoint->Cdiff) {
							MyPoint1 = *p;
							*p = MyPoint;
							p1 = p;
							++p1;
							for (j = i+1;
								  j < (Depth + 3);
								  ++j,++p1) {
								MyPoint2 = *p1;
								*p1 = MyPoint1;
								MyPoint1 = MyPoint2;
							}
							flag = FALSE;
						}
					}
				}
			}
		}
		if (!Everything) {
			Points[Depth+3] = (struct MyPoint *)NumPoints;
			return;
		}
	}
	else {
		p = Points;
		NumPoints = (ULONG)Points[Depth+3];
		for (i = 0;
				((i < Depth+3) && *p);
				++i,++p) {
			MyPoint = *p;
	   	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;
	  		}
	   	MyPoint->Cdiff = MyPoint->xdiff * MyPoint->xdiff +
								  MyPoint->ydiff * MyPoint->ydiff;
		}
	}
   // 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
	if (!Depth && !(Mode & (MODE_STAT | MODE_DELAU))) {
		// Special case, only have 3 points
		MyPoint = Points[0];
		MyPoint1 = Points[1];
		MyPoint2 = Points[2];
	}
	else {
		KeepCount = 0;
	   if (Mode & MODE_DELAU) {
	   	MyPoint = Points[0];
			for (T = (struct Triangle *)MyPoint->TList.lh_Head;
					T->TNode.mln_Succ && !KeepCount;
	   			T = (struct Triangle *)T->TNode.mln_Succ) {
				i1 = intersect(MyPoint,T->Point1);
		   	if (i1 + (i2 = intersect(T->Point1,T->Point2)) < 2) {
					if ((i1 + i2 + intersect(T->Point2,MyPoint)) == 1) {
						MyPoint1 = T->Point1;
						MyPoint2 = T->Point2;
						KeepCount = 1;
					}
				}
	   	}
	   }
	   else {
		  	if (Mode & MODE_CLOSEST) {
				checkdiff = 0x7FFFFFFF;
				for (p = Points, i=0;
					  i<(NumPoints-2);
					  ++i,++p) {
					if ((*p)->Cdiff < checkdiff) {
						for (p1 = p+1, j=i;
							  j<(NumPoints-2);
							  ++j,++p1) {
							if (((*p)->Cdiff + (*p1)->Cdiff) < checkdiff) {
								i1 = intersect(*p,*p1);
								for (p2 = p1+1, k=j;
									  k<(NumPoints-2);
									  ++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<(NumPoints-2));
					  ++i,++p) {
					for (p1 = p+1, j=i;
						  !KeepCount && (j<(NumPoints-2));
						  ++j,++p1) {
						i1 = intersect(*p,*p1);
						for (p2 = p1+1, k=j;
							  !KeepCount && (k<(NumPoints-2));
							  ++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
	if (Integer) {
		Triangle_I(RetPoint,MyPoint,MyPoint1,MyPoint2);
	}
	else {
		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
 */
static UWORD __regargs
#ifdef __SASC_60
__inline
#endif
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
 */
static void __regargs
#ifdef __SASC_60
__inline
#endif
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	*/
static void __regargs
#ifdef __SASC_60
__inline
#endif
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)));
		}
	}
}

/* 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
 * Integer version
 * Completely different to the real version
 */
static void __regargs
#ifdef __SASC_60
__inline
#endif
Triangle_I(struct MyPoint *p,struct MyPoint *a,struct MyPoint *bb,struct MyPoint *c) {
	LONG ABBCCA2;
	ABBCCA2 = 2 * (a->Cdiff + bb->Cdiff + c->Cdiff);
	p->x = x +
				((a->x - a->Cx) * (bb->Cdiff + c->Cdiff) +
				 (bb->x - bb->Cx) * (a->Cdiff + c->Cdiff) +
				 (c->x - c->Cx) * (a->Cdiff + bb->Cdiff)) /
				 ABBCCA2;
	p->x1 = x +
				((a->x1 - a->Cx) * (bb->Cdiff + c->Cdiff) +
				 (bb->x1 - bb->Cx) * (a->Cdiff + c->Cdiff) +
				 (c->x1 - c->Cx) * (a->Cdiff + bb->Cdiff)) /
				ABBCCA2;
	p->y = y +
				((a->y - a->Cy) * (bb->Cdiff + c->Cdiff) +
				 (bb->y - bb->Cy) * (a->Cdiff + c->Cdiff) +
				 (c->y - c->Cy) * (a->Cdiff + bb->Cdiff)) /
				ABBCCA2;
	p->y1 = y +
				((a->y1 - a->Cy) * (bb->Cdiff + c->Cdiff) +
				 (bb->y1 - bb->Cy) * (a->Cdiff + c->Cdiff) +
				 (c->y1 - c->Cy) * (a->Cdiff + bb->Cdiff)) /
				ABBCCA2;
}

// Load some image(s) for a frame
BOOL
LoadFrames(BOOL points,BOOL image1,BOOL image2) {
	char buffer[257];
	BOOL ok=TRUE;
	UBYTE *er,*eg,*eb;
	int i;
	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 (ok && EGS && !EGS_BitMap) {
				if (EGS_BitMap = E_AllocBitMap(swidth,1,24,E_PIXELMAP,E_EB_NOTSWAPABLE,NULL)) {
					EGS_NewWindow.LeftEdge = 0;
					EGS_NewWindow.TopEdge = 20;
					EGS_NewWindow.Width = width;
					EGS_NewWindow.Height = height;
					EGS_NewWindow.MinWidth = 20;
					EGS_NewWindow.MinHeight = 20;
					EGS_NewWindow.MaxWidth = width;
					EGS_NewWindow.MaxHeight = height;
					EGS_NewWindow.Screen = NULL;
					EGS_NewWindow.Bordef.SysGadgets = EI_WINDOWSIZE | EI_WINDOWFRONT | EI_WINDOWFLIP |
																 EI_WINDOWARROWL | EI_WINDOWARROWR | EI_WINDOWARROWU | EI_WINDOWARROWD |
																 EI_WINDOWSCROLLH | EI_WINDOWSCROLLV | EI_WINDOWDRAG;
					EGS_NewWindow.FirstGadgets = NULL;
					EGS_NewWindow.Title = "TSMorph-render output";
					EGS_NewWindow.Flags = EI_GIMMEZEROZERO | EI_SUPER_BITMAP | EI_WINDOWACTIVE | EI_REPORTMOUSE | EI_QUICKSCROLL | EI_RMBTRAP;
					EGS_NewWindow.IDCMPFlags = NULL;
					EGS_NewWindow.Port = NULL;
				// EGS_NewWindow.Colors = ??;
					EGS_NewWindow.Menu = NULL;
					EGS_NewWindow.Render = NULL;
					if (EGS_Win = EI_OpenWindow(&EGS_NewWindow)) {
						EI_SetWindowTitles(EGS_Win,(UBYTE *)-1,"TSMorph-render");
						E_ActivateEGSScreen();
						er = Pic1.red;
						eg = Pic1.green;
						eb = Pic1.blue;
						for (i = 0; i<height; ++i) {
							CopyEGS(i,&er,&eg,&eb);
						}
					}
				}
			}
		}
		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;
}

void
CopyEGS(int line, UBYTE **er,UBYTE **eg,UBYTE **eb) {
	int j;
	UBYTE *plane;
	plane = ((struct E_EBitMapFull *)EGS_BitMap)->Typekey.PixelMap.Planes.Dest;
	for (j = 0; j<swidth; ++j) {
		*plane++ = *((*er)++);
		*plane++ = *((*eg)++);
		*plane++ = *((*eb)++);
		++plane;
	}
	EG_CopyBitMapRastPort(EGS_BitMap,EGS_Win->RPort,0,0,width,1,0,line);
}

int
cmpX(struct MyPoint **A,struct MyPoint **B) {
	if ((*A)->Cx < (*B)->Cx) {
		return -1;
	}
	if ((*A)->Cx > (*B)->Cx) {
		return 1;
	}
	return 0;
}

int
cmpY(struct MyPoint **A,struct MyPoint **B) {
	if ((*A)->Cy < (*B)->Cy) {
		return -1;
	}
	if ((*A)->Cy > (*B)->Cy) {
		return 1;
	}
	return 0;
}

int
cmpDiff(struct MyPoint **A,struct MyPoint **B) {
	if ((*A)->Cdiff < (*B)->Cdiff) {
		return -1;
	}
	if ((*A)->Cdiff > (*B)->Cdiff) {
		return 1;
	}
	return 0;
}

/* nsort() was written by Dave Watson and uses the algorithm described in -
 *    Watson, D.F., 1981, Computing the n-dimensional Delaunay tessellation with 
 *          application to Voronoi polytopes: The Computer J., 24(2), p. 167-172. 
 */

#define SQ(x)				(x) * (x)
#define BIGNUM				1E37
#define EPSILON         0.00001
#define TSIZE				75                  /* temporary storage size factor*/
#define  XRANGE          10.0                /* factor (>=1) for radius of control points */
#define FILENAMELEN     32
#define AND             &&
#define OR              ||
#define EQ              ==
#define NE              !=

BOOL
GenerateTriangles(void) {
	BOOL ok = FALSE;
	BOOL flag = TRUE;
   double xx, bgs, **mxy, **wrk, **pts, **ccr;
   int i0, i1, i2, i3, i4, i5, i6, i7, i8, i9, i11, ii[3], 
       dm, nts, tsz, *id, **tmp, **a3s;

   if (mxy = DoubleMatrix(2, 2)) {
	   for (i0=0; i0<2; i0++) {
	  		mxy[0][i0] = - (mxy[1][i0] = BIGNUM);
	   }
	   if (wrk = DoubleMatrix(2, 3)) {
		   for (i0=0; i0<2; i0++) {
		   	for (i1=0; i1<3; i1++) {
		   		wrk[i0][i1] = -XRANGE;
		   	}
		   }
		   for (i0=0; i0<2; i0++) {
		   	wrk[i0][i0] = XRANGE * (3 * 2 - 1);
		   }
			if (pts = DoubleMatrix(PointCount + 3, 2)) {
				for (i0=0; i0<PointCount; i0++) {
			   	NewList(&(PointsX[i0]->TList));
					pts[i0][0] = PointsX[i0]->Cx;
					pts[i0][1] = PointsX[i0]->Cy;
					for (i1=0; i1<2; i1++) {
						if (mxy[0][i1] < pts[i0][i1]) {
							mxy[0][i1] = pts[i0][i1];
						}
						if (mxy[1][i1] > pts[i0][i1]) {
							mxy[1][i1] = pts[i0][i1];
						}
					}
				}
				for (bgs=0, i0=0; i0<2; i0++) {
					mxy[0][i0] -= mxy[1][i0];
			      if (bgs < mxy[0][i0]) {
			      	bgs = mxy[0][i0];
			      }
			   }
				bgs *= EPSILON;
			   for (i0=0; i0<PointCount; i0++) {
			   	for (i1=0; i1<2; i1++) {
						pts[i0][i1] += bgs * (0.5 - (double)rand() / 0x7fffffff);
					}
				}
				for (i0=0; i0<3; i0++) {
					for (i1=0; i1<2; i1++) {
						pts[PointCount+i0][i1] = mxy[1][i1] + wrk[i1][i0] * mxy[0][i1];
					}
				}
			   for (i1=1, i0=2; i0<3; i0++) {
			   	i1 *= i0;
			   }
			   tsz = TSIZE * i1;
			   if (tmp = IntMatrix(tsz + 1, 2)) {
				   i1 *= (PointCount + i1);
				   if (id = IntVect(i1)) {
					   for (i0=0; i0<i1; i0++) {
					   	id[i0] = i0;
					   }
						if (a3s = IntMatrix(i1,3)) {
						  	if (ccr = DoubleMatrix(i1,3)) {
							  	for (a3s[0][0]=PointCount, i0=1; i0<3; i0++) {
							  		a3s[0][i0] = a3s[0][i0-1] + 1;
							  	}
								for (ccr[0][2]=BIGNUM, i0=0; i0<2; i0++) {
									ccr[0][i0] = 0;
								}
							   nts = i4 = 1;
							   dm = 2 - 1;
							   for (i0=0; i0<PointCount; i0++) {
							      i1 = i7 = -1;
							      i9 = 0;
							      for (i11=0; i11<nts; i11++) {
							         i1++;
							         while (a3s[i1][0] < 0) { 
							         	i1++;
							         }
							         xx = ccr[i1][2];
							         for (i2=0; i2<2; i2++) {
							            xx -= SQ(pts[i0][i2] - ccr[i1][i2]);
										   if (xx<0) {
										   	goto Corner3;
										   }
							         }
							         i9--;
										i4--;
										id[i4] = i1;
										for (i2=0; i2<3; i2++) {
										   ii[0] = 0;
							            if (ii[0] EQ i2) {
							            	ii[0]++;
							            }
							            for (i3=1; i3<2; i3++) {
							               ii[i3] = ii[i3-1] + 1;
							               if (ii[i3] EQ i2) {
							               	ii[i3]++;
							               }
							            }
							            if (i7>dm) {
											   i8 = i7;
												for (i3=0; i3<=i8; i3++) {
												   for (i5=0; i5<2; i5++) {
												   	if (a3s[i1][ii[i5]] NE tmp[i3][i5]) {
												   		goto Corner1;
												   	}
												   }
													for (i6=0; i6<2; i6++) {
														tmp[i3][i6] = tmp[i8][i6];
													}
													i7--;
													goto Corner2;
Corner1:;
												}
											}
											if (++i7 > tsz) {
												goto returnfail;
											}
											for (i3=0; i3<2; i3++) {
												tmp[i7][i3] = a3s[i1][ii[i3]];
											}
Corner2:;
										}
								      a3s[i1][0] = -1;
Corner3:;
								   }
								   for (i1=0; i1<=i7; i1++) {
								      for (i2=0; i2<2; i2++) {
								   		for (wrk[i2][2]=0, i3=0; i3<2; i3++) {
								            wrk[i2][i3] = pts[tmp[i1][i2]][i3] - pts[i0][i3];
										   	wrk[i2][2] += wrk[i2][i3] * (pts[tmp[i1][i2]][i3] + pts[i0][i3]) / 2;
											}
										}
										xx = wrk[0][0] * wrk[1][1] - wrk[1][0] * wrk[0][1];
										ccr[id[i4]][0] = (wrk[0][2] * wrk[1][1] - wrk[1][2] * wrk[0][1]) / xx;
										ccr[id[i4]][1] = (wrk[0][0] * wrk[1][2] - wrk[1][0] * wrk[0][2]) / xx;
								      for (ccr[id[i4]][2]=0, i2=0; i2<2; i2++) {
								        	ccr[id[i4]][2] += SQ(pts[i0][i2] - ccr[id[i4]][i2]);
										   a3s[id[i4]][i2] = tmp[i1][i2];
								      }
										a3s[id[i4]][2] = i0;
										i4++;
								      i9++;
								   }
								   nts += i9;
								}
								i0 = -1;
								for (i11=0; flag && (i11<nts); i11++) {
									i0++;
								   while (a3s[i0][0] < 0) {
								   	i0++;
								   }
								   if (a3s[i0][0] < PointCount) {
										for (i1=0; i1<2; i1++) for (i2=0; i2<2; i2++)  {
								      	wrk[i1][i2] = pts[a3s[i0][i1]][i2] - pts[a3s[i0][2]][i2];
								      }
								      xx = wrk[0][0] * wrk[1][1] - wrk[0][1] * wrk[1][0];
								      if (fabs(xx) > EPSILON) {
											flag = AddTri(a3s[i0][0],a3s[i0][2],a3s[i0][1]);
								      }
								   }
								}
							   ok = flag;
returnfail:
								FreeMatrixd(ccr);
							}
							FreeMatrixi(a3s);
						}
						MyFreeVecti(id);
					}
				   FreeMatrixi(tmp);
				}
				FreeMatrixd(pts);
			}
			FreeMatrixd(wrk);
		}
		FreeMatrixd(mxy);
	}
	return ok;
}
	
int
*IntVect(int ncols) {  
	return (int *) MyAllocVec(ncols * sizeof(int),0);
}

void
MyFreeVecti(int *vectptr) {
	if (vectptr) {
		MyFreeVec(vectptr);
	}
}

int
**IntMatrix(int nrows, int ncols) {
   int i0;
   int **matptr;
   if (nrows<2) nrows = 2;
   if (ncols<2) ncols = 2;
   if ((matptr = (int **) MyAllocVec(nrows * sizeof(int *),0)) EQ NULL) {
   	return NULL;
   }
   if ((matptr[0] = (int *) MyAllocVec(nrows * ncols * sizeof(int),0)) EQ NULL) {
   	MyFreeVec(matptr);
   	return NULL;
   }
   for (i0=1; i0<nrows; i0++) {
   	matptr[i0] = matptr[0] + i0 * ncols;
   }
   return matptr;
}

void
FreeMatrixi(int **matptr) {  
	if (matptr) {
		if (matptr[0]) {
			MyFreeVec(matptr[0]);
		}
		MyFreeVec(matptr);
	}
}

double
**DoubleMatrix(int nrows, int ncols) {
   int i0;
   double **matptr;
   if (nrows<2) nrows = 2;
   if (ncols<2) ncols = 2;
   if ((matptr = (double **) MyAllocVec(nrows * sizeof(double *),0)) EQ NULL) {
   	return NULL;
   }
   if ((matptr[0] = (double *) MyAllocVec(nrows * ncols * sizeof(double),0)) EQ NULL) {
   	MyFreeVec(matptr);
   	return NULL;
   }
   for (i0=1; i0<nrows; i0++) {
	   matptr[i0] = matptr[0] + i0 * ncols;
	}
   return matptr;
}

void
FreeMatrixd(double **matptr) {
	if (matptr) {
		if (matptr[0]) {
		   MyFreeVec(matptr[0]);
		}
		MyFreeVec(matptr);
	}
}

void
FreeTriangles(void) {
	int i;
	struct Triangle *T;
	struct MyPoint *P;
	for (i=0; i<PointCount; i++) {
		P = PointsX[i];
		while ((T =(struct Triangle *)(P->TList.lh_Head))->TNode.mln_Succ) {
			Remove((struct Node *)T);
			MyFreeVec(T);
		}
	}
}

BOOL AddTri(int aa,int bb,int cc) {
	struct Triangle *T1,*T2=NULL,*T3;
	if ((T1 = (struct Triangle *)MyAllocVec(sizeof (struct Triangle),0)) &&
		 (T2 = (struct Triangle *)MyAllocVec(sizeof (struct Triangle),0)) &&
		 (T3 = (struct Triangle *)MyAllocVec(sizeof (struct Triangle),0))) {
		T1->Point1 = PointsX[bb];
		T1->Point2 = PointsX[cc];
		AddTail(&(PointsX[aa]->TList),(struct Node *)T1);
		T2->Point1 = PointsX[aa];
		T2->Point2 = PointsX[cc];
		AddTail(&(PointsX[bb]->TList),(struct Node *)T2);
		T3->Point1 = PointsX[aa];
		T3->Point2 = PointsX[bb];
		AddTail(&(PointsX[cc]->TList),(struct Node *)T3);
		return TRUE;
	}
	if (T1) {
		MyFreeVec(T1);
	}
	if (T2) {
		MyFreeVec(T2);
	}
	return FALSE;
}

static UWORD top = 0;

// add a message to the progress list
void
AddMessage(UBYTE *message) {
	struct Node *node;
	if ((node = (struct Node *)Mymalloc(sizeof(struct Node))) &&
		 (node->ln_Name = Mystrdup(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++;
	}
}
