		/*************************************
		 *                                   *
		 *            Render v2.0            *
		 *   by Torsten Jürgeleit in 05/91   *
		 *                                   *
		 *              Routines             *
		 *                                   *
		 *************************************/

	/* Includes */

#include <exec/types.h>
#include <exec/memory.h>
#include <intuition/intuition.h>
#include <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

	/* 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[MAX_RENDER_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);
	/* 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))

	/* Get render info for given screen */

   struct RenderInfo  *
get_render_info(struct Screen  *screen, USHORT flags)
{
   struct RenderInfo  *ri = NULL;
   struct Screen      wb_screen;
   struct ColorMap    *cmap;
   struct Image       *image = &images[0];
   USHORT *data;
   ULONG  data_size = 0;
   SHORT  i, j, temp, num_colors, back_color, colors[MAX_RENDER_COLORS],
	  color_levels[MAX_RENDER_COLORS], pens[MAX_RENDER_COLORS];

   if (ri = AllocMem((LONG)sizeof(struct RenderInfo),
					  (LONG)MEMF_PUBLIC | MEMF_CLEAR)) {
      /* If no screen given then use workbench screen */
      if (! screen) {
	 GetScreenData((BYTE *)(screen = &wb_screen), (LONG)
				  sizeof(struct Screen), (LONG)WBENCHSCREEN,
						     (struct Screen *)NULL);
      }

      /* Get screen and window 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;

      /* Calc gadget chipmem data buffer size */
      for (i = 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))) {
	 FreeMem(ri, (LONG)sizeof(struct RenderInfo));
	 ri = NULL;
      } else {
	 /* 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 = OpenFont(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 = OpenFont(&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 {

	    /* Get current screen colors */
	    Forbid();
	    cmap = screen->ViewPort.ColorMap;
	    for (i = 0; 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++) {
	       for (j = i + 1; j < num_colors; j++) {
		  if (color_levels[i] > color_levels[j]) {

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

	       /* Set back pen */
	       if (! pens[i = 1]) {   /* 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 */
	       if (! pens[i = num_colors - 1]) {   /* don't use normal background color */
		  i--;
	       }
	       ri->ri_HighlightPen = pens[i];

	       /* Set shadow pen */
	       if (! pens[i = 0]) {
		  i++;
	       }
	       ri->ri_ShadowPen = pens[i];

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

	    /* Set text pen 1 */
	    for (i = 0, j = 0; i < num_colors; i++) {
	       if ((temp = calc_color_difference(colors[i],
							 back_color)) > j) {
		  j               = temp;
		  ri->ri_TextPen1 = pens[i];
	       }
	    }

	    /* Set text pen 2 */
	    for (i = 0, j = 0; i < num_colors; i++) {
	       if ((temp = calc_color_difference(colors[i],
			   back_color)) > j && pens[i] != ri->ri_TextPen1) {
		  j               = temp;
		  ri->ri_TextPen2 = pens[i];
	       }
	    }
         }
         init_render_info_images(ri);
      }
   }
   return(ri);
}
	/* 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];
   UBYTE  color;
   USHORT i, x, y, *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++) {
      image_dest->Depth = ri->ri_ScreenDepth;
      if (image_src->ImageData) {

	 /* Set data buffer ptr */
	 image_dest->ImageData = data;
	 image_dest->PlanePick = (1 << image_dest->Depth) - 1;
	 data += (image_dest->Width / 16 + (image_dest->Width & 15 ? 1 :
			       0)) * image_dest->Height * image_dest->Depth;
	 /* Convert image data */
	 for (y = 0; y < image_src->Height; y++) {
	    for (x = 0; x < image_src->Width; x++) {
	       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;
		  case 3 :   /* special pen */
		     color = ri->ri_TextPen2;
		     break;
	       }
	       set_image_pixel_color(image_dest, x, y, color);
	    }
	 }
      } else {

	 /* 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;
	    case 3 :   /* 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)
{
   UBYTE  i, depth = image->Depth, color_mask = 1, color = 0;
   USHORT modulo = image->Width / 16 + (image->Width & 15 ? 1 : 0),
	  *data = image->ImageData + y * modulo + x / 16,
	  bit_mask = 0x8000 >> (x & 15);

   for (i = 0; i < depth; i++, data += image->Height * modulo,
							 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)
{
   UBYTE  i, depth = image->Depth;
   USHORT modulo = image->Width / 16 + (image->Width & 15 ? 1 : 0),
	  *data = image->ImageData + y * modulo + x / 16,
	  bit_mask = 0x8000 >> (x & 15);

   for (i = 0; i < depth; i++, data += image->Height * modulo, color >>= 1) {
      if (color & 1) {
	 *data |= bit_mask;
      }
   }
}
	/* Free render info */

   VOID
free_render_info(struct RenderInfo  *ri)
{
   if (ri) {
      if (ri->ri_TextFont) {
	 CloseFont(ri->ri_TextFont);
      }
      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 NewWindow  *nw, new_nw;
   struct Window     *win = NULL;

   if (ri && org_nw) {

      /* Duplicate given new window struct */
      CopyMem((BYTE *)org_nw, (BYTE *)(nw = &new_nw), (LONG)
						  sizeof(struct NewWindow));
      /* Scale new window dimension */
      if (nw->Width > ri->ri_ScreenWidth) {
	 nw->LeftEdge = 0;
	 nw->Width    = ri->ri_ScreenWidth;
      } else {
	 if ((nw->LeftEdge + nw->Width) > ri->ri_ScreenWidth) {
	    nw->LeftEdge = ri->ri_ScreenWidth - nw->Width;
	 }
      }
      if (nw->Height > ri->ri_ScreenHeight) {
	 nw->TopEdge = 0;
	 nw->Height  = ri->ri_ScreenHeight;
      } else {
	 if ((nw->TopEdge + nw->Height) > ri->ri_ScreenHeight) {
	    nw->TopEdge = ri->ri_ScreenHeight - nw->Height;
	 }
      }

      /* Change new window struct */
      if (flags & OPEN_WINDOW_FLAG_CENTER_WINDOW) {
	 nw->LeftEdge = (ri->ri_ScreenWidth - nw->Width) / 2;
	 nw->TopEdge  = (ri->ri_ScreenHeight - 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) > ri->ri_ScreenWidth) {
	    nw->Width = ri->ri_ScreenWidth - nw->LeftEdge;
	 }
	 nw->Height += ri->ri_WindowBorderTop + ri->ri_WindowBorderBottom;
	 if ((nw->TopEdge + nw->Height) > ri->ri_ScreenHeight) {
	    nw->Height = ri->ri_ScreenHeight - nw->TopEdge;
	 }
      }
      if (win = OpenWindow(nw)) {
	 if (ri->ri_Flags & RENDER_INFO_FLAG_BACK_FILL) {
	    clear_render_window(ri, win, 0, 0, nw->Width, nw->Height);
	 }
      }
   }
   return(win);
}
	/* Clear given rectangle of window with data from render info */

   VOID
clear_render_window(struct RenderInfo  *ri, struct Window  *win,
	     USHORT left_edge, USHORT top_edge, USHORT width, USHORT height)
{
   struct RastPort  *rp = win->RPort;
   USHORT inner_left, inner_top, inner_width, inner_height, back_pen,
	  flags = ri->ri_Flags;

   /* Calc inner window dimension */
   if ((flags & RENDER_INFO_FLAG_INNER_WINDOW) ||
				     (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 and if not then scale it */
   if ((SHORT)left_edge >= 0 && (SHORT)top_edge >= 0 && width && height &&
	   left_edge < (inner_left + inner_width) && top_edge < (inner_top +
							    inner_height)) {
      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;
      }
      if (flags & RENDER_INFO_FLAG_BACK_FILL) {
	 back_pen = ri->ri_BackPen;
      } else {
	 back_pen = 0;
      }
      SetDrMd(rp, (LONG)JAM1);
      SetAPen(rp, (LONG)back_pen);
      RectFill(rp, (LONG)left_edge, (LONG)top_edge, (LONG)(left_edge +
				 width - 1), (LONG)(top_edge + height - 1));
   }
}
