/* Copyright (C) 1994, 1995, 1996, 1997, 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: gsbitops.c,v 1.3.4.1.2.1 2003/01/17 00:49:02 giles Exp $ */
/* Bitmap filling, copying, and transforming operations */
#include "stdio_.h"
#include "memory_.h"
#include "gdebug.h"
#include "gserror.h"
#include "gserrors.h"
#include "gstypes.h"
#include "gsbittab.h"
#include "gxbitops.h"

/* ---------------- Bit-oriented operations ---------------- */

/* Define masks for little-endian operation. */
/* masks[i] has the first i bits off and the rest on. */
#if !arch_is_big_endian
const bits16 mono_copy_masks[17] = {
    0xffff, 0xff7f, 0xff3f, 0xff1f,
    0xff0f, 0xff07, 0xff03, 0xff01,
    0xff00, 0x7f00, 0x3f00, 0x1f00,
    0x0f00, 0x0700, 0x0300, 0x0100,
    0x0000
};
const bits32 mono_fill_masks[33] = {
#define mask(n)\
  ((~0xff | (0xff >> (n & 7))) << (n & -8))
    mask( 0),mask( 1),mask( 2),mask( 3),mask( 4),mask( 5),mask( 6),mask( 7),
    mask( 8),mask( 9),mask(10),mask(11),mask(12),mask(13),mask(14),mask(15),
    mask(16),mask(17),mask(18),mask(19),mask(20),mask(21),mask(22),mask(23),
    mask(24),mask(25),mask(26),mask(27),mask(28),mask(29),mask(30),mask(31),
    0
#undef mask
};
#endif

/* Fill a rectangle of bits with an 8x1 pattern. */
/* The pattern argument must consist of the pattern in every byte, */
/* e.g., if the desired pattern is 0xaa, the pattern argument must */
/* have the value 0xaaaa (if ints are short) or 0xaaaaaaaa. */
#undef chunk
#define chunk mono_fill_chunk
#undef mono_masks
#define mono_masks mono_fill_masks
void
bits_fill_rectangle(byte * dest, int dest_bit, uint draster,
		    mono_fill_chunk pattern, int width_bits, int height)
{
    uint bit;
    chunk right_mask;
    int line_count = height;
    chunk *ptr;
    int last_bit;

#define FOR_EACH_LINE(stat)\
	do { stat } while ( inc_ptr(ptr, draster), --line_count )

    dest += (dest_bit >> 3) & -chunk_align_bytes;
    ptr = (chunk *) dest;
    bit = dest_bit & chunk_align_bit_mask;
    last_bit = width_bits + bit - (chunk_bits + 1);

    if (last_bit < 0) {		/* <=1 chunk */
	set_mono_thin_mask(right_mask, width_bits, bit);
	switch ((byte) pattern) {
	    case 0:
		FOR_EACH_LINE(*ptr &= ~right_mask;
		    );
		break;
	    case 0xff:
		FOR_EACH_LINE(*ptr |= right_mask;
		    );
		break;
	    default:
		FOR_EACH_LINE(
		       *ptr = (*ptr & ~right_mask) | (pattern & right_mask);
		    );
	}
    } else {
	chunk mask;
	int last = last_bit >> chunk_log2_bits;

	set_mono_left_mask(mask, bit);
	set_mono_right_mask(right_mask, (last_bit & chunk_bit_mask) + 1);
	switch (last) {
	    case 0:		/* 2 chunks */
		switch ((byte) pattern) {
		    case 0:
			FOR_EACH_LINE(*ptr &= ~mask;
				      ptr[1] &= ~right_mask;
			    );
			break;
		    case 0xff:
			FOR_EACH_LINE(*ptr |= mask;
				      ptr[1] |= right_mask;
			    );
			break;
		    default:
			FOR_EACH_LINE(
				   *ptr = (*ptr & ~mask) | (pattern & mask);
					 ptr[1] = (ptr[1] & ~right_mask) | (pattern & right_mask);
			    );
		}
		break;
	    case 1:		/* 3 chunks */
		switch ((byte) pattern) {
		    case 0:
			FOR_EACH_LINE(
					 *ptr &= ~mask;
					 ptr[1] = 0;
					 ptr[2] &= ~right_mask;
			    );
			break;
		    case 0xff:
			FOR_EACH_LINE(
					 *ptr |= mask;
					 ptr[1] = ~(chunk) 0;
					 ptr[2] |= right_mask;
			    );
			break;
		    default:
			FOR_EACH_LINE(
				   *ptr = (*ptr & ~mask) | (pattern & mask);
					 ptr[1] = pattern;
					 ptr[2] = (ptr[2] & ~right_mask) | (pattern & right_mask);
			    );
		}
		break;
	    default:{		/* >3 chunks */
		    uint byte_count = (last_bit >> 3) & -chunk_bytes;

		    switch ((byte) pattern) {
			case 0:
			    FOR_EACH_LINE(
					     *ptr &= ~mask;
					     memset(ptr + 1, 0, byte_count);
					     ptr[last + 1] &= ~right_mask;
				);
			    break;
			case 0xff:
			    FOR_EACH_LINE(
					     *ptr |= mask;
					  memset(ptr + 1, 0xff, byte_count);
					     ptr[last + 1] |= right_mask;
				);
			    break;
			default:
			    FOR_EACH_LINE(
				   *ptr = (*ptr & ~mask) | (pattern & mask);
				memset(ptr + 1, (byte) pattern, byte_count);
					     ptr[last + 1] =
					     (ptr[last + 1] & ~right_mask) |
					     (pattern & right_mask);
				);
		    }
		}
	}
    }
#undef FOR_EACH_LINE
}

