/*
	name:	display.c

	Amiga display routines
	----------------------

	I know there is alot of code in this source, but I have tried
	to make it as flexible as possible, and as Amiga-like as
	possible (with requesters and menus).

	Currently supported displaymodes:

	0 - No display (default)
	1 - Select from requester
	2 - Greyscale (16 colors), hires, interlaced
	3 - HAM6, lowres, non-interlaced
	4 - HAM6, lowres, interlaced
	5 - HAM8, lowres, non-interlaced
	6 - HAM8, lowres, interlaced
	7 - HAM8, hires, non-interlaced
	8 - HAM8, hires, interlaced


    This source-code is part of the RayLab 1.1 package, and it is provided
    for your compiling pleasure.  You may use it, change it, re-compile it
    etc., as long as nobody else but you receive the changes/compilations
    that you have made!

    You may not use any part(s) of this source-code for your own productions
    without the permission from the author of RayLab. Please read the legal
    information found in the users documentation for RayLab for more details.

*/


#include  <exec/libraries.h>		/* AmigaOS specific includes */
#include  <exec/ports.h>
#include  <dos/dos.h>
#include  <intuition/intuition.h>
#include  <graphics/gfxbase.h>
#include  <graphics/modeid.h>
#include  <libraries/asl.h>
#include  <utility/tagitem.h>

#include  <proto/exec.h>		/* AmigaOS prototypes */
#include  <proto/intuition.h>
#include  <proto/graphics.h>
#include  <proto/diskfont.h>
#include  <proto/asl.h>

#include  "defs.h"
#include  "extern.h"
#include  "amidefs.h"


struct MyDisplayInfo {			/* Used for requester */
	ULONG	ModeID;
	UWORD	Depth;
	BOOL	AutoScroll;
};


/* Prototypes for some subroutines */

void OpenAmigaScreen(struct NewScreen *SPars,ULONG *STags,struct NewWindow *WPars,ULONG *WTags,UBYTE *Palette,ULONG NumCols);
void CloseAmigaScreen(void);
UBYTE ConvertColorToHam6(COLOR24 *RGBColor, int x, int y);
UBYTE ConvertColorToGrey(COLOR24 *RGBColor, int x, int y);
UBYTE ConvertColorToHam8(COLOR24 *RGBColor, int x, int y);
UBYTE MG_DitherGrey(UBYTE Value, int x, int y);
UBYTE MG_Dither8(UBYTE Value, int x, int y);
void MG_Dither24(COLOR12 *RGBColor12, COLOR24 *RGBColor24, int x, int y);
void RequestDisplay(struct MyDisplayInfo *mydisplayinfo);


/* Some declarations */

#define DISPLAY_NONE       0		/* Supported display modes */
#define DISPLAY_REQUEST    1
#define DISPLAY_GREY       2
#define DISPLAY_HAM6       3
#define DISPLAY_HAM6LACE   4
#define DISPLAY_HAM8       5
#define DISPLAY_HAM8LACE   6
#define DISPLAY_HAM8HI     7
#define DISPLAY_HAM8HILACE 8
#define DISPLAY_LAST       DISPLAY_HAM8HILACE

#define DMODE_GREY  1
#define DMODE_HAM   2
#define DMODE_HIRES 4		/* Indicates "high" x-resolution */
#define DMODE_LACE  8		/* Indicates "high" y-resolution */
#define DMODE_TRUEC 16		/* True-color, not supported yet */

#define HAM_MOD_PAL 0x00
#define HAM_MOD_RED 0x20
#define HAM_MOD_GRN 0x30
#define HAM_MOD_BLU 0x10

#define HAM8_MOD_PAL 0x00
#define HAM8_MOD_BLU 0x40
#define HAM8_MOD_RED 0x80
#define HAM8_MOD_GRN 0xC0

#define SCREEN_TITLE_HEIGHT 20		/* Should not be needed... */


