/* Copyright (C) 1997, 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: gxsample.c,v 1.2.6.1.2.1 2003/01/17 00:49:04 giles Exp $ */
/* Sample unpacking procedures */
#include "gx.h"
#include "gxsample.h"

/* ---------------- Lookup tables ---------------- */

/*
 * Define standard tables for spreading 1-bit input data.
 * Note that these depend on the end-orientation of the CPU.
 * We can't simply define them as byte arrays, because
 * they might not wind up properly long- or short-aligned.
 */
#define map4tox(z,a,b,c,d)\
    z, z^a, z^b, z^(a+b),\
    z^c, z^(a+c), z^(b+c), z^(a+b+c),\
    z^d, z^(a+d), z^(b+d), z^(a+b+d),\
    z^(c+d), z^(a+c+d), z^(b+c+d), z^(a+b+c+d)
/* Work around warnings from really picky compilers. */
#ifdef __STDC__
#  define n0L 0xffffffffU
#  define ffL8 0x0000ff00U
#  define ffL16 0x00ff0000U
#  define ffL24 0xff000000U
#else
#if arch_sizeof_long == 4
/*
 * The compiler evaluates long expressions mod 2^32.  Even very picky
 * compilers allow assigning signed longs to unsigned longs, so we use
 * signed constants.
 */
#  define n0L (-1)
#  define ffL8 0x0000ff00
#  define ffL16 0x00ff0000
#  define ffL24 (-0x01000000)
#else
/*
 * The compiler evaluates long expressions mod 2^64.
 */
#  define n0L 0xffffffffL
#  define ffL8 0x0000ff00L
#  define ffL16 0x00ff0000L
#  define ffL24 0xff000000L
#endif
#endif
#if arch_is_big_endian
const bits32 lookup4x1to32_identity[16] = {
    map4tox(0, 0xff, ffL8, ffL16, ffL24)
};
const bits32 lookup4x1to32_inverted[16] = {
    map4tox(n0L, 0xff, ffL8, ffL16, ffL24)
};
#else /* !arch_is_big_endian */
const bits32 lookup4x1to32_identity[16] = {
    map4tox(0, ffL24, ffL16, ffL8, 0xff)
};
const bits32 lookup4x1to32_inverted[16] = {
    map4tox(n0L, ffL24, ffL16, ffL8, 0xff)
};
#endif
#undef n0L
#undef ffL8
#undef ffL16
#undef ffL24

/* ---------------- Unpacking procedures ---------------- */

const byte *
sample_unpack_copy(byte * bptr, int *pdata_x, const byte * data, int data_x,
		uint dsize, const sample_lookup_t * ignore_ptab, int spread)
{				/* We're going to use the data right away, so no copying is needed. */
    *pdata_x = data_x;
    return data;
}

const byte *
sample_unpack_1(byte * bptr, int *pdata_x, const byte * data, int data_x,
		uint dsize, const sample_lookup_t * ptab, int spread)
{
    const byte *psrc = data + (data_x >> 3);
    int left = dsize - (data_x >> 3);

    if (spread == 1) {
	bits32 *bufp = (bits32 *) bptr;
	const bits32 *map = &ptab->lookup4x1to32[0];
	uint b;

	if (left & 1) {
	    b = psrc[0];
	    bufp[0] = map[b >> 4];
	    bufp[1] = map[b & 0xf];
	    psrc++, bufp += 2;
	}
	left >>= 1;
	while (left--) {
	    b = psrc[0];
	    bufp[0] = map[b >> 4];
	    bufp[1] = map[b & 0xf];
	    b = psrc[1];
	    bufp[2] = map[b >> 4];
	    bufp[3] = map[b & 0xf];
	    psrc += 2, bufp += 4;
	}
    } else {
	byte *bufp = bptr;
	const byte *map = &ptab->lookup8[0];

	while (left--) {
	    uint b = *psrc++;

	    *bufp = map[b >> 7];
	    bufp += spread;
	    *bufp = map[(b >> 6) & 1];
	    bufp += spread;
	    *bufp = map[(b >> 5) & 1];
	    bufp += spread;
	    *bufp = map[(b >> 4) & 1];
	    bufp += spread;
	    *bufp = map[(b >> 3) & 1];
	    bufp += spread;
	    *bufp = map[(b >> 2) & 1];
	    bufp += spread;
	    *bufp = map[(b >> 1) & 1];
	    bufp += spread;
	    *bufp = map[b & 1];
	    bufp += spread;
	}
    }
    *pdata_x = data_x & 7;
    return bptr;
}

const byte *
sample_unpack_2(byte * bptr, int *pdata_x, const byte * data, int data_x,
		uint dsize, const sample_lookup_t * ptab, int spread)
{
    const byte *psrc = data + (data_x >> 2);
    int left = dsize - (data_x >> 2);

    if (spread == 1) {
	bits16 *bufp = (bits16 *) bptr;
	const bits16 *map = &ptab->lookup2x2to16[0];

	while (left--) {
	    uint b = *psrc++;

	    *bufp++ = map[b >> 4];
	    *bufp++ = map[b & 0xf];
	}
    } else {
	byte *bufp = bptr;
	const byte *map = &ptab->lookup8[0];

	while (left--) {
	    unsigned b = *psrc++;

	    *bufp = map[b >> 6];
	    bufp += spread;
	    *bufp = map[(b >> 4) & 3];
	    bufp += spread;
	    *bufp = map[(b >> 2) & 3];
	    bufp += spread;
	    *bufp = map[b & 3];
	    bufp += spread;
	}
    }
    *pdata_x = data_x & 3;
    return bptr;
}

const byte *
sample_unpack_4(byte * bptr, int *pdata_x, const byte * data, int data_x,
		uint dsize, const sample_lookup_t * ptab, int spread)
{
    byte *bufp = bptr;
    const byte *psrc = data + (data_x >> 1);
    int left = dsize - (data_x >> 1);
    const byte *map = &ptab->lookup8[0];

    while (left--) {
	uint b = *psrc++;

	*bufp = map[b >> 4];
	bufp += spread;
	*bufp = map[b & 0xf];
	bufp += spread;
    }
    *pdata_x = data_x & 1;
    return bptr;
}

const byte *
sample_unpack_8(byte * bptr, int *pdata_x, const byte * data, int data_x,
		uint dsize, const sample_lookup_t * ptab, int spread)
{
    byte *bufp = bptr;
    const byte *psrc = data + data_x;

    *pdata_x = 0;
    if (spread == 1) {
	if (ptab->lookup8[0] != 0 ||
	    ptab->lookup8[255] != 255
	    ) {
	    uint left = dsize - data_x;
	    const byte *map = &ptab->lookup8[0];

	    while (left--)
		*bufp++ = map[*psrc++];
	} else {		/* No copying needed, and we'll use the data right away. */
	    return psrc;
	}
    } else {
	int left = dsize - data_x;
	const byte *map = &ptab->lookup8[0];

	for (; left--; psrc++, bufp += spread)
	    *bufp = map[*psrc];
    }
    return bptr;
}
