/* Copyright (C) 1990, 1991 Aladdin Enterprises.  All rights reserved.
   Distributed by Free Software Foundation, Inc.

This file is part of Ghostscript.

Ghostscript is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY.  No author or distributor accepts responsibility
to anyone for the consequences of using it or for whether it serves any
particular purpose or works at all, unless he says so in writing.  Refer
to the Ghostscript General Public License for full details.

Everyone is granted permission to copy, modify and redistribute
Ghostscript, but only under the conditions described in the Ghostscript
General Public License.  A copy of this license is supposed to have been
given to you along with Ghostscript so you can know your rights and
responsibilities.  It should be in a file named COPYING.  Among other
things, the copyright notice and this notice must be preserved on all
copies.  */

/* gdevdjgp.c */
/* Ghostscript driver for IBM-PC with DJ's GCC */
/* Driver name : djgpp */

#ifdef USE_libgrx
#include <grx.h>
#else
#include <graphics.h>
#endif
#include <pc.h>
#include "dos_.h"
#include "memory_.h"
#include "gx.h"
#include "gserrors.h"
#include "gxdevice.h"
#include "djgpp.h"

/* Procedures */
/* See gxdevice.h for the definitions of the procedures */

private dev_proc_open_device(djgpp_open);
private dev_proc_close_device(djgpp_close);
private dev_proc_map_rgb_color(djgpp_map_rgb_color);
private dev_proc_map_color_rgb(djgpp_map_color_rgb);
private dev_proc_fill_rectangle(djgpp_fill_rectangle);
private dev_proc_copy_mono(djgpp_copy_mono);
private dev_proc_copy_color(djgpp_copy_color);

typedef byte *fb_ptr;

#define no_color ((int)gx_no_color_index)

/* The color map for dynamically assignable colors. */
#define max_color_count 256
#define max_color_index (max_color_count-1)
#define static_color_count 64
#define first_color_index (static_color_count)

/* djgpp driver uses no special mode selection procedures 
   so structure gx_device_djgpp_s has no more special members
   besides gx_device_common */
   
typedef struct gx_device_djgpp_s gx_device_djgpp;

struct gx_device_djgpp_s {
  gx_device_common;
  int next_color_index;
  ushort dynamic_colors[max_color_count - static_color_count];
};

#define GRAM_BASE ((byte *)PC_VRAM_BASE)
#define fb_dev ((gx_device_djgpp *)dev)

/* Macro for validating rectangle parameters x and w.
 * set_pixel_ptr implicitly validates y and h. 
 */
#define validate_rect()\
  if ( w <= 0 ) return 0;\
  if ( x < 0 || x + w > dev->width ) return -1


/* Procedure records */
#define djgpp_proc_body(output_page_proc) \
{\
    djgpp_open,\
    gx_default_get_initial_matrix,\
    gx_default_sync_output,\
    output_page_proc,\
    djgpp_close,\
    djgpp_map_rgb_color,\
    djgpp_map_color_rgb,\
    djgpp_fill_rectangle,\
    gx_default_tile_rectangle,\
    djgpp_copy_mono,\
    djgpp_copy_color,\
    gx_default_draw_line,\
    gx_default_get_bits,\
	gx_default_get_props,\
	gx_default_put_props,\
}

private gx_device_procs djgpp_procs =
  djgpp_proc_body(gx_default_output_page);

gx_device_djgpp gs_djgpp_device = {
	sizeof(gx_device_djgpp),
	&djgpp_procs,
	"djgpp",
	0, 0,		/* width and height are set in djgpp_open */
	0, 0,		/* horizontal and vertical resolutions are
	               set in djgpp_open */
	no_margins,
	dci_color(8, 31, 4),
	0		/* not opened yet */
};

private int djgpp_open(gx_device *dev)
{
    int c;
    GrSetMode(GR_default_graphics);
    dev->width = GrSizeX();
    dev->height = GrSizeY();
    dev->x_pixels_per_inch = dev->y_pixels_per_inch = dev->height / 11.0;
    
    while (GrAllocCell() != GrNOCOLOR) ;
    for( c = 0; c < static_color_count ; c++ ){
        static byte c2[10] = {0, 168, 0, 0, 0, 0, 0, 0, 84, 252};
        GrSetColor(c, c2[(c >> 2) & 9], c2[(c >> 1) & 9], c2[ c & 9]);
    }
    fb_dev->next_color_index = first_color_index;
    return 0;
}

private int djgpp_close(gx_device *dev)
{
    GrSetMode(GR_default_text);
    return 0;
}