/* Some global variables */

	extern int	Language;
	extern char	**MyCatalogs[];
	int	CurrentDisplayType;
	COLOR12	Last12BitColor = {0,0,0};
	COLOR24	Last24BitColor = {0,0,0};
	long	DisplayFlags,DisplayDepth,PaletteColors;

	struct	Screen *RL_Screen = NULL;
	struct	Window *RL_Window = NULL;
	struct	RastPort *RL_RastPort = NULL;
	struct	ViewPort *RL_ViewPort = NULL;
	struct	IntuitionBase *IntuitionBase = NULL;
	struct	GfxBase *GfxBase = NULL;
	struct	Library *DiskfontBase = NULL;
	struct	Library *AslBase = NULL;

	/* The best HAM6-palette i could come up with: */

	UBYTE	Ham6Palette[] = {
			0x00,0x00,0x00, 0xAA,0xAA,0xAA,
			0x55,0x55,0x55, 0xFF,0xFF,0xFF,
			0x88,0x00,0x88, 0xFF,0x44,0xAA,
			0xEE,0x00,0x00, 0x66,0x22,0x00,
			0xFF,0x66,0x00, 0xFF,0xAA,0x88,
			0xFF,0xEE,0x00, 0x00,0x88,0x00,
			0x00,0xDD,0x00, 0x00,0xCC,0xCC,
			0x00,0x66,0xFF, 0x00,0x00,0xAA,	/* 16 cols */
		};

	/* The best HAM8-palette i could come up with (?): */

	UBYTE	Ham8Palette[] = {
			0x00,0x00,0x00, 0xAA,0xAA,0xAA,
			0x55,0x55,0x55, 0xFF,0xFF,0xFF,
			0x88,0x00,0x88, 0xFF,0x44,0xAA,
			0xEE,0x00,0x00, 0x66,0x22,0x00,
			0xFF,0x66,0x00, 0xFF,0xAA,0x88,
			0xFF,0xEE,0x00, 0x00,0x88,0x00,
			0x00,0xDD,0x00, 0x00,0xCC,0xCC,
			0x00,0x66,0xFF, 0x00,0x00,0xAA,	/* 16 cols */
			0x01,0x23,0x45, 0x67,0x89,0xAB,
			0xCD,0xEF,0xFE, 0xFE,0xDC,0xBA,
			0x98,0x76,0x54, 0x32,0x10,0x01,
			0x23,0x01,0x45, 0x89,0x67,0xAB,
			0xEF,0xCD,0xFE, 0xDC,0xFE,0xBA,
			0x76,0x98,0x54, 0x01,0x32,0x10,
			0x45,0x01,0x23, 0xAB,0x67,0x89,
			0xFE,0xCD,0xEF, 0xBA,0xFE,0xDC,	/* 32 cols */
			0x54,0x98,0x76, 0x01,0x32,0x10,
			0x00,0x00,0x55, 0x00,0x00,0xAA,
			0x00,0x00,0xFF, 0x00,0x55,0x00,
			0x00,0x55,0x55, 0x00,0x55,0xAA,
			0x00,0x55,0xFF, 0x00,0xAA,0x00,
			0x00,0xAA,0x55, 0x00,0xAA,0xAA,
			0x00,0xAA,0xFF, 0x00,0xFF,0x00,
			0x00,0xFF,0x55, 0x00,0xFF,0xAA,	/* 48 cols */
			0x00,0xFF,0xFF, 0x55,0x00,0x00,
			0x55,0x00,0x55, 0x55,0x00,0xAA,
			0x55,0x00,0xFF, 0x55,0x55,0x00,
			0x55,0x55,0x55, 0x55,0x55,0xAA,
			0x55,0x55,0xFF, 0x55,0xAA,0x00,
			0x55,0xAA,0x55, 0x55,0xAA,0xAA,
			0x55,0xAA,0xFF, 0x55,0xFF,0x00,
			0x55,0xFF,0x55, 0x55,0xFF,0xAA,	/* 64 cols */
		};


	UWORD	DitherMask[16] = {
			0x8000, 0x8080, 0x8410, 0x8888,
			0x9224, 0xA492, 0xA94A, 0xAAAA,
			0xD555, 0xD6B5, 0xDB6D, 0xEDDB,
			0xF777, 0xFBEF, 0xFF7F, 0xFFFF
		};

	UBYTE	DitherYTable[16] = {
			0x08, 0x0B, 0x0E, 0x0F, 0x10, 0x0F, 0x0E, 0x0B,
			0x08, 0x05, 0x02, 0x01, 0x00, 0x01, 0x02, 0x05
		};

	struct TextAttr TopazAttr =
		{(STRPTR)"topaz.font",TOPAZ_EIGHTY,0,0};

	struct TextAttr GraniteAttr =
		{(STRPTR)"Granite.font",12,0,0};	/* Lores */

	struct TextAttr HelveticaAttr =
		{(STRPTR)"helvetica.font",11,0,0};	/* Lores */

	struct TextAttr OpalAttr =
		{(STRPTR)"opal.font",12,0,0};		/* Hires */


	extern int	spenttime, spent_h, spent_m, spent_s;


/******************************************************************
 *
 *  AvailableDisplayModes()
 *
 ******************************************************************/

void AvailableDisplayModes(void)
{
	fprintf(textoutput,MyCatalogs[Language][CTXT_AVAILDISP]);
	fprintf(textoutput,MyCatalogs[Language][CTXT_DISP0]);
	fprintf(textoutput,MyCatalogs[Language][CTXT_DISP1]);
	fprintf(textoutput,MyCatalogs[Language][CTXT_DISP2]);
	fprintf(textoutput,MyCatalogs[Language][CTXT_DISP3]);
	fprintf(textoutput,MyCatalogs[Language][CTXT_DISP4]);
	fprintf(textoutput,MyCatalogs[Language][CTXT_DISP5]);
	fprintf(textoutput,MyCatalogs[Language][CTXT_DISP6]);
	fprintf(textoutput,MyCatalogs[Language][CTXT_DISP7]);
	fprintf(textoutput,MyCatalogs[Language][CTXT_DISP8]);
}


/******************************************************************
 *
 *  OpenDisplay()
 *
 ******************************************************************/