/* Replicate a bitmap horizontally in place. */
void
bits_replicate_horizontally(byte * data, uint width, uint height,
		 uint raster, uint replicated_width, uint replicated_raster)
{
    /* The current algorithm is extremely inefficient! */
    const byte *orig_row = data + (height - 1) * raster;
    byte *tile_row = data + (height - 1) * replicated_raster;
    uint y;

    if (!(width & 7)) {
	uint src_bytes = width >> 3;
	uint dest_bytes = replicated_width >> 3;

	for (y = height; y-- > 0;
	     orig_row -= raster, tile_row -= replicated_raster
	     ) {
	    uint move = src_bytes;
	    const byte *from = orig_row;
	    byte *to = tile_row + dest_bytes - src_bytes;

	    memmove(to, from, move);
	    while (to - tile_row >= move) {
		from = to;
		to -= move;
		memmove(to, from, move);
		move <<= 1;
	    }
	    if (to != tile_row)
		memmove(tile_row, to, to - tile_row);
	}
    } else {
	/*
	 * This algorithm is inefficient, but probably not worth improving.
	 */
	uint bit_count = width & -width;  /* lowest bit: 1, 2, or 4 */
	uint left_mask = (0xff00 >> bit_count) & 0xff;

	for (y = height; y-- > 0;
	     orig_row -= raster, tile_row -= replicated_raster
	     ) {
	    uint sx;

	    for (sx = width; sx > 0;) {
		uint bits, dx;

		sx -= bit_count;
		bits = (orig_row[sx >> 3] << (sx & 7)) & left_mask;
		for (dx = sx + replicated_width; dx >= width;) {
		    byte *dp;
		    int dbit;

		    dx -= width;
		    dbit = dx & 7;
		    dp = tile_row + (dx >> 3);
		    *dp = (*dp & ~(left_mask >> dbit)) | (bits >> dbit);
		}
	    }
	}
    }
}

/* Replicate a bitmap vertically in place. */
void
bits_replicate_vertically(byte * data, uint height, uint raster,
			  uint replicated_height)
{
    byte *dest = data;
    uint h = replicated_height;
    uint size = raster * height;

    while (h > height) {
	memcpy(dest + size, dest, size);
	dest += size;
	h -= height;
    }
}

