/* $Revision Header *** Header built automatically - do not edit! ***********
 *
 *	(C) Copyright 1991 by Torsten Jürgeleit
 *
 *	Name .....: render.c
 *	Created ..: Thursday 19-Dec-91 15:14:35
 *	Revision .: 11
 *
 *	Date        Author                 Comment
 *	=========   ====================   ====================
 *	28-Jul-92   Torsten Jürgeleit      open window centered over position
 *					   of mouse pointer
 *	28-Jul-92   Torsten Jürgeleit      save window pointer in RenderInfo
 *	28-Jul-92   Torsten Jürgeleit      open_window(): now correct window
 *					   position if RENDER_INFO_FLAG_-
 *					   INNER_WINDOW set
 *	23-Jun-92   Torsten Jürgeleit      close_window() now checks if
 *	                                   win->UserPort exists before trying
 *	                                   to reply all IntuiMessages
 *	22-Jun-92   Torsten Jürgeleit      after modifying NewWindow struct
 *	                                   for inner window mode new window
 *	                                   dimension is scaled once again in
 *	                                   open_window()
 *	12-May-92   Torsten Jürgeleit      text colors for clear_window()
 *	30-Apr-92   Torsten Jürgeleit      raster for clear_window()
 *	26-Apr-92   Torsten Jürgeleit      special algorithm to calc text 2
 *                                         pen for screens with 1 or 2 planes
 *	22-Mar-92   Torsten Jürgeleit      mungwall hit in create_avail_fonts
 *                                            no CopyMem() instead of 2nd
 *                                            AvailFonts() allowed
 *	18-Mar-92   Torsten Jürgeleit      calc inner window dimension BEFORE
 *                                         centering window in open_window()
 *	06-Jan-92   Torsten Jürgeleit      add font support routines
 *	19-Dec-91   Torsten Jürgeleit      Created this file!
 *
 ****************************************************************************
 *
 *	Routines to get information needed to render ISUP objects and some
 *	window support functions
 *
 * $Revision Header ********************************************************/

	/* Includes */

#include <exec/types.h>
#include <exec/memory.h>
#include <libraries/diskfont.h>
#include <intuition/intuition.h>
#ifdef AZTEC_C
#include <functions.h>   /* needed for Aztec C - prototypes and pragmas for all Amiga system functions */
#endif
#include <libraries/memwatch.h>   /* header file for memory debug link library (Fish 240) - AFTER functions.h */
#include "render.h"

	/* Defines */

#define RED_SCALE	3	/* amount of RGB values to take in account the different visual impact of those colors */
#define GREEN_SCALE	6
#define BLUE_SCALE	2

#define DEFAULT_AVAIL_FONTS_BUFFER_SIZE		1000

	/* Externals */

IMPORT struct IntuitionBase  *IntuitionBase;

	/* Statics */