long OpenDisplay(long DisplayType)
{
	long	ModeID, AutoScroll;
	UBYTE	*PalettePtr;
	struct MyDisplayInfo mydisplayinfo;
	struct DisplayInfo displayinfo;
	long	Ret_OK;

	ULONG ScreenTagList[] =
	{
	    SA_DisplayID,0,
	    SA_AutoScroll,1,
	    SA_Overscan,OSCAN_MAX,
	    TAG_DONE,0
	};

	ULONG WindowTagList[] =
	{
	    WA_NewLookMenus,1,		/* Intuition TRUE = 1 */
	    TAG_DONE,0
	};

	struct NewScreen ScreenPars =
	{
		0, 0,		/* LeftEdge, TopEdge        */
		NULL, NULL,	/* Set screen size later    */
		NULL,		/* Set depth later          */
		2, 7,		/* DetailPen, BlockPen      */
		NULL,		/* Set display-mode later   */
		CUSTOMSCREEN,
		NULL,		/* Set font later           */
		NULL,		/* Set title later          */
		NULL,		/* No special gadgets       */
		NULL		/* No custom bitmap         */
	};

	struct NewWindow WindowPars =
	{
		0, 0,		/* LeftEdge, TopEdge        */
		NULL, NULL,	/* Set screen size later    */
		3, 7,		/* DetailPen, BlockPen      */
		MENUPICK,	/* IDCMP                    */
		SMART_REFRESH | BORDERLESS | NOCAREREFRESH,
		NULL,		/* No special gadgets       */
		NULL,		/* No checkmark             */
		NULL,		/* No window title          */
		NULL,		/* Set screen later         */
		NULL,		/* No bitmap                */
		NULL, NULL,	/* Set min size later       */
		NULL, NULL,	/* Set max size later       */
		CUSTOMSCREEN
	};


	RL_Screen = NULL; RL_Window = NULL;

	IntuitionBase = (struct IntuitionBase *)
		OpenLibrary("intuition.library",36);

	if(IntuitionBase==NULL) {
		fprintf(textoutput,MyCatalogs[Language][CTXT_INTERR]);
		IntuitionBase = NULL;
	}
	else {
		GfxBase = (struct GfxBase *)
			OpenLibrary("graphics.library",36);
		if(GfxBase==NULL) {
			fprintf(textoutput,MyCatalogs[Language][CTXT_GFXERR]);
			CloseLibrary((struct Library *)IntuitionBase);
			IntuitionBase = NULL; GfxBase = NULL;
		}
	}

	if( (IntuitionBase!=NULL) && (GfxBase!=NULL) ) {
	    if( (DisplayType>DISPLAY_NONE) && (DisplayType<=DISPLAY_LAST) ) {
		CurrentDisplayType = DisplayType;

		ScreenPars.Width = (WORD)PicWidth;
		ScreenPars.Height = (WORD)PicHeight;
		ScreenPars.DefaultTitle = MyCatalogs[Language][CTXT_DISPTITLE];

		WindowPars.Width = (WORD)PicWidth;
		WindowPars.Height = (WORD)PicHeight;
		WindowPars.MinWidth = WindowPars.MaxWidth = (WORD)PicWidth;
		WindowPars.MinHeight = WindowPars.MaxHeight = (WORD)PicHeight;

		ScreenTagList[1]=DEFAULT_MONITOR_ID;	/* MONITOR_ID_MASK */

		ModeID=0L; AutoScroll=1L;
		DisplayFlags=0L;
		DisplayDepth=0L;
		PaletteColors=0L;
		PalettePtr=NULL;

		switch(CurrentDisplayType) {
		    case DISPLAY_GREY:
			ModeID=HIRESLACE_KEY;
			DisplayFlags=DMODE_GREY|DMODE_HIRES|DMODE_LACE;
			DisplayDepth=4;
			PaletteColors=16;
			break;

		    case DISPLAY_HAM6:
			ModeID=HAM_KEY;
			DisplayFlags=DMODE_HAM;
			DisplayDepth=6;
			PaletteColors=16;
			PalettePtr=Ham6Palette;
			break;

		    case DISPLAY_HAM6LACE:
			ModeID=HAMLACE_KEY;
			DisplayFlags=DMODE_HAM|DMODE_LACE;
			DisplayDepth=6;
			PaletteColors=16;
			PalettePtr=Ham6Palette;
			break;

		    case DISPLAY_HAM8:
			ModeID=HAM_KEY;
			DisplayFlags=DMODE_HAM;
			DisplayDepth=8;
			PaletteColors=64;
			PalettePtr=Ham8Palette;
			break;

		    case DISPLAY_HAM8LACE:
			ModeID=HAMLACE_KEY;
			DisplayFlags=DMODE_HAM|DMODE_LACE;
			DisplayDepth=8;
			PaletteColors=64;
			PalettePtr=Ham8Palette;
			break;

		    case DISPLAY_HAM8HI:
			ModeID=HIRESHAM_KEY;
			DisplayFlags=DMODE_HAM|DMODE_HIRES;
			DisplayDepth=8;
			PaletteColors=64;
			PalettePtr=Ham8Palette;
			break;

		    case DISPLAY_HAM8HILACE:
			ModeID=HIRESHAMLACE_KEY;
			DisplayFlags=DMODE_HAM|DMODE_HIRES|DMODE_LACE;
			DisplayDepth=8;
			PaletteColors=64;
			PalettePtr=Ham8Palette;
			break;

		    case DISPLAY_REQUEST:
			RequestDisplay(&mydisplayinfo);
			ModeID=mydisplayinfo.ModeID;
			AutoScroll=mydisplayinfo.AutoScroll;
			DisplayDepth=(ULONG)mydisplayinfo.Depth;
			DisplayFlags=0L;
			if(DisplayDepth>8) {
			    DisplayFlags=DMODE_TRUEC;
			    fprintf(textoutput,MyCatalogs[Language][CTXT_TRUECNSUPP]);
			    CurrentDisplayType=DISPLAY_NONE;
			}
			else if(GfxBase->LibNode.lib_Version>=36) {
			    if(GetDisplayInfoData(NULL,(UBYTE *)&displayinfo,sizeof(struct DisplayInfo),DTAG_DISP,ModeID)) {
				if(displayinfo.Resolution.x<30)
				    DisplayFlags=DisplayFlags|DMODE_HIRES;
				if(((displayinfo.PropertyFlags&DIPF_IS_LACE)!=0)||(displayinfo.Resolution.y<30))
				    DisplayFlags=DisplayFlags|DMODE_LACE;
				if((displayinfo.PropertyFlags&DIPF_IS_HAM)!=0) {
				    DisplayFlags=DisplayFlags|DMODE_HAM;
				    PaletteColors=1L<<(DisplayDepth-2);
				    if(DisplayDepth==6)
					PalettePtr=Ham6Palette;
				    else
					PalettePtr=Ham8Palette;
				}
				else {
				    DisplayFlags=DisplayFlags|DMODE_GREY;
				    PaletteColors=1L<<DisplayDepth;
				}
			    }
			}
			if(DisplayFlags==0L) {
			    DisplayFlags=DMODE_GREY;
			    PaletteColors=1L<<DisplayDepth;
			}
			break;

		    case DISPLAY_NONE:
		    default:
			ModeID=0;
			DisplayFlags=0;
			DisplayDepth=0;
			CurrentDisplayType=DISPLAY_NONE;
			break;
		}


	/* Setup screen, knowing the display modes... */

		if(CurrentDisplayType!=DISPLAY_NONE) {
		    ScreenTagList[1]=ModeID;
		    ScreenTagList[3]=AutoScroll;
		    ScreenPars.Depth=DisplayDepth;

		    if((DisplayFlags&DMODE_HAM)!=0L) {
			ScreenPars.DetailPen = 3;
			ScreenPars.BlockPen = 1;
		    }
		    else {
			ScreenPars.DetailPen = (2*PaletteColors)/16;
			ScreenPars.BlockPen = (7*PaletteColors)/16;
		    }

		    if(((DisplayFlags&DMODE_HIRES)!=0L)&&((DisplayFlags&DMODE_LACE)!=0L))
			ScreenPars.Font = &OpalAttr;
		    else if((DisplayFlags&DMODE_HIRES)!=0L)
			ScreenPars.Font = &OpalAttr;
		    else if((DisplayFlags&DMODE_LACE)!=0L)
			ScreenPars.Font = &GraniteAttr;
		    else
			ScreenPars.Font = &HelveticaAttr;

		    OpenAmigaScreen(&ScreenPars,ScreenTagList,&WindowPars,WindowTagList,PalettePtr,(ULONG)PaletteColors);
		}
		else {
		    fprintf(textoutput,MyCatalogs[Language][CTXT_UNSUPP],DisplayType);
		}
	    }
	    else {
		if(DisplayType!=DISPLAY_NONE)
		    fprintf(textoutput,MyCatalogs[Language][CTXT_UNSUPP],DisplayType);
		CurrentDisplayType = DISPLAY_NONE;
	    }
	}
	else {
		CurrentDisplayType = DISPLAY_NONE;
	}

	Ret_OK = 0L;
	if(CurrentDisplayType!=DISPLAY_NONE) {
		Ret_OK = 1L;
	}

	return(Ret_OK);
}


