/* Copyright (C) 1994, 1996, 1998, 1999 artofcode LLC.  All rights reserved.
  
  This program is free software; you can redistribute it and/or modify it
  under the terms of the GNU General Public License as published by the
  Free Software Foundation; either version 2 of the License, or (at your
  option) any later version.

  This program is distributed in the hope that it will be useful, but
  WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  General Public License for more details.

  You should have received a copy of the GNU General Public License along
  with this program; if not, write to the Free Software Foundation, Inc.,
  59 Temple Place, Suite 330, Boston, MA, 02111-1307.

*/

/*$Id: gdevm16.c,v 1.2.6.1.2.1 2003/01/17 00:49:00 giles Exp $ */
/* 16-bit-per-pixel "memory" (stored bitmap) device */
#include "memory_.h"
#include "gx.h"
#include "gxdevice.h"
#include "gxdevmem.h"		/* semi-public definitions */
#include "gdevmem.h"		/* private definitions */

#undef chunk
#define chunk byte

/* The 16 bits are divided 5 for red, 6 for green, and 5 for blue. */
/* Note that the bits must always be kept in big-endian order. */

/* Procedures */
declare_mem_map_procs(mem_true16_map_rgb_color, mem_true16_map_color_rgb);
declare_mem_procs(mem_true16_copy_mono, mem_true16_copy_color, mem_true16_fill_rectangle);

/* The device descriptor. */
const gx_device_memory mem_true16_device =
    mem_device("image16", 16, 0,
	       mem_true16_map_rgb_color, mem_true16_map_color_rgb,
	       mem_true16_copy_mono, mem_true16_copy_color,
	       mem_true16_fill_rectangle, mem_default_strip_copy_rop);

/* Map a r-g-b color to a color index. */
private gx_color_index
mem_true16_map_rgb_color(gx_device * dev, gx_color_value r, gx_color_value g,
			 gx_color_value b)
{
    return ((r >> (gx_color_value_bits - 5)) << 11) +
	((g >> (gx_color_value_bits - 6)) << 5) +
	(b >> (gx_color_value_bits - 5));
}

/* Map a color index to a r-g-b color. */
private int
mem_true16_map_color_rgb(gx_device * dev, gx_color_index color,
			 gx_color_value prgb[3])
{
    ushort value = color >> 11;

    prgb[0] = ((value << 11) + (value << 6) + (value << 1) + (value >> 4))
		      >> (16 - gx_color_value_bits);
    value = (color >> 5) & 0x3f;
    prgb[1] = ((value << 10) + (value << 4) + (value >> 2))
		      >> (16 - gx_color_value_bits);
    value = color & 0x1f;
    prgb[2] = ((value << 11) + (value << 6) + (value << 1) + (value >> 4))
		      >> (16 - gx_color_value_bits);
    return 0;
}

/* Convert x coordinate to byte offset in scan line. */
#undef x_to_byte
#define x_to_byte(x) ((x) << 1)

/* Fill a rectangle with a color. */
private int
mem_true16_fill_rectangle(gx_device * dev,
			  int x, int y, int w, int h, gx_color_index color)
{
    gx_device_memory * const mdev = (gx_device_memory *)dev;
#if arch_is_big_endian
    const ushort color16 = (ushort)color;
#else
    const ushort color16 = (ushort)((color << 8) | (color >> 8));
#endif
    declare_scan_ptr(dest);

    fit_fill(dev, x, y, w, h);
    setup_rect(dest);
    if (w == 1) {
	while (h-- > 0) {
	    *(ushort *)dest = color16;
	    inc_ptr(dest, draster);
	}
    } else if ((color16 >> 8) == (color16 & 0xff)) {
	bytes_fill_rectangle(scan_line_base(mdev, y) + (x << 1), draster,
			     (byte)color, w << 1, h);
    } else {
	while (h-- > 0) {
	    ushort *pptr = (ushort *) dest;
	    int cnt = w;

	    for (; cnt >= 4; pptr += 4, cnt -= 4)
		pptr[3] = pptr[2] = pptr[1] = pptr[0] = color16;
	    switch (cnt) {
	    case 3: pptr[2] = color16;
	    case 2: pptr[1] = color16;
	    case 1: pptr[0] = color16;
	    case 0: DO_NOTHING;
	    }
	    inc_ptr(dest, draster);
	}
    }
    return 0;
}

/* Copy a monochrome bitmap. */
private int
mem_true16_copy_mono(gx_device * dev,
		     const byte * base, int sourcex, int sraster,
		     gx_bitmap_id id, int x, int y, int w, int h,
		     gx_color_index zero, gx_color_index one)
{
    gx_device_memory * const mdev = (gx_device_memory *)dev;
#if arch_is_big_endian
    const ushort zero16 = (ushort)zero;
    const ushort one16 = (ushort)one;
#else
    ushort zero16 = ((uint) (byte) zero << 8) + ((ushort) zero >> 8);
    ushort one16 = ((uint) (byte) one << 8) + ((ushort) one >> 8);
#endif
    const byte *line;
    int first_bit;

    declare_scan_ptr(dest);
    fit_copy(dev, base, sourcex, sraster, id, x, y, w, h);
    setup_rect(dest);
    line = base + (sourcex >> 3);
    first_bit = 0x80 >> (sourcex & 7);
    while (h-- > 0) {
	register ushort *pptr = (ushort *) dest;
	const byte *sptr = line;
	register int sbyte = *sptr++;
	register int bit = first_bit;
	int count = w;

	do {
	    if (sbyte & bit) {
		if (one != gx_no_color_index)
		    *pptr = one16;
	    } else {
		if (zero != gx_no_color_index)
		    *pptr = zero16;
	    }
	    if ((bit >>= 1) == 0)
		bit = 0x80, sbyte = *sptr++;
	    pptr++;
	}
	while (--count > 0);
	line += sraster;
	inc_ptr(dest, draster);
    }
    return 0;
}

/* Copy a color bitmap. */
private int
mem_true16_copy_color(gx_device * dev,
	       const byte * base, int sourcex, int sraster, gx_bitmap_id id,
		      int x, int y, int w, int h)
{
    gx_device_memory * const mdev = (gx_device_memory *)dev;

    fit_copy(dev, base, sourcex, sraster, id, x, y, w, h);
    mem_copy_byte_rect(mdev, base, sourcex, sraster, x, y, w, h);
    return 0;
}