STATIC USHORT image_data_arrow_left[] = {   /* bitmap data for left arrow image */
	0x0780, 0x3e00, 0xf800, 0x3e00, 0x0780, 0x0000, 0x0000, 0x0000,
	0x0000, 0x0000
};
STATIC USHORT image_data_arrow_right[] = {   /* bitmap data for right arrow image */
	0xf000, 0x3e00, 0x0f80, 0x3e00, 0xf000, 0x0000, 0x0000, 0x0000,
	0x0000, 0x0000
};
STATIC USHORT image_data_arrow_up[] = {   /* bitmap data for up arrow image */
	0x0800, 0x1c00, 0x3e00, 0x7700, 0xc180, 0x0000, 0x0000, 0x0000,
	0x0000, 0x0000
};
STATIC USHORT image_data_arrow_down[] = {   /* bitmap data for down arrow image */
	0xc180, 0x7700, 0x3e00, 0x1c00, 0x0800, 0x0000, 0x0000, 0x0000,
	0x0000, 0x0000
};
STATIC USHORT image_data_toggle_knob_unselected[] = {   /* bitmap data for unselected toggle knob image */
	0x1ffc, 0x0000, 0x7000, 0x0000, 0xc000, 0x0000, 0xc000, 0x0000,
	0xc000, 0x0000, 0xc000, 0x0000, 0xc000, 0x0000, 0x7000, 0x0000,
	0x1000, 0x0000,   /* Plane 0 */
	0x0002, 0x0000, 0x0003, 0x8000, 0x0000, 0xc000,	0x0000, 0xc000,
	0x0000, 0xc000, 0x0000, 0xc000, 0x0000, 0xc000,	0x0003, 0x8000,
	0x0ffe, 0x0000    /* Plane 1 */
};
STATIC USHORT image_data_toggle_knob_selected[] = {   /* bitmap data for selected toggle knob image */
	0x0002, 0x0000, 0x0003, 0x8000, 0x03f0, 0xc000, 0x0ffc, 0xc000,
	0x0ffc, 0xc000, 0x0ffc, 0xc000, 0x03f0, 0xc000, 0x0003, 0x8000,
	0x0ffe, 0x0000,   /* Plane 0 */
	0x1ffc, 0x0000, 0x7000, 0x0000, 0xc3f0, 0x0000,	0xcffc, 0x0000,
	0xcffc, 0x0000, 0xcffc, 0x0000, 0xc3f0, 0x0000,	0x7000, 0x0000,
	0x1000, 0x0000    /* Plane 1 */
};
STATIC USHORT image_data_toggle_check_selected[] = {   /* bitmap data for selected toggle checkmark image */
	0x0038, 0x0060, 0x00c0, 0xe180, 0x7300, 0x3e00, 0x1c00
};
STATIC USHORT image_data_cycle[] = {   /* bitmap data for cycle image */
	0x7f01, 0xc181, 0xc181, 0xc7e1, 0xc3c1, 0xc181, 0xc001,	0xc181,
	0x7f01,   /* Plane 0 */
	0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002,
	0x0002    /* Plane 1 */
};
STATIC USHORT image_data_count_left[] = {   /* bitmap data for left count image */
	0x0700, 0x3f00, 0xff00, 0x3f00, 0x0700
};
STATIC USHORT image_data_count_right[] = {   /* bitmap data for right count image */
	0xe000, 0xfc00, 0xff00, 0xfc00, 0xe000
};
STATIC struct Image  images[] = {
   {		/* left arrow */
	0, 0, IMAGE_ARROW_WIDTH, IMAGE_ARROW_HEIGHT, IMAGE_ARROW_DEPTH,
	&image_data_arrow_left[0], 0, 0, NULL
   }, {		/* right arrow */
	0, 0, IMAGE_ARROW_WIDTH, IMAGE_ARROW_HEIGHT, IMAGE_ARROW_DEPTH,
	&image_data_arrow_right[0], 0, 0, NULL
   }, {		/* up arrow */
	0, 0, IMAGE_ARROW_WIDTH, IMAGE_ARROW_HEIGHT, IMAGE_ARROW_DEPTH,
	&image_data_arrow_up[0], 0, 0, NULL
   }, {		/* down arrow */
	0, 0, IMAGE_ARROW_WIDTH, IMAGE_ARROW_HEIGHT, IMAGE_ARROW_DEPTH,
	&image_data_arrow_down[0], 0, 0, NULL
   }, {		/* toggle select knob unselected */
	0, 0, IMAGE_KNOB_WIDTH, IMAGE_KNOB_HEIGHT, IMAGE_KNOB_DEPTH,
	&image_data_toggle_knob_unselected[0], 0, 0, NULL
   }, {		/* toggle select knob selected */
	0, 0, IMAGE_KNOB_WIDTH, IMAGE_KNOB_HEIGHT, IMAGE_KNOB_DEPTH,
	&image_data_toggle_knob_selected[0], 0, 0, NULL
   }, {		/* toggle select check unselected */
	IMAGE_HORIZ_OFFSET, IMAGE_VERT_OFFSET, IMAGE_CHECK_WIDTH,
	IMAGE_CHECK_HEIGHT, 0, NULL, 0, 0, NULL
   }, {		/* toggle select check selected */
	IMAGE_HORIZ_OFFSET, IMAGE_VERT_OFFSET, IMAGE_CHECK_WIDTH,
	IMAGE_CHECK_HEIGHT, IMAGE_CHECK_DEPTH,
	&image_data_toggle_check_selected[0], 0, 0, NULL
   }, {		/* cycle */
	0, 0, IMAGE_CYCLE_WIDTH, IMAGE_CYCLE_HEIGHT, IMAGE_CYCLE_DEPTH,
	&image_data_cycle[0], 0, 0, NULL
   }, {		/* left count */
	0, 0, IMAGE_COUNT_WIDTH, IMAGE_COUNT_HEIGHT, IMAGE_COUNT_DEPTH,
	&image_data_count_left[0], 0, 0, NULL
   }, {		/* right count */
	0, 0, IMAGE_COUNT_WIDTH, IMAGE_COUNT_HEIGHT, IMAGE_COUNT_DEPTH,
	&image_data_count_right[0], 0, 0, NULL
   }
};
STATIC struct TextAttr  topaz80_attr = { (STRPTR)"topaz.font", TOPAZ_EIGHTY,
						   FS_NORMAL, FPF_ROMFONT };
	/* Static prototypes */