/******************************************************************
 *
 *  CloseDisplay()
 *
 ******************************************************************/

void CloseDisplay(void)
{
	ULONG	signalmask, signals, class, code, menunum, itemnum;
	struct	IntuiMessage *message;
	int	done;

	if((CurrentDisplayType!=0)&&(picturerendering==TRUE)) {
	    CloseAmigaScreen();
	}
	else if((CurrentDisplayType!=0)&&(picturerendering==FALSE)) {
	    struct IntuiText ItemsText[] =
	    {
		{2,1,JAM1,2,1,NULL,NULL,    NULL},
		{2,1,JAM1,2,1,NULL,NULL,    NULL}
	    };

	    struct MenuItem Items[] =
	    {
		{
		NULL,		/* No more items            */
		2, 1, 110, 10,	/* Left, top, width, height */
		ITEMTEXT | COMMSEQ | HIGHCOMP,
		0,		/* MutualExclude            */
		NULL,		/* Set item text later      */
		NULL,
		'A',		/* Key equivalent           */
		NULL,		/* Sub item                 */
		MENUNULL	/* NextSelect               */
		},
		{
		NULL,		/* No more items            */
		2, 11, 110, 10,	/* Left, top, width, height */
		ITEMTEXT | COMMSEQ | ITEMENABLED | HIGHCOMP,
		0,		/* MutualExclude            */
		NULL,		/* Set item text later      */
		NULL,
		'C',		/* Key equivalent           */
		NULL,		/* Sub item                 */
		MENUNULL	/* NextSelect               */
		}

	    };

	    struct Menu MenuPars =
	    {
		NULL,		/* No more menus            */
		0, 0, 120, 40,	/* Left, top, width, height */
		MENUENABLED,	/* Flags                    */
		NULL,		/* Menu name                */
		NULL		/* Set first item later     */
	    };

	    ScreenToFront(RL_Screen);	/* Bring screen to front when done */
	    DisplayBeep(RL_Screen);	/* Beep to show that we are done */
	    ActivateWindow(RL_Window);	/* Activate window -> menu-access */

	    ItemsText[0].IText = MyCatalogs[Language][CTXT_ABOUT];
	    ItemsText[1].IText = MyCatalogs[Language][CTXT_CLOSE];
	    ItemsText[0].ITextFont = ItemsText[1].ITextFont = &TopazAttr;
	    Items[0].ItemFill = &ItemsText[0];
	    Items[1].ItemFill = &ItemsText[1];
	    Items[0].NextItem = &Items[1];
	    MenuPars.FirstItem = &Items[0];
	    MenuPars.MenuName = MyCatalogs[Language][CTXT_MENU];

	    SetMenuStrip(RL_Window, &MenuPars);	/* Add menu */

	    signalmask = 1L << RL_Window->UserPort->mp_SigBit;
	    done = FALSE;

	/*  Wait for menu select */

	    while( done == FALSE ) {
		signals = Wait(signalmask);
		if((signals&signalmask)!=0L) {
		    while( message = (struct IntuiMessage *)GetMsg(RL_Window->UserPort) ) {
			class = message->Class;
			code = message->Code;
			ReplyMsg((struct Message *)message);
			if(class==MENUPICK) {
			    menunum = MENUNUM(code);
			    itemnum = ITEMNUM(code);
			    if(menunum==0L) {
				if(itemnum==0L) {
				    DisplayBeep(RL_Screen);
				}
				else if(itemnum==1L) {
				    done = TRUE;
				}
			    }
			}
		    }
		}
	    }
	    CloseAmigaScreen();
	}
}


