/* $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 .: 1
 *
 *	Date        Author                 Comment
 *	=========   ====================   ====================
 *	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>
#include <functions.h>
#include <libraries/memwatch.h>		/* 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		20000

	/* 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);
struct AvailFontsHeader  *create_avail_fonts(VOID);
VOID  free_avail_fonts(struct AvailFontsHeader  *afh);

	/* 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(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) {
	 screen_type = WBENCHSCREEN;
      } else {
	 screen_type = CUSTOMSCREEN;
      }
      if (GetScreenData((BYTE *)&screen_data, (LONG)sizeof(struct Screen),
				       (LONG)screen_type, screen) == TRUE) {
	 /* Get screen and window data */
	 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;

	 /* Create list with available fonts */
	 if (!(flags & RENDER_INFO_FLAG_AVAIL_FONTS) ||
			 (ri->ri_AvailFontsHeader = create_avail_fonts())) {
	    struct Image  *image;
	    USHORT *data;
	    ULONG  data_size;
	    SHORT  i;

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

	       /* Open screen text font and clone screen text attr struct */
	       Forbid();
	       if (screen->Font) {
		  ri->ri_TextFont = open_font(ri, screen->Font);
	       }
	       Permit();
	       if (ri->ri_TextFont) {
		  ri->ri_TextAttr.ta_Name  = (STRPTR)
				ri->ri_TextFont->tf_Message.mn_Node.ln_Name;
		  ri->ri_TextAttr.ta_YSize = ri->ri_TextFont->tf_YSize;
		  ri->ri_TextAttr.ta_Style = ri->ri_TextFont->tf_Style;
		  ri->ri_TextAttr.ta_Flags = ri->ri_TextFont->tf_Flags;
	       } else {
		  ri->ri_TextFont = open_font(ri, &topaz80_attr);
		  CopyMem((BYTE *)&topaz80_attr, (BYTE *)&ri->ri_TextAttr,
					     (LONG)sizeof(struct TextAttr));
	       }

	       /* Examine screen 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 */
		  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];

		     /* 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]) {
			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 2nd 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 &&
					       pens[i] != ri->ri_TextPen1) {
			max_diff        = diff;
			ri->ri_TextPen2 = pens[i];
		     }
		  }
               }
	       init_render_info_images(ri);
	       return(ri);
	    }
	    free_avail_fonts(ri->ri_AvailFontsHeader);
	 }
      }
      FreeMem(ri, (LONG)sizeof(struct RenderInfo));
   }
   return(NULL);
}
	/* Create list of available fonts - called by create_render_info() */

   STATIC struct AvailFontsHeader *
create_avail_fonts(VOID)
{
   struct AvailFontsHeader  *afh;
   LONG old_size = DEFAULT_AVAIL_FONTS_BUFFER_SIZE;

   if (afh = AllocMem(old_size, (LONG)MEMF_PUBLIC)) {
      LONG new_size;

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

	 /* Allocate buffer required for font list and copy list into it */
	 new_size = old_afh->afh_NumEntries * sizeof(struct AvailFonts) +
					    sizeof(struct AvailFontsHeader);
	 if (afh = AllocMem(new_size, (LONG)MEMF_PUBLIC)) {
	    CopyMem((BYTE *)old_afh, (BYTE *)afh, new_size);
	 }
	 FreeMem(old_afh, old_size);
      }
   }
   return(afh);
}
	/* Free list of available fonts - called by free_render_info() */

   STATIC VOID
free_avail_fonts(struct AvailFontsHeader  *afh)
{
   if (afh) {
      FreeMem(afh, (LONG)(afh->afh_NumEntries * sizeof(struct AvailFonts) +
					  sizeof(struct AvailFontsHeader)));
   }
}
	/* 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;
      }
   }
}
	/* 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->ri_AvailFontsHeader);
      FreeMem(ri->ri_ImageData, ri->ri_ImageDataSize);
      FreeMem(ri, (LONG)sizeof(struct RenderInfo));
   }
}
	/* 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, *nw = &new_nw;
      USHORT max_width = ri->ri_ScreenWidth, max_height = ri->ri_ScreenHeight;

      /* Duplicate given new window struct */
      CopyMem((BYTE *)org_nw, (BYTE *)nw, (LONG)sizeof(struct NewWindow));

      /* Scale new window dimension */
      if (nw->Width > max_width) {
	 nw->LeftEdge = 0;
	 nw->Width    = max_width;
      } else {
	 if ((nw->LeftEdge + nw->Width) > max_width) {
	    nw->LeftEdge = max_width - nw->Width;
	 }
      }
      if (nw->Height > max_height) {
	 nw->TopEdge = 0;
	 nw->Height  = max_height;
      } else {
	 if ((nw->TopEdge + nw->Height) > max_height) {
	    nw->TopEdge = max_height - nw->Height;
	 }
      }

      /* Change new window struct */
      if (flags & OPEN_WINDOW_FLAG_CENTER_WINDOW) {
	 nw->LeftEdge = (max_width - nw->Width) / 2;
	 nw->TopEdge  = (max_height - nw->Height) / 2;
      }
      if (flags & OPEN_WINDOW_FLAG_RENDER_PENS) {
	 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) {
	 nw->Width += ri->ri_WindowBorderLeft + ri->ri_WindowBorderRight;
	 if ((nw->LeftEdge + nw->Width) > max_width) {
	    nw->Width = max_width - nw->LeftEdge;
	 }
	 nw->Height += ri->ri_WindowBorderTop + ri->ri_WindowBorderBottom;
	 if ((nw->TopEdge + nw->Height) > max_height) {
	    nw->Height = max_height - nw->TopEdge;
	 }
      }
      if (win = OpenWindow(nw)) {
	 if (ri->ri_Flags & RENDER_INFO_FLAG_BACK_FILL) {
	    clear_window(ri, win, 0, 0, nw->Width, nw->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;

	 /* Scale rectangle dimension if neccessary */
	 if (left_edge < inner_left) {
	    left_edge  = inner_left;
	 } else {
	    left_edge += inner_left;
	 }
	 if (top_edge < inner_top) {
	    top_edge  = inner_top;
	 } else {
	    top_edge += inner_top;
	 }
	 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 */
	 if (!(flags & CLEAR_WINDOW_FLAG_CUSTOM_DRAW_MODE)) {
	    SetDrMd(rp, (LONG)JAM1);
	 }

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

	    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);
	 }
	 RectFill(rp, (LONG)left_edge, (LONG)top_edge, (LONG)(left_edge +
				 width - 1), (LONG)(top_edge + height - 1));
      }
   }
}
	/* Close window safely, even if shared user port */

   VOID
close_window(struct Window  *win, BOOL more_windows)
{
   if (win) {
      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);
   }
}
	/* 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);
}