SHORT calc_color_level(SHORT color);
VOID  init_render_info_images(struct RenderInfo  *ri);
UBYTE get_image_pixel_color(struct Image  *image, USHORT x, USHORT y);
VOID  set_image_pixel_color(struct Image  *image, USHORT x, USHORT y,
							       UBYTE color);
BOOL  create_avail_fonts(struct RenderInfo  *ri);
VOID  free_avail_fonts(struct RenderInfo  *ri);

	/* Static pragmas */

#pragma regcall(calc_color_level(d0))
#pragma regcall(init_render_info_images(a0))
#pragma regcall(get_image_pixel_color(a0,d0,d1))
#pragma regcall(set_image_pixel_color(a0,d0,d1,d2))
#pragma regcall(create_avail_fonts(a0))
#pragma regcall(free_avail_fonts(a0))

	/* Get render info for given screen */

   struct RenderInfo  *
get_render_info(struct Screen  *screen, USHORT flags)
{
   struct RenderInfo  *ri;

   if (ri = AllocMem((LONG)sizeof(struct RenderInfo),
					  (LONG)MEMF_PUBLIC | MEMF_CLEAR)) {
      struct Screen  screen_data;
      USHORT screen_type;

      /* Get screen data - if no screen given then use workbench screen */
      if (!screen) {
	 LONG ilock;

	 /* Get pointer to workbench screen */
	 ilock  = LockIBase(0L);
	 screen = IntuitionBase->FirstScreen;
	 while (screen && (screen->Flags & SCREENTYPE) != WBENCHSCREEN) {
	    screen = screen->NextScreen;
	 }
	 UnlockIBase(ilock);
	 screen_type = WBENCHSCREEN;
      } else {
	 screen_type = CUSTOMSCREEN;
      }
      if (GetScreenData((BYTE *)&screen_data, (LONG)sizeof(struct Screen),
				       (LONG)screen_type, screen) == TRUE) {
	 struct Image  *image;
	 USHORT *data;
	 ULONG  data_size;
	 SHORT  i;

	 /* Get screen and window data */
	 ri->ri_Screen             = screen;
	 screen                    = &screen_data;
	 ri->ri_ScreenWidth        = screen->Width;
	 ri->ri_ScreenHeight       = screen->Height;
	 ri->ri_ScreenDepth        = screen->RastPort.BitMap->Depth;
	 ri->ri_WindowBorderTop    = screen->BarHeight -
				       screen->BarVBorder + screen->WBorTop;
	 ri->ri_WindowBorderLeft   = screen->WBorLeft;
	 ri->ri_WindowBorderRight  = screen->WBorRight;
	 ri->ri_WindowBorderBottom = screen->WBorBottom;
	 ri->ri_Flags              = flags;
	 ri->ri_ID                 = ISUP_ID;

	 /* Calc chipmem buffer size for image data */
	 for (i = 0, image = &images[0], data_size = 0;
				      i < MAX_RENDER_IMAGES; i++, image++) {
	    if (image->ImageData) {
	       data_size += (image->Width / 16 + (image->Width & 15 ? 1 : 0))
				   * image->Height * ri->ri_ScreenDepth * 2;
	    }
	 }
	 if (data = AllocMem(data_size,
			      (LONG)MEMF_PUBLIC | MEMF_CHIP | MEMF_CLEAR)) {
	    SHORT num_colors;

	    /* Insert image data buffer and size */
	    ri->ri_ImageData     = data;
	    ri->ri_ImageDataSize = data_size;

	    /* Calculate render colors */
	    if ((num_colors = 1 << ri->ri_ScreenDepth) > MAX_RENDER_COLORS) {
	       num_colors = MAX_RENDER_COLORS;
	    }
	    if (num_colors < 3) {

	       /* Set render pens for monochrome screen */
	       if (flags & RENDER_INFO_FLAG_BACK_FILL) {
		  ri->ri_HighlightPen = 0;
		  ri->ri_ShadowPen    = 0;
		  ri->ri_TextPen1     = 0;
		  ri->ri_TextPen2     = 0;
		  ri->ri_BackPen      = 1;
	       } else {
		  ri->ri_HighlightPen = 1;
		  ri->ri_ShadowPen    = 1;
		  ri->ri_TextPen1     = 1;
		  ri->ri_TextPen2     = 1;
		  ri->ri_BackPen      = 0;
	       }
	    } else {
	       struct ColorMap  *cmap;
	       SHORT back_color, max_diff, colors[MAX_RENDER_COLORS],
		     color_levels[MAX_RENDER_COLORS],
		     pens[MAX_RENDER_COLORS];

	       /* Get current screen colors */
	       Forbid();
	       for (i = 0, cmap = screen->ViewPort.ColorMap; i < num_colors;
								      i++) {
		  colors[i]       = GetRGB4(cmap, (LONG)i);
		  color_levels[i] = calc_color_level(colors[i]);
		  pens[i]         = i;
	       }
	       Permit();

	       /* Save normal background color */
	       back_color = colors[0];

	       /* Sort colors darkest to brightest (BUBBLE SORT) */
	       for (i = 0; i < (num_colors - 1); i++) {
		  SHORT j;

		  for (j = i + 1; j < num_colors; j++) {
		     if (color_levels[i] > color_levels[j]) {
			SHORT temp;

			/* Swap colors, color levels and pens */
			temp      = colors[i];
			colors[i] = colors[j];
			colors[j] = temp;

			temp            = color_levels[i];
			color_levels[i] = color_levels[j];
			color_levels[j] = temp;

			temp    = pens[i];
			pens[i] = pens[j];
			pens[j] = temp;
		     }
		  }
	       }
	       if (flags & RENDER_INFO_FLAG_BACK_FILL) {

		  /* Set render pens - highlight pen first */
		  ri->ri_HighlightPen = pens[num_colors - 1];   /* use brightest color */

		  /* Set shadow pen */
		  ri->ri_ShadowPen = pens[0];   /* use darkest color */

		  /* Set back pen */
		  i = 1;   /* use 2nd darkest color */
		  if (! pens[i]) {   /* don't use normal background color */
		     i++;
		  }
		  ri->ri_BackPen = pens[i];
		  back_color     = colors[i];   /* new background color */
	       } else {

		  /* Set render pens - highlight pen first */
		  i = num_colors - 1;   /* use brightest color */
		  if (! pens[i]) {   /* don't use normal background color */
		     i--;
		  }
		  ri->ri_HighlightPen = pens[i];

		  /* Set shadow pen */
		  i = 0;   /* use darkest color */
		  if (! pens[i]) {   /* don't use normal background color */
		     i++;
		  }
		  ri->ri_ShadowPen = pens[i];

		  /* Set back pen */
		  ri->ri_BackPen = 0;
	       }

	       /* Set text pen 1 - use color with greatest difference to
		* back color */
	       for (i = 0, max_diff = 0; i < num_colors; i++) {
		  SHORT diff;

		  if ((diff = calc_color_difference(colors[i],
						  back_color)) > max_diff) {
		     max_diff        = diff;
		     ri->ri_TextPen1 = pens[i];
		  }
	       }

	       /* Set text pen 2 - use color with greatest difference to
		* back color and other color than highlight or shadow pen */
	       for (i = 0, max_diff = 0; i < num_colors; i++) {
		  SHORT diff;

		  if ((diff = calc_color_difference(colors[i],
						  back_color)) > max_diff) {
		     SHORT text1_pen = ri->ri_TextPen1, text2_pen = pens[i];

		     /* Don't use text 1 pen again */
		     if (text2_pen != text1_pen) {

			/* Use special algorithm for 1 and 2 bitplanes */
			if (num_colors < 5) {

			   /* Try to get the pen not used for highlight and
			    * shadow color if this isn't already used for
			    * text 1 color */
			   if ((text1_pen != ri->ri_HighlightPen &&
					   text1_pen != ri->ri_ShadowPen) ||
					(text2_pen != ri->ri_HighlightPen &&
					   text2_pen != ri->ri_ShadowPen)) {
			      max_diff        = diff;
			      ri->ri_TextPen2 = pens[i];
			   }
			} else {

			   /* Try to get the best one from pens not used for
			    * highlight and shadow color */
			   if (text2_pen != ri->ri_HighlightPen &&
					    text2_pen != ri->ri_ShadowPen) {
			      max_diff        = diff;
			      ri->ri_TextPen2 = pens[i];
			   }
			}
		     }
		  }
	       }
            }

	    /* Get font and attributes for rendering texts */
	    if (!(flags & RENDER_INFO_FLAG_AVAIL_FONTS) ||
					   create_avail_fonts(ri) == TRUE) {
	       struct TextFont  *tf;

	       /* First try to open screen font - if any */
	       Forbid();
	       if (!screen->Font) {
		  tf = NULL;
	       } else {
		  tf = open_font(ri, screen->Font);
	       }
	       Permit();
	       if (tf) {
		  struct TextAttr  *ta = &ri->ri_TextAttr;

		  /* Build text attribute structure from screen font */
		  ta->ta_Name  = (STRPTR)tf->tf_Message.mn_Node.ln_Name;
		  ta->ta_YSize = tf->tf_YSize;
		  ta->ta_Style = tf->tf_Style;
		  ta->ta_Flags = tf->tf_Flags;
	       } else {

		  /* Fall back to TOPAZ80 */
		  if (tf = open_font(ri, &topaz80_attr)) {
		     CopyMem((BYTE *)&topaz80_attr, (BYTE *)&ri->ri_TextAttr,
					     (LONG)sizeof(struct TextAttr));
		  }
	       }
	       if (ri->ri_TextFont = tf) {
		  init_render_info_images(ri);
		  return(ri);
	       }
	       free_avail_fonts(ri);
	    }
	 }
      }
      FreeMem(ri, (LONG)sizeof(struct RenderInfo));
   }
   return(NULL);
}
	/* Free render info */

   VOID