/******************************************************************
 *
 *  DisplayPlot()
 *
 ******************************************************************/

void DisplayPlot(int x, int y, COLOR24 *RGBColor)
{
	if(CurrentDisplayType!=DISPLAY_NONE) {
	    if((DisplayFlags&DMODE_GREY)!=0L)
		SetAPen(RL_RastPort, ConvertColorToGrey(RGBColor,x,y));
	    else if(((DisplayFlags&DMODE_HAM)!=0L)&&(DisplayDepth==6))
		SetAPen(RL_RastPort, ConvertColorToHam6(RGBColor,x,y));
	    else if(((DisplayFlags&DMODE_HAM)!=0L)&&(DisplayDepth==8))
		SetAPen(RL_RastPort, ConvertColorToHam8(RGBColor,x,y));
	    else SetAPen(RL_RastPort,(x+y)&(PaletteColors-1));
	    (void) WritePixel(RL_RastPort,x,y);
	}
}


/******************************************************************

    Some routines for converting 24-bit rgb colors to apropriate
    display-modes

 ******************************************************************/


/***************************************************************
 *
 *	ConvertColorToGrey()
 *
 *	Now supports 2-256 colors
 *	(<256 colors: dither, 256 colors: don't)
 *
 ***************************************************************/

UBYTE ConvertColorToGrey(COLOR24 *RGBColor, int x, int y)
{
	UBYTE	Grey;

	Grey = (UBYTE)(((UWORD)RGBColor->r+(UWORD)RGBColor->g+(UWORD)RGBColor->b)/(UWORD)3);
	if(PaletteColors<256)
	    Grey = (UBYTE)MG_DitherGrey(Grey,x,y);

	return(Grey);
}


/***************************************************************
 *
 *	ConvertColorToHam6()
 *
 ***************************************************************/

UBYTE ConvertColorToHam6(COLOR24 *RGBColor, int x, int y)
{
	COLOR12	New12BitColor;
	UBYTE	HamMode, HamCol;
	long	Diff12BitColor[3];
	register long RGBDiff, t1, t2, t3, i, BestColor;

	if(x==0) {
		Last12BitColor.r = (Ham6Palette[0]>>4)&0x0f;
		Last12BitColor.g = (Ham6Palette[1]>>4)&0x0f;
		Last12BitColor.b = (Ham6Palette[2]>>4)&0x0f;
	}

	MG_Dither24(&New12BitColor,RGBColor,x,y);	/* Dither 24bit -> 12bit */

	Diff12BitColor[0] = (long)New12BitColor.r-(long)Last12BitColor.r;
	Diff12BitColor[1] = (long)New12BitColor.g-(long)Last12BitColor.g;
	Diff12BitColor[2] = (long)New12BitColor.b-(long)Last12BitColor.b;
	Diff12BitColor[0] *= Diff12BitColor[0];
	Diff12BitColor[1] *= Diff12BitColor[1];
	Diff12BitColor[2] *= Diff12BitColor[2];
	HamMode = HAM_MOD_RED;
	if(Diff12BitColor[1]>Diff12BitColor[0]) {
		HamMode = HAM_MOD_GRN;
		if(Diff12BitColor[2]>Diff12BitColor[1]) {
			HamMode = HAM_MOD_BLU;
		}
	}
	else if(Diff12BitColor[2]>Diff12BitColor[0]) {
		HamMode = HAM_MOD_BLU;
	}

	switch(HamMode) {
	    case HAM_MOD_RED:
		HamCol = Last12BitColor.r = New12BitColor.r;
		RGBDiff = Diff12BitColor[1] + Diff12BitColor[2];
		break;
	    case HAM_MOD_GRN:
		HamCol = Last12BitColor.g = New12BitColor.g;
		RGBDiff = Diff12BitColor[0] + Diff12BitColor[2];
		break;
	    case HAM_MOD_BLU:
		HamCol = Last12BitColor.b = New12BitColor.b;
		RGBDiff = Diff12BitColor[0] + Diff12BitColor[1];
		break;
	}

	BestColor = 100;
	for(i=0;i<16;i++) {
		t1 = (long)((Ham6Palette[i*3]>>4)&0x0f);
		t1 = t1 - (long)New12BitColor.r;
		t2 = (long)((Ham6Palette[i*3+1]>>4)&0x0f);
		t2 = t2 - (long)New12BitColor.g;
		t3 = (long)((Ham6Palette[i*3+2]>>4)&0x0f);
		t3 = t3 - (long)New12BitColor.b;
		t1 = t1*t1 + t2*t2 + t3*t3;
		if(RGBDiff>t1) {
			RGBDiff = t1;
			BestColor = i;
		}
	}
	if(BestColor<16) {
		HamMode = HAM_MOD_PAL;
		HamCol  = (UBYTE)BestColor;
		Last12BitColor.r = (Ham6Palette[BestColor*3]>>4)&0x0f;
		Last12BitColor.g = (Ham6Palette[BestColor*3+1]>>4)&0x0f;
		Last12BitColor.b = (Ham6Palette[BestColor*3+2]>>4)&0x0f;
	}

	return((UBYTE)(HamMode | (HamCol&0x0f)));
}