private gx_color_index
djgpp_map_rgb_color(gx_device *dev, gx_color_value r, gx_color_value g, gx_color_value b)
{
#define cv_bits(v,n) ((v) >> ( gx_color_value_bits - (n)))
    ushort r5 = cv_bits(r, 5), g5 = cv_bits(g, 5), b5 = cv_bits(b, 5);
    static byte cube_bits[32] =
       {   0, 128, 128, 128, 128, 128, 128, 128, 128, 128,
           8, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
           1, 128, 128, 128, 128, 128, 128, 128, 128, 128,
           9
       };
    int cx = ((uint)cube_bits[r5] << 2) + ((uint)cube_bits[g5] << 1) +
              (uint)cube_bits[b5];
    ushort rgb;
    /* Check for a color on the cube. */
    if ( cx < static_color_count)
      return (gx_color_index) cx;
    /* Not on the cube, chech the dynamic color table. */
    rgb = (r5 << 10) + (g5 << 5) + b5;
    for (cx=fb_dev->next_color_index-first_color_index;cx>=0;cx--) {
		  if ( fb_dev->dynamic_colors[cx] == rgb )
		    return (gx_color_index)(cx+first_color_index);
    }
	if ( fb_dev->next_color_index > max_color_index ) {
  	/* No space left, report failure. */
	  return gx_no_color_index;
  }
  else {
    cx = fb_dev->next_color_index++;
		fb_dev->dynamic_colors[cx-first_color_index] = rgb;
    GrSetColor(cx,cv_bits(r, 8), cv_bits(g, 8), cv_bits(b, 8));
		return (gx_color_index)cx;
	}
}

/* Map a color code to r-g-b. */
/* This routine must invert the transformation of the one above. */
private int
djgpp_map_color_rgb(gx_device *dev, gx_color_index color, gx_color_value prgb[3])
{	
  int r,g,b;
  GrQueryColor(color, &r, &g, &b);
  prgb[0] = r;
  prgb[1] = g;
  prgb[2] = b;
	return 0;
}

/* Copy a monochrome bitmap.  The colors are given explicitly. */
/* Color = gx_no_color_index means transparent (no effect on the image). */
private int
djgpp_copy_mono(gx_device *dev,
  const byte *base, int sourcex, int raster, gx_bitmap_id id,
  int x, int y, int w, int h, gx_color_index czero, gx_color_index cone)
{	int xi, temp_x = x;
	int yi, temp_y = y;
	const byte *srow = base + (sourcex >> 3);
	uint imask = 0x80 >> (sourcex & 7);
	validate_rect();
#define izero (int)czero
#define ione (int)cone
	for ( yi = 0; yi < h; yi++ ) {
		const byte *sptr = srow;
		uint bits = *sptr;
		register uint mask = imask;
		temp_x = x;
		for ( xi = 0; xi < w; xi++ ){
			if ( bits & mask ){
				if ( ione != no_color ) 
					GrPlot(temp_x + xi, temp_y, ione);
			}else{
				if ( izero != no_color )
					GrPlot(temp_x + xi, temp_y, izero);
			}
			if ( !(mask >>= 1) ) mask = 0x80, bits = *++sptr;
		}
		temp_y++;
		srow += raster;
	}
#undef izero
#undef ione
	return 0;
}

/* Copy a color pixelmap.  This is just like a bitmap, */
/* except that each pixel takes 8 bits instead of 1. */
private int
djgpp_copy_color(gx_device *dev,
  const byte *base, int sourcex, int raster, gx_bitmap_id id,
  int x, int y, int w, int h)
{	int xi, yi;
	int skip = raster - w;
#ifndef USE_libgrx
	fb_ptr ptr, ptr_temp;
#endif
  
	validate_rect();
  base += sourcex;
  
#ifdef USE_libgrx
  w += x;
  h += y;
  for ( yi = y ; yi < h ; yi++ ) {
    for ( xi = x ; xi < w ; xi++ ) {
  		GrPlot(xi, yi, *base);
      base++;
    }
    base += skip;
  }
#else
	ptr = GRAM_BASE + dev->width * y + x;
	for ( yi = 0; yi < h; yi++ ){
	    ptr_temp = ptr;
		for ( xi = 0; xi < w; xi++ ){
			*ptr_temp++ =  *base++;
		}
		base += skip;
		ptr += dev->width;
	}
#endif
	return 0;
}

/* Fill a rectangle. */
private int
djgpp_fill_rectangle(gx_device *dev, int x, int y, int w, int h,
  gx_color_index color)
{
#ifdef USE_libgrx
  GrFilledBox(x,y,x+w-1,y+h-1,color);
#else
	int xi, yi;
	byte *ptr;
	if ( x < 0 || x + w > dev->width ) return -1;	/* skip w */
	ptr = GRAM_BASE + dev->width * y + x;
	for ( yi = 0; yi < h; yi++ ){
		for ( xi = 0; xi < w; xi++ ){
			*(ptr+xi) = (byte)color;
		}
		ptr += dev->width;
	}
#endif
	return 0;
}