free_render_info(struct RenderInfo  *ri)
{
   if (ri && ri->ri_ID == ISUP_ID) {
      if (ri->ri_TextFont) {
	 CloseFont(ri->ri_TextFont);
      }
      free_avail_fonts(ri);
      FreeMem(ri->ri_ImageData, ri->ri_ImageDataSize);
      FreeMem(ri, (LONG)sizeof(struct RenderInfo));
   }
}
	/* Calc color level with specified scaling values */

   STATIC SHORT
calc_color_level(SHORT color)
{
   return(calc_color_difference(color, 0));
}
	/* Calc color difference with specified scaling values */

   SHORT   /* non static - needed by menus.c too */
calc_color_difference(SHORT color1, SHORT color2)
{
   USHORT temp, diff;

   temp  = (color1 & 0x00f) - (color2 & 0x00f);
   diff  = temp * temp * BLUE_SCALE;
   temp  = ((color1 >> 4) & 0x00f) - ((color2 >> 4) & 0x00f);
   diff += temp * temp * GREEN_SCALE;
   temp  = ((color1 >> 8) & 0x00f) - ((color2 >> 8) & 0x00f);
   diff += temp * temp * RED_SCALE;
   return(diff);
}
	/* Init render info images */

   STATIC VOID
init_render_info_images(struct RenderInfo *ri)
{
   struct Image  *image_src = &images[0], *image_dest = &ri->ri_Images[0];
   USHORT i, *data = ri->ri_ImageData;

   CopyMem((BYTE *)image_src, (BYTE *)image_dest,
			  (LONG)(MAX_RENDER_IMAGES * sizeof(struct Image)));
   for (i = 0; i < MAX_RENDER_IMAGES; i++, image_src++, image_dest++) {

      /* Set image depth first - needed by pixel functions */
      image_dest->Depth = ri->ri_ScreenDepth;

      /* Init image data if any */
      if (image_src->ImageData) {
	 USHORT y, width = image_src->Width, height = image_src->Height,
		depth = image_dest->Depth;   /* DEST image depth !!! */

	 /* Set data buffer ptr */
	 image_dest->ImageData = data;
	 image_dest->PlanePick = (1 << depth) - 1;
	 data += (width / 16 + (width & 15 ? 1 : 0)) * height * depth;

	 /* Convert image data to current colors */
	 for (y = 0; y < height; y++) {
	    USHORT x;

	    for (x = 0; x < width; x++) {
	       UBYTE color;

	       switch(get_image_pixel_color(image_src, x, y)) {
		  case 0 :   /* back pen */
		     color = ri->ri_BackPen;
		     break;

		  case 1 :   /* highlight pen */
		     color = ri->ri_HighlightPen;
		     break;

		  case 2 :   /* shadow pen */
		     color = ri->ri_ShadowPen;
		     break;

		  default :   /* special pen */
		     color = ri->ri_TextPen2;
		     break;
	       }
	       set_image_pixel_color(image_dest, x, y, color);
	    }
	 }
      } else {
	 UBYTE color;

	 /* No image data -> monochrome rectangle */
	 switch(image_src->PlaneOnOff) {
	    case 0 :   /* back pen */
	       color = ri->ri_BackPen;
	       break;

	    case 1 :   /* highlight pen */
	       color = ri->ri_HighlightPen;
	       break;

	    case 2 :   /* shadow pen */
	       color = ri->ri_ShadowPen;
	       break;

	    default :   /* special pen */
	       color = ri->ri_TextPen2;
	       break;
	 }
	 image_dest->PlaneOnOff = color;
      }
   }
}
	/* Get color of image pixel */

   STATIC UBYTE