/***************************************************************
 *
 *	ConvertColorToHam8()
 *
 ***************************************************************/

UBYTE ConvertColorToHam8(COLOR24 *RGBColor, int x, int y)
{
	COLOR24	New24BitColor;
	UBYTE	HamMode, HamCol;
	long	Diff24BitColor[3];
	register long RGBDiff, t1, t2, t3, i, BestColor;

	if(x==0) {
		Last24BitColor.r = Ham8Palette[0];
		Last24BitColor.g = Ham8Palette[1];
		Last24BitColor.b = Ham8Palette[2];
	}

	New24BitColor=*RGBColor;	/* No dithering */

	Diff24BitColor[0] = (long)New24BitColor.r-(long)Last24BitColor.r;
	Diff24BitColor[1] = (long)New24BitColor.g-(long)Last24BitColor.g;
	Diff24BitColor[2] = (long)New24BitColor.b-(long)Last24BitColor.b;
	Diff24BitColor[0] *= Diff24BitColor[0];
	Diff24BitColor[1] *= Diff24BitColor[1];
	Diff24BitColor[2] *= Diff24BitColor[2];
	HamMode = HAM8_MOD_RED;
	if(Diff24BitColor[1]>Diff24BitColor[0]) {
		HamMode = HAM8_MOD_GRN;
		if(Diff24BitColor[2]>Diff24BitColor[1]) {
			HamMode = HAM8_MOD_BLU;
		}
	}
	else if(Diff24BitColor[2]>Diff24BitColor[0]) {
		HamMode = HAM8_MOD_BLU;
	}

	switch(HamMode) {
	    case HAM8_MOD_RED:
		Last24BitColor.r = (New24BitColor.r&0xfc) | (Last24BitColor.r&0x03);
		HamCol = New24BitColor.r>>2;
		RGBDiff = New24BitColor.r-Last24BitColor.r; RGBDiff*=RGBDiff;
		RGBDiff += Diff24BitColor[1] + Diff24BitColor[2];
		break;
	    case HAM8_MOD_GRN:
		Last24BitColor.g = (New24BitColor.g&0xfc) | (Last24BitColor.g&0x03);
		HamCol = New24BitColor.g>>2;
		RGBDiff = New24BitColor.g-Last24BitColor.g; RGBDiff*=RGBDiff;
		RGBDiff += Diff24BitColor[0] + Diff24BitColor[2];
		break;
	    case HAM8_MOD_BLU:
		Last24BitColor.b = (New24BitColor.b&0xfc) | (Last24BitColor.b&0x03);
		HamCol = New24BitColor.b>>2;
		RGBDiff = New24BitColor.b-Last24BitColor.b; RGBDiff*=RGBDiff;
		RGBDiff += Diff24BitColor[0] + Diff24BitColor[1];
		break;
	}

	BestColor = 100;
	for(i=0;i<192;i+=3) {
		t1 = (long)Ham8Palette[i]   - (long)New24BitColor.r;
		t2 = (long)Ham8Palette[i+1] - (long)New24BitColor.g;
		t3 = (long)Ham8Palette[i+2] - (long)New24BitColor.b;
		t1 = t1*t1 + t2*t2 + t3*t3;
		if(RGBDiff>t1) {
			RGBDiff = t1;
			BestColor = i/3;
		}
	}
	if(BestColor<64) {
		HamMode = HAM8_MOD_PAL;
		HamCol  = (UBYTE)BestColor;
		Last24BitColor.r = (long)Ham8Palette[BestColor*3];
		Last24BitColor.g = (long)Ham8Palette[BestColor*3+1];
		Last24BitColor.b = (long)Ham8Palette[BestColor*3+2];
	}

	return((UBYTE)(HamMode | (HamCol & 0x3f)));
}




