/* Copyright (C) 1992, 1995, 1996, 1997, 1998, 1999 Aladdin Enterprises.  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 version 2
  as published by the Free Software Foundation.


  This software is provided AS-IS with no warranty, either express or
  implied. That is, 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.
  
  For more information about licensing, please refer to
  http://www.ghostscript.com/licensing/. For information on
  commercial licensing, go to http://www.artifex.com/licensing/ or
  contact Artifex Software, Inc., 101 Lucas Valley Road #110,
  San Rafael, CA  94903, U.S.A., +1(415)492-9861.
*/

/* $Id: gdevm4.c,v 1.4 2002/02/21 22:24:51 giles Exp $ */
/* 4-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 */

/* ================ Standard (byte-oriented) device ================ */

#undef chunk
#define chunk byte
#define fpat(byt) mono_fill_make_pattern(byt)

/* Procedures */
declare_mem_procs(mem_mapped4_copy_mono, mem_mapped4_copy_color, mem_mapped4_fill_rectangle);

/* The device descriptor. */
const gx_device_memory mem_mapped4_device =
mem_device("image4", 4, 0,
	   mem_mapped_map_rgb_color, mem_mapped_map_color_rgb,
  mem_mapped4_copy_mono, mem_mapped4_copy_color, mem_mapped4_fill_rectangle,
	   mem_gray_strip_copy_rop);

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

/* Define the 4-bit fill patterns. */
static const mono_fill_chunk tile_patterns[16] =
{fpat(0x00), fpat(0x11), fpat(0x22), fpat(0x33),
 fpat(0x44), fpat(0x55), fpat(0x66), fpat(0x77),
 fpat(0x88), fpat(0x99), fpat(0xaa), fpat(0xbb),
 fpat(0xcc), fpat(0xdd), fpat(0xee), fpat(0xff)
};


/* Fill a rectangle with a color. */
private int
mem_mapped4_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;

    fit_fill(dev, x, y, w, h);
    bits_fill_rectangle(scan_line_base(mdev, y), x << 2, mdev->raster,
			tile_patterns[color], w << 2, h);
    return 0;
}

/* Copy a bitmap. */
private int
mem_mapped4_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;
    const byte *line;
    declare_scan_ptr(dest);
    byte invert, bb;

    fit_copy(dev, base, sourcex, sraster, id, x, y, w, h);
    setup_rect(dest);
    line = base + (sourcex >> 3);
    /* Divide into opaque and masked cases. */
    if (one == gx_no_color_index) {
	if (zero == gx_no_color_index)
	    return 0;		/* nothing to do */
	invert = 0xff;
	bb = ((byte) zero << 4) | (byte) zero;
    } else if (zero == gx_no_color_index) {
	invert = 0;
	bb = ((byte) one << 4) | (byte) one;
    } else {
	/* Opaque case. */
	int shift = ~(sourcex ^ x) & 1;
	byte oz[4];

	oz[0] = (byte)((zero << 4) | zero);
	oz[1] = (byte)((zero << 4) | one);
	oz[2] = (byte)((one << 4) | zero);
	oz[3] = (byte)((one << 4) | one);
	do {
	    register byte *dptr = (byte *) dest;
	    const byte *sptr = line;
	    register uint sbyte = *sptr++;
	    register int sbit = ~sourcex & 7;
	    int count = w;

	    /*
	     * If the first source bit corresponds to an odd X in the
	     * destination, process it now.
	     */
	    if (x & 1) {
		*dptr = (*dptr & 0xf0) |
		    ((sbyte >> sbit) & 1 ? one : zero);
		--count;	/* may now be 0 */
		if (--sbit < 0)
		    sbit = 7, sbyte = *sptr++;
		++dptr;
	    }
	    /*
	     * Now we know the next destination X is even.  We want to
	     * process 2 source bits at a time from now on, so set things up
	     * properly depending on whether the next source X (bit) is even
	     * or odd.  In both even and odd cases, the active source bits
	     * are in bits 8..1 of sbyte.
	     */
	    sbyte <<= shift;
	    sbit += shift - 1;
	    /*
	     * Now bit # sbit+1 is the most significant unprocessed bit
	     * in sbyte.  -1 <= sbit <= 7; sbit is odd.
	     * Note that if sbit = -1, all of sbyte has been processed.
	     *
	     * Continue processing pairs of bits in the first source byte.
	     */
	    while (count >= 2 && sbit >= 0) {
		*dptr++ = oz[(sbyte >> sbit) & 3];
		sbit -= 2, count -= 2;
	    }
	    /*
	     * Now sbit = -1 iff we have processed the entire first source
	     * byte.
	     *
	     * Process full source bytes.
	     */
	    if (shift) {
		sbyte >>= 1;	/* in case count < 8 */
		for (; count >= 8; dptr += 4, count -= 8) {
		    sbyte = *sptr++;
		    dptr[0] = oz[sbyte >> 6];
		    dptr[1] = oz[(sbyte >> 4) & 3];
		    dptr[2] = oz[(sbyte >> 2) & 3];
		    dptr[3] = oz[sbyte & 3];
		}
		sbyte <<= 1;
	    } else {
		for (; count >= 8; dptr += 4, count -= 8) {
		    sbyte = (sbyte << 8) | *sptr++;
		    dptr[0] = oz[(sbyte >> 7) & 3];
		    dptr[1] = oz[(sbyte >> 5) & 3];
		    dptr[2] = oz[(sbyte >> 3) & 3];
		    dptr[3] = oz[(sbyte >> 1) & 3];
		}
	    }
	    if (!count)
		continue;
	    /*
	     * Process pairs of bits in the final source byte.  Note that
	     * if sbit > 0, this is still the first source byte (the
	     * full-byte loop wasn't executed).
	     */
	    if (sbit < 0) {
		sbyte = (sbyte << 8) | (*sptr << shift);
		sbit = 7;
	    }
	    while (count >= 2) {
		*dptr++ = oz[(sbyte >> sbit) & 3];
		sbit -= 2, count -= 2;
	    }
	    /*
	     * If the final source bit corresponds to an even X value,
	     * process it now.
	     */
	    if (count) {
		*dptr = (*dptr & 0x0f) |
		    (((sbyte >> sbit) & 2 ? one : zero) << 4);
	    }
	} while ((line += sraster, inc_ptr(dest, draster), --h) > 0);
	return 0;
    }
    /* Masked case. */
    do {
	register byte *dptr = (byte *) dest;
	const byte *sptr = line;
	register int sbyte = *sptr++ ^ invert;
	register int sbit = 0x80 >> (sourcex & 7);
	register byte mask = (x & 1 ? 0x0f : 0xf0);
	int count = w;

	do {
	    if (sbyte & sbit)
		*dptr = (*dptr & ~mask) | (bb & mask);
	    if ((sbit >>= 1) == 0)
		sbit = 0x80, sbyte = *sptr++ ^ invert;
	    dptr += (mask = ~mask) >> 7;
	} while (--count > 0);
	line += sraster;
	inc_ptr(dest, draster);
    } while (--h > 0);
    return 0;
}