get_image_pixel_color(struct Image  *image, USHORT x, USHORT y)
{
   USHORT modulo = image->Width / 16 + (image->Width & 15 ? 1 : 0),
	  *data = image->ImageData + y * modulo + x / 16,
	  data_offset = image->Height * modulo,
	  bit_mask = 0x8000 >> (x & 15);
   UBYTE  i, depth = image->Depth, color_mask = 1, color = 0;

   for (i = 0; i < depth; i++, data += data_offset, color_mask <<= 1) {
      if (*data & bit_mask) {
	 color |= color_mask;
      }
   }
   return(color);
}
	/* Set color of image pixel */

   STATIC VOID
set_image_pixel_color(struct Image  *image, USHORT x, USHORT y, UBYTE color)
{
   USHORT modulo = image->Width / 16 + (image->Width & 15 ? 1 : 0),
	  *data = image->ImageData + y * modulo + x / 16,
	  data_offset = image->Height * modulo,
	  bit_mask = 0x8000 >> (x & 15);
   UBYTE  i, depth = image->Depth;

   for (i = 0; i < depth; i++, data += data_offset, color >>= 1) {
      if (color & 1) {
	 *data |= bit_mask;
      }
   }
}
	/* Open window */

   struct Window *
open_window(struct RenderInfo  *ri, struct NewWindow  *org_nw, USHORT flags)
{
   struct Window  *win = NULL;

   if (ri && ri->ri_ID == ISUP_ID && org_nw) {
      struct NewWindow  new_nw = *org_nw, *nw = &new_nw;
      SHORT  left_edge = nw->LeftEdge, top_edge = nw->TopEdge;
      USHORT width = nw->Width, height = nw->Height,
	     max_width = ri->ri_ScreenWidth, max_height = ri->ri_ScreenHeight;

      /* Scale new window dimension */
      if (width > max_width) {
	 width     = max_width;
	 left_edge = 0;
      } else {
	 if ((left_edge + width) > max_width) {
	    left_edge = max_width - width;
	 }
      }
      if (height > max_height) {
	 height   = max_height;
	 top_edge = 0;
      } else {
	 if ((top_edge + height) > max_height) {
	    top_edge = max_height - height;
	 }
      }

      /* Change new window struct */
      if (flags & OPEN_WINDOW_FLAG_RENDER_PENS) {

	 /* Set colors */
	 if (ri->ri_Flags & RENDER_INFO_FLAG_BACK_FILL) {
	    nw->DetailPen = ri->ri_TextPen1;
	    nw->BlockPen  = ri->ri_BackPen;
	 } else {
	    nw->DetailPen = ri->ri_BackPen;
	    nw->BlockPen  = ri->ri_TextPen1;
	 }
      }
      if (ri->ri_Flags & RENDER_INFO_FLAG_INNER_WINDOW) {
	 USHORT hoffset = ri->ri_WindowBorderLeft,
		voffset = ri->ri_WindowBorderTop;

	 /* Change width and left edge for inner window dimension */
	 if ((width += hoffset + ri->ri_WindowBorderRight) > max_width) {
	    width     = max_width;
	    left_edge = 0;
	 } else {
	    if (left_edge > hoffset) {
	       left_edge -= hoffset;
	       if ((left_edge + width) > max_width) {
		  left_edge = max_width - width;
	       }
	    } else {
	       left_edge = 0;
	    }
	 }

	 /* Change height and top edge for inner window dimension */
	 if ((height += voffset + ri->ri_WindowBorderBottom) > max_height) {
	    height   = max_height;
	    top_edge = 0;
	 } else {
	    if (top_edge > voffset) {
	       top_edge -= voffset;
	       if ((top_edge + height) > max_height) {
		  top_edge = max_height - height;
	       }
	    } else {
	       top_edge = 0;
	    }
	 }
      }

      /* Center window AFTER calculation of inner window dimension */
      if (flags & OPEN_WINDOW_FLAG_CENTER_SCREEN) {

	 /* Center window on screen */
	 left_edge = (max_width - width) / 2;
	 top_edge  = (max_height - height) / 2;
      } else {
	 if (flags & OPEN_WINDOW_FLAG_CENTER_MOUSE) {

	    /* Center window over current position of mouse pointer */
	    left_edge = ri->ri_Screen->MouseX - width / 2;
	    top_edge  = ri->ri_Screen->MouseY - height / 2;

	    /* Scale window position according to screen dimension */
	    if (left_edge < 0) {
	       left_edge = 0;
	    } else {
	       if (left_edge > (max_width - width)) {
		  left_edge = max_width - width;
	       }
	    }
	    if (top_edge < 0) {
	       top_edge = 0;
	    } else {
	       if (top_edge > (max_height - height)) {
		  top_edge = max_height - height;
	       }
	    }
	 }
      }

      /* Insert position + dimension of window and try to open it */
      nw->LeftEdge = left_edge;
      nw->TopEdge  = top_edge;
      nw->Width    = width;
      nw->Height   = height;
      if (win = OpenWindow(nw)) {

	 /* Finally fill window background */
	 if (ri->ri_Flags & RENDER_INFO_FLAG_BACK_FILL) {
	    clear_window(ri, win, 0, 0, width, height, 0);
	 }
      }
   }
   return(win);
}
	/* Clear given rectangle of window with data from render info */

   VOID