/* Find the bounding box of a bitmap. */
/* Assume bits beyond the width are zero. */
void
bits_bounding_box(const byte * data, uint height, uint raster,
		  gs_int_rect * pbox)
{
    register const ulong *lp;
    static const byte first_1[16] = {
	4, 3, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0
    };
    static const byte last_1[16] = {
	0, 4, 3, 4, 2, 4, 3, 4, 1, 4, 3, 4, 2, 4, 3, 4
    };

    /* Count trailing blank rows. */
    /* Since the raster is a multiple of sizeof(long), */
    /* we don't need to scan by bytes, only by longs. */

    lp = (const ulong *)(data + raster * height);
    while ((const byte *)lp > data && !lp[-1])
	--lp;
    if ((const byte *)lp == data) {
	pbox->p.x = pbox->q.x = pbox->p.y = pbox->q.y = 0;
	return;
    }
    pbox->q.y = height = ((const byte *)lp - data + raster - 1) / raster;

    /* Count leading blank rows. */

    lp = (const ulong *)data;
    while (!*lp)
	++lp;
    {
	uint n = ((const byte *)lp - data) / raster;

	pbox->p.y = n;
	if (n)
	    height -= n, data += n * raster;
    }

    /* Find the left and right edges. */
    /* We know that the first and last rows are non-blank. */

    {
	uint raster_longs = raster >> arch_log2_sizeof_long;
	uint left = raster_longs - 1, right = 0;
	ulong llong = 0, rlong = 0;
	const byte *q;
	uint h, n;

	for (q = data, h = height; h-- > 0; q += raster) {	/* Work from the left edge by longs. */
	    for (lp = (const ulong *)q, n = 0;
		 n < left && !*lp; lp++, n++
		);
	    if (n < left)
		left = n, llong = *lp;
	    else
		llong |= *lp;
	    /* Work from the right edge by longs. */
	    for (lp = (const ulong *)(q + raster - sizeof(long)),
		 n = raster_longs - 1;

		 n > right && !*lp; lp--, n--
		);
	    if (n > right)
		right = n, rlong = *lp;
	    else
		rlong |= *lp;
	}

	/* Do binary subdivision on edge longs.  We assume that */
	/* sizeof(long) = 4 or 8. */
#if arch_sizeof_long > 8
	Error_longs_are_too_large();
#endif

#if arch_is_big_endian
#  define last_bits(n) ((1L << (n)) - 1)
#  define shift_out_last(x,n) ((x) >>= (n))
#  define right_justify_last(x,n) DO_NOTHING
#else
#  define last_bits(n) (-1L << ((arch_sizeof_long * 8) - (n)))
#  define shift_out_last(x,n) ((x) <<= (n))
#  define right_justify_last(x,n) (x) >>= ((arch_sizeof_long * 8) - (n))
#endif

	left <<= arch_log2_sizeof_long + 3;
#if arch_sizeof_long == 8
	if (llong & ~last_bits(32))
	    shift_out_last(llong, 32);
	else
	    left += 32;
#endif
	if (llong & ~last_bits(16))
	    shift_out_last(llong, 16);
	else
	    left += 16;
	if (llong & ~last_bits(8))
	    shift_out_last(llong, 8);
	else
	    left += 8;
	right_justify_last(llong, 8);
	if (llong & 0xf0)
	    left += first_1[(byte) llong >> 4];
	else
	    left += first_1[(byte) llong] + 4;

	right <<= arch_log2_sizeof_long + 3;
#if arch_sizeof_long == 8
	if (!(rlong & last_bits(32)))
	    shift_out_last(rlong, 32);
	else
	    right += 32;
#endif
	if (!(rlong & last_bits(16)))
	    shift_out_last(rlong, 16);
	else
	    right += 16;
	if (!(rlong & last_bits(8)))
	    shift_out_last(rlong, 8);
	else
	    right += 8;
	right_justify_last(rlong, 8);
	if (!(rlong & 0xf))
	    right += last_1[(byte) rlong >> 4];
	else
	    right += last_1[(uint) rlong & 0xf] + 4;

	pbox->p.x = left;
	pbox->q.x = right;
    }
}

/* Extract a plane from a pixmap. */
int
bits_extract_plane(const bits_plane_t *dest /*write*/,
    const bits_plane_t *source /*read*/, int shift, int width, int height)
{
    int source_depth = source->depth;
    int source_bit = source->x * source_depth;
    const byte *source_row = source->data.read + (source_bit >> 3);
    int dest_depth = dest->depth;
    uint plane_mask = (1 << dest_depth) - 1;
    int dest_bit = dest->x * dest_depth;
    byte *dest_row = dest->data.write + (dest_bit >> 3);
    enum {
	EXTRACT_SLOW = 0,
	EXTRACT_4_TO_1,
	EXTRACT_32_TO_8
    } loop_case = EXTRACT_SLOW;
    int y;

    source_bit &= 7;
    dest_bit &= 7;
    /* Check for the fast CMYK cases. */
    if (!(source_bit | dest_bit)) {
	switch (source_depth) {
	case 4:
	    loop_case =
		(dest_depth == 1 && !(source->raster & 3) &&
		 !(source->x & 1) ? EXTRACT_4_TO_1 :
		 EXTRACT_SLOW);
	    break;
	case 32:
	    if (dest_depth == 8 && !(shift & 7)) {
		loop_case = EXTRACT_32_TO_8;
		source_row += 3 - (shift >> 3);
	    }
	    break;
	}
    }
    for (y = 0; y < height;
	 ++y, source_row += source->raster, dest_row += dest->raster
	) {
	int x;

	switch (loop_case) {
	case EXTRACT_4_TO_1: {
	    const byte *source = source_row;
	    byte *dest = dest_row;

	    /* Do groups of 8 pixels. */
	    for (x = width; x >= 8; source += 4, x -= 8) {
		bits32 sword =
		    (*(const bits32 *)source >> shift) & 0x11111111;

		*dest++ =
		    byte_acegbdfh_to_abcdefgh[(
#if arch_is_big_endian
		    (sword >> 21) | (sword >> 14) | (sword >> 7) | sword
#else
		    (sword << 3) | (sword >> 6) | (sword >> 15) | (sword >> 24)
#endif
					) & 0xff];
	    }
	    if (x) {
		/* Do the final 1-7 pixels. */
		uint test = 0x10 << shift, store = 0x80;

		do {
		    *dest = (*source & test ? *dest | store : *dest & ~store);
		    if (test >= 0x10)
			test >>= 4;
		    else
			test <<= 4, ++source;
		    store >>= 1;
		} while (--x > 0);
	    }
	    break;
	}
	case EXTRACT_32_TO_8: {
	    const byte *source = source_row;
	    byte *dest = dest_row;

	    for (x = width; x > 0; source += 4, --x)
		*dest++ = *source;
	    break;
	}
	default: {
	    sample_load_declare_setup(sptr, sbit, source_row, source_bit,
				      source_depth);
	    sample_store_declare_setup(dptr, dbit, dbbyte, dest_row, dest_bit,
				       dest_depth);

	    sample_store_preload(dbbyte, dptr, dbit, dest_depth);
	    for (x = width; x > 0; --x) {
		bits32 color;
		uint pixel;

		sample_load_next32(color, sptr, sbit, source_depth);
		pixel = (color >> shift) & plane_mask;
		sample_store_next8(pixel, dptr, dbit, dest_depth, dbbyte);
	    }
	    sample_store_flush(dptr, dbit, dest_depth, dbbyte);
	}
	}
    }
    return 0;
}