/* Copy a color bitmap. */
private int
mem_mapped4_copy_color(gx_device * dev,
	       const byte * base, int sourcex, int sraster, gx_bitmap_id id,
		       int x, int y, int w, int h)
{
    /* Use monobit copy_mono. */
    int code;

    /* Patch the width in the device temporarily. */
    dev->width <<= 2;
    code = (*dev_proc(&mem_mono_device, copy_mono))
	(dev, base, sourcex << 2, sraster, id,
	 x << 2, y, w << 2, h, (gx_color_index) 0, (gx_color_index) 1);
    /* Restore the correct width. */
    dev->width >>= 2;
    return code;
}

/* ================ "Word"-oriented device ================ */

/* Note that on a big-endian machine, this is the same as the */
/* standard byte-oriented-device. */

#if !arch_is_big_endian

/* Procedures */
declare_mem_procs(mem4_word_copy_mono, mem4_word_copy_color, mem4_word_fill_rectangle);

/* Here is the device descriptor. */
const gx_device_memory mem_mapped4_word_device =
mem_full_device("image4w", 4, 0, mem_open,
		mem_mapped_map_rgb_color, mem_mapped_map_color_rgb,
	mem4_word_copy_mono, mem4_word_copy_color, mem4_word_fill_rectangle,
		gx_default_map_cmyk_color, gx_default_strip_tile_rectangle,
		gx_no_strip_copy_rop, mem_word_get_bits_rectangle);

/* Fill a rectangle with a color. */
private int
mem4_word_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;
    byte *base;
    uint raster;

    fit_fill(dev, x, y, w, h);
    base = scan_line_base(mdev, y);
    raster = mdev->raster;
    mem_swap_byte_rect(base, raster, x << 2, w << 2, h, true);
    bits_fill_rectangle(base, x << 2, raster,
			tile_patterns[color], w << 2, h);
    mem_swap_byte_rect(base, raster, x << 2, w << 2, h, true);
    return 0;
}

/* Copy a bitmap. */
private int
mem4_word_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;
    byte *row;
    uint raster;
    bool store;

    fit_copy(dev, base, sourcex, sraster, id, x, y, w, h);
    row = scan_line_base(mdev, y);
    raster = mdev->raster;
    store = (zero != gx_no_color_index && one != gx_no_color_index);
    mem_swap_byte_rect(row, raster, x << 2, w << 2, h, store);
    mem_mapped4_copy_mono(dev, base, sourcex, sraster, id,
			  x, y, w, h, zero, one);
    mem_swap_byte_rect(row, raster, x << 2, w << 2, h, false);
    return 0;
}

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

    fit_copy(dev, base, sourcex, sraster, id, x, y, w, h);
    /* Use monobit copy_mono. */
    /* Patch the width in the device temporarily. */
    dev->width <<= 2;
    code = (*dev_proc(&mem_mono_word_device, copy_mono))
	(dev, base, sourcex << 2, sraster, id,
	 x << 2, y, w << 2, h, (gx_color_index) 0, (gx_color_index) 1);
    /* Restore the correct width. */
    dev->width >>= 2;
    return code;
}

#endif /* !arch_is_big_endian */