clear_window(struct RenderInfo  *ri, struct Window  *win, USHORT left_edge,
		 USHORT top_edge, USHORT width, USHORT height, USHORT flags)
{
   if (ri && ri->ri_ID == ISUP_ID && win) {
      USHORT inner_left, inner_top, inner_width, inner_height,
	     ri_flags = ri->ri_Flags;

      /* Calc inner window dimension */
      if ((ri_flags & RENDER_INFO_FLAG_INNER_WINDOW) ||
				  (ri_flags & RENDER_INFO_FLAG_BACK_FILL)) {
	 inner_left   = ri->ri_WindowBorderLeft;
	 inner_top    = ri->ri_WindowBorderTop;
	 inner_width  = win->Width - inner_left - ri->ri_WindowBorderRight;
	 inner_height = win->Height - inner_top - ri->ri_WindowBorderBottom;
      } else {
	 inner_left   = 0;
	 inner_top    = 0;
	 inner_width  = win->Width;
	 inner_height = win->Height;
      }

      /* Check if given rectangle fits into inner window */
      if ((SHORT)left_edge >= 0 && (SHORT)top_edge >= 0 && width &&
			 height && left_edge < (inner_left + inner_width) &&
				    top_edge < (inner_top + inner_height)) {
	 struct RastPort  *rp = win->RPort;

	 /* Add offsets for inner window to given upper left position */
	 if (!(flags & CLEAR_WINDOW_FLAG_ABSOLUTE_POS)) {
	    left_edge += inner_left;
	    top_edge  += inner_top;

	    /* Scale upper left position of given rectangle if neccessary */
	    if (left_edge < inner_left) {
	       left_edge = inner_left;
	    }
	    if (top_edge < inner_top) {
	       top_edge = inner_top;
	    }
	 }

	 /* Scale dimension of given rectangle if neccessary */
	 if ((SHORT)width <= 0 || (left_edge + width) > (inner_left +
							     inner_width)) {
	    width = inner_left + inner_width - left_edge;
	 }
	 if ((SHORT)height <= 0 || (top_edge + height) > (inner_top +
							    inner_height)) {
	    height = inner_top + inner_height - top_edge;
	 }

	 /* Set draw mode, colors and fill pattern for using raster */
	 if (flags & CLEAR_WINDOW_FLAG_USE_RASTER) {
	    STATIC USHORT raster_pattern[] = { 0xaaaa, 0x5555 };

	    SetDrMd(rp, (LONG)JAM2);
	    SetAPen(rp, (LONG)ri->ri_HighlightPen);
	    SetBPen(rp, (LONG)ri->ri_BackPen);
	    SetAfPt(rp, &raster_pattern[0], 1L);   /* macro !!! */
	 } else {

	    /* Set draw mode */
	    if (!(flags & CLEAR_WINDOW_FLAG_CUSTOM_DRAW_MODE)) {
	       SetDrMd(rp, (LONG)JAM1);
	    }

	    /* Set fill colors */
	    if (!(flags & CLEAR_WINDOW_FLAG_CUSTOM_COLOR)) {
	       USHORT apen;

	       /* Check if special color needed */
	       if (flags & CLEAR_WINDOW_FLAG_TEXT1_COLOR) {
		  apen = ri->ri_TextPen1;
	       } else {
		  if (flags & CLEAR_WINDOW_FLAG_TEXT2_COLOR) {
		     apen = ri->ri_TextPen2;
		  } else {

		     /* Use background color */
		     if ((ri_flags & RENDER_INFO_FLAG_BACK_FILL) &&
				!(flags & CLEAR_WINDOW_FLAG_NORMAL_COLOR)) {
			apen = ri->ri_BackPen;
		     } else {
			apen = 0;
		     }
		  }
	       }
	       SetAPen(rp, (LONG)apen);
	    }
	 }

	 /* And do clearing window background */
	 RectFill(rp, (LONG)left_edge, (LONG)top_edge, (LONG)(left_edge +
				 width - 1), (LONG)(top_edge + height - 1));

	 /* Clear fill pattern */
	 if (flags & CLEAR_WINDOW_FLAG_USE_RASTER) {
	    SetAfPt(rp, NULL, 1L);   /* macro !!! */
	 }
      }
   }
}
	/* Close window safely, even if shared user port */

   VOID