/***************************************************************
 *
 *	This is some strange dithering method that I managed
 *	to produce late one night
 *
 ***************************************************************/


UBYTE MG_Dither8(UBYTE Value, int x, int y)
{
	UBYTE	Dithered;

	Dithered = Value>>4;	/* Make 4-bit value from 8-bit value */
	if(Dithered<15) {
	    if(((0x01<<(((y<<2)+x+DitherYTable[y&0x000f])&0x000f))&DitherMask[Value&0x0f])!=0) Dithered++;
	}
	return(Dithered);
}


UBYTE MG_DitherGrey(UBYTE Value, int x, int y)
{
	UBYTE	Dithered,Val2;

	if(DisplayDepth<=4) Val2 = Value>>(4-DisplayDepth);
	else                Val2 = Value<<(DisplayDepth-4);

	Dithered = Value>>(8-DisplayDepth);
	if(Dithered<(PaletteColors-1)) {
	    if(((0x01<<(((y<<2)+x+DitherYTable[y&0x000f])&0x000f))&DitherMask[Val2&0x0f])!=0) Dithered++;
	}
	return(Dithered);
}


void MG_Dither24(COLOR12 *RGBColor12, COLOR24 *RGBColor24, int x, int y)
{
	register int	Dither;

	RGBColor12->r = (RGBColor24->r>>4)&0x0f;	/* Make 12-bit color from 24-bit color */
	RGBColor12->g = (RGBColor24->g>>4)&0x0f;
	RGBColor12->b = (RGBColor24->b>>4)&0x0f;
	Dither = 0x01<<(((y<<2)+x+DitherYTable[y&0x000f])&0x000f);
	if((RGBColor12->r<15)&(Dither&DitherMask[RGBColor24->r&0x0f])!=0) RGBColor12->r++;
	if((RGBColor12->g<15)&(Dither&DitherMask[RGBColor24->g&0x0f])!=0) RGBColor12->g++;
	if((RGBColor12->b<15)&(Dither&DitherMask[RGBColor24->b&0x0f])!=0) RGBColor12->b++;
}


/******************************************************************

    Some routines for simplifying the opening and closing of Amiga
    screens and windows

 ******************************************************************/


/***************************************************************
 *
 *	OpenAmigaScreen()
 *
 ***************************************************************/

void OpenAmigaScreen(struct NewScreen *SPars,ULONG *STags,struct NewWindow *WPars,ULONG *WTags,UBYTE *Palette,ULONG NumCols)
{
	WORD	ScreenHeight,ScreenWidth,ScreenType;
	UBYTE	*Pal8;
	ULONG	ColR,ColG,ColB;
	int	i;

    /* Read required font from disk (if necesary) */
	DiskfontBase = OpenLibrary("diskfont.library",36);
	if(DiskfontBase!=NULL) {
	    (void) OpenDiskFont(SPars->Font);
	    CloseLibrary(DiskfontBase);
	}

    /* Determine screen height by briefly opening and closing a dummy screen */
	ScreenWidth=SPars->Width;
	ScreenHeight=SPars->Height;
	ScreenType=SPars->Type;
	SPars->Width=128; SPars->Height=64;
	SPars->Type=SPars->Type|SCREENBEHIND|SCREENQUIET;
	RL_Screen = (struct Screen *)OpenScreenTagList(SPars,(struct TagItem *)STags);
	if(RL_Screen!=NULL) {
	    ScreenHeight+=(WORD)(RL_Screen->BarHeight+1);
	    CloseScreen(RL_Screen);
	}
	else {
	    ScreenHeight+=SCREEN_TITLE_HEIGHT;
	}
	SPars->Width=ScreenWidth;
	SPars->Height=ScreenHeight;
	SPars->Type=ScreenType;

    /* Open screen for real */
	RL_Screen = (struct Screen *)OpenScreenTagList(SPars,(struct TagItem *)STags);
	if(RL_Screen!=NULL) {
	    WPars->Screen = RL_Screen;
	    WPars->TopEdge=(WORD)(RL_Screen->BarHeight+1);
	    RL_Window = (struct Window *)OpenWindowTagList(WPars,(struct TagItem *)WTags);
	    if(RL_Window!=NULL) {
		RL_RastPort = RL_Window->RPort;
		RL_ViewPort = &(RL_Screen->ViewPort);
		SetDrMd(RL_RastPort, JAM1);
	/* Load palette */
		if((DisplayFlags&DMODE_HAM)!=0L) {	/* Set colormap to HAM palette */
		    Pal8=(UBYTE *)Palette;
		    if(GfxBase->LibNode.lib_Version>=39) {
			for(i=0;i<NumCols;i++) {
			    ColR=((ULONG)*Pal8++)&0x000000ff; ColR=(ColR<<24)|(ColR<<16)|(ColR<<8)|ColR;
			    ColG=((ULONG)*Pal8++)&0x000000ff; ColG=(ColG<<24)|(ColG<<16)|(ColG<<8)|ColG;
			    ColB=((ULONG)*Pal8++)&0x000000ff; ColB=(ColB<<24)|(ColB<<16)|(ColB<<8)|ColB;
			    SetRGB32(RL_ViewPort,(ULONG)i,ColR,ColG,ColB);
			}
		    } else {
			for(i=0;i<NumCols;i++) {
			    ColR=(((ULONG)*Pal8++)&0x000000ff)>>4;
			    ColG=(((ULONG)*Pal8++)&0x000000ff)>>4;
			    ColB=(((ULONG)*Pal8++)&0x000000ff)>>4;
			    SetRGB4(RL_ViewPort,(LONG)i,ColR,ColG,ColB);
			}
		    }

		}
		else {					/* Set colormap to grey-scale */
		    if(GfxBase->LibNode.lib_Version>=39) {
			for(i=0;i<NumCols;i++) {
			    ColR=(0x000000ff*i)/NumCols;
			    ColR=(ColR<<24)|(ColR<<16)|(ColR<<8)|ColR;
			    SetRGB32(RL_ViewPort,(ULONG)i,ColR,ColR,ColR);
			}
		    } else {
			for(i=0;i<NumCols;i++) {
			    ColR=(0x000000f*i)/NumCols;
			    SetRGB4(RL_ViewPort,(LONG)i,ColR,ColR,ColR);
			}
		    }
		}
	    }
	    else {
		RL_RastPort = NULL;
		fprintf(textoutput,MyCatalogs[Language][CTXT_WINERR]);
		CurrentDisplayType = DISPLAY_NONE;
		CloseScreen(RL_Screen);
		RL_Screen = NULL; RL_Window = NULL;
	    }
	}
	else {
	    fprintf(textoutput,MyCatalogs[Language][CTXT_SCRNERR]);
	    CurrentDisplayType = DISPLAY_NONE;
	    RL_Screen = NULL; RL_Window = NULL;
	}
}