/* Expand a plane into a pixmap. */
int
bits_expand_plane(const bits_plane_t *dest /*write*/,
    const bits_plane_t *source /*read*/, int shift, int width, int height)
{
    /*
     * Eventually we will optimize this just like bits_extract_plane.
     */
    int source_depth = source->depth;
    int source_bit = source->x * source_depth;
    const byte *source_row = source->data.read + (source_bit >> 3);
    int dest_depth = dest->depth;
    int dest_bit = dest->x * dest_depth;
    byte *dest_row = dest->data.write + (dest_bit >> 3);
    enum {
	EXPAND_SLOW = 0,
	EXPAND_1_TO_4,
	EXPAND_8_TO_32
    } loop_case = EXPAND_SLOW;
    int y;

    source_bit &= 7;
    /* Check for the fast CMYK cases. */
    if (!(source_bit || (dest_bit & 31) || (dest->raster & 3))) {
	switch (dest_depth) {
	case 4:
	    if (source_depth == 1)
		loop_case = EXPAND_1_TO_4;
	    break;
	case 32:
	    if (source_depth == 8 && !(shift & 7))
		loop_case = EXPAND_8_TO_32;
	    break;
	}
    }
    dest_bit &= 7;
    switch (loop_case) {

    case EXPAND_8_TO_32: {
#if arch_is_big_endian
#  define word_shift (shift)
#else
	int word_shift = 24 - shift;
#endif
	for (y = 0; y < height;
	     ++y, source_row += source->raster, dest_row += dest->raster
	     ) {
	    int x;
	    const byte *source = source_row;
	    bits32 *dest = (bits32 *)dest_row;

	    for (x = width; x > 0; --x)
		*dest++ = (bits32)(*source++) << word_shift;
	}
#undef word_shift
    }
	break;

    case EXPAND_1_TO_4:
    default:
	for (y = 0; y < height;
	     ++y, source_row += source->raster, dest_row += dest->raster
	     ) {
	    int x;
	    sample_load_declare_setup(sptr, sbit, source_row, source_bit,
				      source_depth);
	    sample_store_declare_setup(dptr, dbit, dbbyte, dest_row, dest_bit,
				       dest_depth);

	    sample_store_preload(dbbyte, dptr, dbit, dest_depth);
	    for (x = width; x > 0; --x) {
		uint color;
		bits32 pixel;

		sample_load_next8(color, sptr, sbit, source_depth);
		pixel = color << shift;
		sample_store_next32(pixel, dptr, dbit, dest_depth, dbbyte);
	    }
	    sample_store_flush(dptr, dbit, dest_depth, dbbyte);
	}
	break;

    }
    return 0;
}

/* ---------------- Byte-oriented operations ---------------- */

/* Fill a rectangle of bytes. */
void
bytes_fill_rectangle(byte * dest, uint raster,
		     byte value, int width_bytes, int height)
{
    while (height-- > 0) {
	memset(dest, value, width_bytes);
	dest += raster;
    }
}

/* Copy a rectangle of bytes. */
void
bytes_copy_rectangle(byte * dest, uint dest_raster,
	     const byte * src, uint src_raster, int width_bytes, int height)
{
    while (height-- > 0) {
	memcpy(dest, src, width_bytes);
	src += src_raster;
	dest += dest_raster;
    }
}