close_window(struct Window  *win, BOOL more_windows)
{
   if (win) {

      /* Check if any IDCMP flag used */
      if (win->UserPort) {
	 struct IntuiMessage  *msg, *succ;

	 /* Reply all messages for given window */
	 Forbid();
	 msg = (struct IntuiMessage *)win->UserPort->mp_MsgList.lh_Head;
	 while (succ = (struct IntuiMessage *)
					 msg->ExecMessage.mn_Node.ln_Succ) {
	    if (more_windows == FALSE || msg->IDCMPWindow == win) {
	       Remove((struct Node *)msg);
	       ReplyMsg((struct Message *)msg);
	    }
	    msg = succ;
	 }

	 /* Don't close shared user port */
	 if (more_windows == TRUE) {
	    win->UserPort = NULL;
	    ModifyIDCMP(win, 0L);
	 }
	 Permit();
      }
      CloseWindow(win);
   }
}
	/* Create list of available fonts - called by create_render_info() */

   STATIC BOOL
create_avail_fonts(struct RenderInfo  *ri)
{
   struct AvailFontsHeader  *afh;
   LONG size = DEFAULT_AVAIL_FONTS_BUFFER_SIZE;
   BOOL success = FALSE;

   /* Allocate buffer and fill it with data from available fonts */
   if (afh = AllocMem(size, (LONG)MEMF_PUBLIC)) {
      LONG new_size;

      if (new_size = AvailFonts((BYTE *)afh, size,
					     (LONG)AFF_MEMORY | AFF_DISK)) {
	 /* Allocate larger buffer and try again */
	 FreeMem(afh, size);
	 size += new_size;
	 if (afh = AllocMem(size, (LONG)MEMF_PUBLIC)) {
	    AvailFonts((BYTE *)afh, size, (LONG)AFF_MEMORY | AFF_DISK);
	 }
      }

      /* Save font header in render info data structure */
      ri->ri_AvailFontsHeader     = afh;
      ri->ri_AvailFontsHeaderSize = size;
      success = TRUE;
   }
   return(success);
}
	/* Free list of available fonts - called by free_render_info() */

   STATIC VOID