/***************************************************************
 *
 *	CloseAmigaScreen()
 *
 ***************************************************************/

void CloseAmigaScreen(void)
{
	CurrentDisplayType = DISPLAY_NONE;
	if(RL_Window!=NULL) {
		if(RL_Window->MenuStrip)
			ClearMenuStrip(RL_Window);	/* Remove menu  */
		CloseWindow(RL_Window);			/* Close window */
		RL_Window = NULL;
	}
	if(RL_Screen!=NULL) {
		CloseScreen(RL_Screen);			/* Close screen */
		RL_Screen = NULL;
	}
	if(GfxBase!=NULL) {
		CloseLibrary((struct Library *)GfxBase);  /* Close graphics.library */
		GfxBase = NULL;
	}
	if(IntuitionBase!=NULL) {
		CloseLibrary((struct Library *)IntuitionBase);  /* Close intuition.library */
		IntuitionBase = NULL;
	}
}


/***************************************************************
 *
 *	RequestDisplay()
 *
 ***************************************************************/

#define UnwantedProps (DIPF_IS_DUALPF|DIPF_IS_PF2PRI|DIPF_IS_EXTRAHALFBRITE)

void RequestDisplay(struct MyDisplayInfo *mydisplayinfo)
{
	struct ScreenModeRequester *smr;
	struct TagItem smrtags[11];

	mydisplayinfo->ModeID     = 0xffffffff;
	mydisplayinfo->Depth      = NULL;
	mydisplayinfo->AutoScroll = NULL;

	if( AslBase=OpenLibrary("asl.library", 38L) ) {
	    smrtags[0].ti_Tag  = ASLSM_TitleText;
	    smrtags[0].ti_Data = (ULONG)MyCatalogs[Language][CTXT_DISPREQ];
	    smrtags[1].ti_Tag  = ASLSM_DoDepth;
	    smrtags[1].ti_Data = TRUE;
	    smrtags[2].ti_Tag  = ASLSM_DoAutoScroll;
	    smrtags[2].ti_Data = TRUE;
	    smrtags[3].ti_Tag  = ASLSM_PropertyFlags;
	    smrtags[3].ti_Data = 0L;
	    smrtags[4].ti_Tag  = ASLSM_PropertyMask;
	    smrtags[4].ti_Data = UnwantedProps;
	    smrtags[5].ti_Tag  = ASLSM_InitialDisplayID;
	    smrtags[5].ti_Data = PAL_MONITOR_ID|HAM_KEY; /* Sorry America, Europe is my home */
	    smrtags[6].ti_Tag  = ASLSM_InitialDisplayWidth;
	    smrtags[6].ti_Data = 200L;
	    smrtags[7].ti_Tag  = ASLSM_InitialDisplayHeight;
	    smrtags[7].ti_Data = 150L;
	    smrtags[8].ti_Tag  = ASLSM_InitialDisplayDepth;
	    smrtags[8].ti_Data = 6L;
	    smrtags[9].ti_Tag  = ASLSM_InitialAutoScroll;
	    smrtags[9].ti_Data = TRUE;
	    smrtags[10].ti_Tag = TAG_DONE;
	    if( smr = (struct ScreenModeRequester *)AllocAslRequest(ASL_ScreenModeRequest, smrtags) ) {
	        if( AslRequest(smr, 0L) ) {
		    mydisplayinfo->ModeID     = smr->sm_DisplayID;
		    mydisplayinfo->Depth      = smr->sm_DisplayDepth;
		    mydisplayinfo->AutoScroll = smr->sm_AutoScroll;
		}
		FreeAslRequest(smr);
	    }
	    CloseLibrary(AslBase);
	}
}