free_avail_fonts(struct RenderInfo  *ri)
{
   if (ri->ri_AvailFontsHeader && ri->ri_AvailFontsHeaderSize) {
      FreeMem(ri->ri_AvailFontsHeader, ri->ri_AvailFontsHeaderSize);
   }
}
	/* Return ptr to list of available fonts */

   struct AvailFontsHeader *
avail_fonts(struct RenderInfo  *ri)
{
   struct AvailFontsHeader  *afh = NULL;

   if (ri && ri->ri_ID == ISUP_ID &&
			    (ri->ri_Flags & RENDER_INFO_FLAG_AVAIL_FONTS)) {
      afh = ri->ri_AvailFontsHeader;
   }
   return(afh);
}
	/* Check if given font is available and return its correct data */

   struct TextAttr *
ask_font(struct RenderInfo  *ri, struct TextAttr  *ta)
{
   struct TextAttr  *best_ta = NULL;

   if (ri && ri->ri_ID == ISUP_ID && ta) {
      if (!(ri->ri_Flags & RENDER_INFO_FLAG_AVAIL_FONTS)) {
	 best_ta = ta;
      } else {
	 struct AvailFontsHeader  *afh = ri->ri_AvailFontsHeader;
	 USHORT num = afh->afh_NumEntries;

	 if (num) {
	    struct AvailFonts  *af = (struct AvailFonts *)(afh + 1);
	    USHORT search_ysize = ta->ta_YSize, best_ysize = 0;

	    do {
	       struct TextAttr  *avail_ta = &af++->af_Attr;
	       int stricmp(const char *_s1, const char *_s2);   /* manually prototyping non ANSI function */

	       if (!stricmp((BYTE *)avail_ta->ta_Name, (BYTE *)ta->ta_Name)) {
		  USHORT avail_ysize = avail_ta->ta_YSize;

		  if (avail_ysize == search_ysize) {
		     best_ta = avail_ta;
		     break;
		  } else {
		     if (avail_ysize < search_ysize) {
			if (avail_ysize > best_ysize) {
			   best_ysize = avail_ysize;
			   best_ta    = avail_ta;
			}
		     } else {
			if (!best_ysize || avail_ysize < best_ysize) {
			   best_ysize = avail_ysize;
			   best_ta    = avail_ta;
			}
		     }
		  }
	       }
	    } while (--num);
	 }
      }
   }
   return(best_ta);
}
	/* Open best available text font */

   struct TextFont *
open_font(struct RenderInfo  *ri, struct TextAttr  *ta)
{
   struct TextFont  *tf = NULL;

   if (ri && ri->ri_ID == ISUP_ID && ta) {
      if (ta = ask_font(ri, ta)) {
	 if (!(tf = OpenFont(ta)) || tf->tf_YSize != ta->ta_YSize) {
	    if (tf) {
	       CloseFont(tf);
	    }
	    tf = OpenDiskFont(ta);
	 }
      }
   }
   return(tf);
}
