/* Copyright (C) 1994, 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: gxi12bit.c,v 1.4 2002/02/21 22:24:53 giles Exp $ */
/* 12-bit image procedures */
#include "gx.h"
#include "memory_.h"
#include "gpcheck.h"
#include "gserrors.h"
#include "gxfixed.h"
#include "gxfrac.h"
#include "gxarith.h"
#include "gxmatrix.h"
#include "gsccolor.h"
#include "gspaint.h"
#include "gxdevice.h"
#include "gxcmap.h"
#include "gxdcolor.h"
#include "gxistate.h"
#include "gxdevmem.h"
#include "gxcpath.h"
#include "gximage.h"

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

private const byte *
sample_unpack_12(byte * bptr, int *pdata_x, const byte * data,
		 int data_x, uint dsize, const sample_lookup_t * ignore_ptab,
		 int spread)
{
    register frac *bufp = (frac *) bptr;
    uint dskip = (data_x >> 1) * 3;
    const byte *psrc = data + dskip;
#define inc_bufp(bp, n) bp = (frac *)((byte *)(bp) + (n))
    uint sample;
    int left = dsize - dskip;

    if ((data_x & 1) && left > 0)
	switch (left) {
	    default:
		sample = ((uint) (psrc[1] & 0xf) << 8) + psrc[2];
		*bufp = bits2frac(sample, 12);
		inc_bufp(bufp, spread);
		psrc += 3;
		left -= 3;
		break;
	    case 2:		/* xxxxxxxx xxxxdddd */
		*bufp = (psrc[1] & 0xf) * (frac_1 / 15);
	    case 1:		/* xxxxxxxx */
		left = 0;
	}
    while (left >= 3) {
	sample = ((uint) * psrc << 4) + (psrc[1] >> 4);
	*bufp = bits2frac(sample, 12);
	inc_bufp(bufp, spread);
	sample = ((uint) (psrc[1] & 0xf) << 8) + psrc[2];
	*bufp = bits2frac(sample, 12);
	inc_bufp(bufp, spread);
	psrc += 3;
	left -= 3;
    }
    /* Handle trailing bytes. */
    switch (left) {
	case 2:		/* dddddddd ddddxxxx */
	    sample = ((uint) * psrc << 4) + (psrc[1] >> 4);
	    *bufp = bits2frac(sample, 12);
	    inc_bufp(bufp, spread);
	    *bufp = (psrc[1] & 0xf) * (frac_1 / 15);
	    break;
	case 1:		/* dddddddd */
	    sample = (uint) * psrc << 4;
	    *bufp = bits2frac(sample, 12);
	    break;
	case 0:		/* Nothing more to do. */
	    ;
    }
    *pdata_x = 0;
    return bptr;
}

const sample_unpack_proc_t sample_unpack_12_proc = sample_unpack_12;

/* ------ Strategy procedure ------ */

/* Check the prototype. */
iclass_proc(gs_image_class_2_fracs);

/* Use special (slow) logic for 12-bit source values. */
private irender_proc(image_render_frac);
irender_proc_t
gs_image_class_2_fracs(gx_image_enum * penum)
{
    if (penum->bps > 8) {
	if (penum->use_mask_color) {
	    /* Convert color mask values to fracs. */
	    int i;

	    for (i = 0; i < penum->spp * 2; ++i)
		penum->mask_color.values[i] =
		    bits2frac(penum->mask_color.values[i], 12);
	}
	if_debug0('b', "[b]render=frac\n");
	return image_render_frac;
    }
    return 0;
}

/* ---------------- Rendering procedures ---------------- */

/* ------ Rendering for 12-bit samples ------ */

#define FRACS_PER_LONG (arch_sizeof_long / arch_sizeof_frac)
typedef union {
    frac v[GS_IMAGE_MAX_COLOR_COMPONENTS];
#define LONGS_PER_COLOR_FRACS\
  ((GS_IMAGE_MAX_COLOR_COMPONENTS + FRACS_PER_LONG - 1) / FRACS_PER_LONG)
    long all[LONGS_PER_COLOR_FRACS];	/* for fast comparison */
} color_fracs;

#define LONGS_PER_4_FRACS ((FRACS_PER_LONG + 3) / 4)
#if LONGS_PER_4_FRACS == 1
#  define COLOR_FRACS_4_EQ(f1, f2)\
     ((f1).all[0] == (f2).all[0])
#else
#if LONGS_PER_4_FRACS == 2
#  define COLOR_FRACS_4_EQ(f1, f2)\
     ((f1).all[0] == (f2).all[0] && (f1).all[1] == (f2).all[1])
#endif
#endif

/* Test whether a color is transparent. */
private bool
mask_color12_matches(const frac *v, const gx_image_enum *penum,
		   int num_components)
{
    int i;

    for (i = num_components * 2, v += num_components - 1; (i -= 2) >= 0; --v)
	if (*v < penum->mask_color.values[i] ||
	    *v > penum->mask_color.values[i + 1]
	    )
	    return false;
    return true;
}

/* Render an image with more than 8 bits per sample. */
/* The samples have been expanded into fracs. */
private int
image_render_frac(gx_image_enum * penum, const byte * buffer, int data_x,
		  uint w, int h, gx_device * dev)
{
    const gs_imager_state *pis = penum->pis;
    gs_logical_operation_t lop = penum->log_op;
    gx_dda_fixed_point pnext;
    image_posture posture = penum->posture;
    fixed xl, ytf;
    fixed pdyx, pdyy;		/* edge of parallelogram */
    int yt = penum->yci, iht = penum->hci;
    const gs_color_space *pcs = penum->pcs;
    cs_proc_remap_color((*remap_color)) = pcs->type->remap_color;
    gs_client_color cc;
    bool device_color = penum->device_color;
    const gx_color_map_procs *cmap_procs = gx_get_cmap_procs(pis, dev);
    cmap_proc_rgb((*map_rgb)) = cmap_procs->map_rgb;
    cmap_proc_cmyk((*map_cmyk)) = cmap_procs->map_cmyk;
    bool use_mask_color = penum->use_mask_color;
    gx_device_color devc1, devc2;
    gx_device_color *pdevc = &devc1;
    gx_device_color *pdevc_next = &devc2;
    int spp = penum->spp;
    const frac *psrc_initial = (const frac *)buffer + data_x * spp;
    const frac *psrc = psrc_initial;
    const frac *rsrc = psrc + spp; /* psrc + spp at start of run */
    fixed xrun;			/* x at start of run */
    int irun;			/* int xrun */
    fixed yrun;			/* y ditto */
    color_fracs run;		/* run value */
    color_fracs next;		/* next sample value */
    const frac *bufend = psrc + w;
    int code = 0, mcode = 0;

    if (h == 0)
	return 0;
    pnext = penum->dda.pixel0;
    xrun = xl = dda_current(pnext.x);
    irun = fixed2int_var_rounded(xrun);
    yrun = ytf = dda_current(pnext.y);
    pdyx = dda_current(penum->dda.row.x) - penum->cur.x;
    pdyy = dda_current(penum->dda.row.y) - penum->cur.y;
    if_debug5('b', "[b]y=%d data_x=%d w=%d xt=%f yt=%f\n",
	      penum->y, data_x, w, fixed2float(xl), fixed2float(ytf));
    memset(&run, 0, sizeof(run));
    memset(&next, 0, sizeof(next));
    /* Ensure that we don't get any false dev_color_eq hits. */
    color_set_pure(&devc1, gx_no_color_index);
    cs_full_init_color(&cc, pcs);
    run.v[0] = ~psrc[0];	/* force remap */

    while (psrc < bufend) {
	next.v[0] = psrc[0];
	switch (spp) {
	    case 4:		/* may be CMYK */
		next.v[1] = psrc[1];
		next.v[2] = psrc[2];
		next.v[3] = psrc[3];
		psrc += 4;
		if (COLOR_FRACS_4_EQ(next, run))
		    goto inc;
		if (use_mask_color && mask_color12_matches(next.v, penum, 4)) {
		    color_set_null(pdevc_next);
		    goto f;
		}
		if (device_color) {
		    (*map_cmyk) (next.v[0], next.v[1],
				 next.v[2], next.v[3],
				 pdevc_next, pis, dev,
				 gs_color_select_source);
		    goto f;
		}
		decode_frac(next.v[0], cc, 0);
		decode_frac(next.v[1], cc, 1);
		decode_frac(next.v[2], cc, 2);
		decode_frac(next.v[3], cc, 3);
		if_debug4('B', "[B]cc[0..3]=%g,%g,%g,%g\n",
			  cc.paint.values[0], cc.paint.values[1],
			  cc.paint.values[2], cc.paint.values[3]);
		if_debug1('B', "[B]cc[3]=%g\n",
			  cc.paint.values[3]);
		break;
	    case 3:		/* may be RGB */
		next.v[1] = psrc[1];
		next.v[2] = psrc[2];
		psrc += 3;
		if (COLOR_FRACS_4_EQ(next, run))
		    goto inc;
		if (use_mask_color && mask_color12_matches(next.v, penum, 3)) {
		    color_set_null(pdevc_next);
		    goto f;
		}
		if (device_color) {
		    (*map_rgb) (next.v[0], next.v[1],
				next.v[2], pdevc_next, pis, dev,
				gs_color_select_source);
		    goto f;
		}
		decode_frac(next.v[0], cc, 0);
		decode_frac(next.v[1], cc, 1);
		decode_frac(next.v[2], cc, 2);
		if_debug3('B', "[B]cc[0..2]=%g,%g,%g\n",
			  cc.paint.values[0], cc.paint.values[1],
			  cc.paint.values[2]);
		break;
	    case 1:		/* may be Gray */
		psrc++;
		if (next.v[0] == run.v[0])
		    goto inc;
		if (use_mask_color && mask_color12_matches(next.v, penum, 1)) {
		    color_set_null(pdevc_next);
		    goto f;
		}
		if (device_color) {
		    (*map_rgb) (next.v[0], next.v[0],
				next.v[0], pdevc_next, pis, dev,
				gs_color_select_source);
		    goto f;
		}
		decode_frac(next.v[0], cc, 0);
		if_debug1('B', "[B]cc[0]=%g\n",
			  cc.paint.values[0]);
		break;
	    default:		/* DeviceN */
		{
		    int i;

		    for (i = 1; i < spp; ++i)
			next.v[i] = psrc[i];
		    psrc += spp;
		    if (!memcmp(next.v, run.v, spp * sizeof(next.v[0])))
			goto inc;
		    if (use_mask_color &&
			mask_color12_matches(next.v, penum, spp)
			) {
			color_set_null(pdevc_next);
			goto f;
		    }
		    for (i = 0; i < spp; ++i)
			decode_frac(next.v[i], cc, i);
#ifdef DEBUG
		    if (gs_debug_c('B')) {
			dprintf2("[B]cc[0..%d]=%g", spp - 1,
				 cc.paint.values[0]);
			for (i = 1; i < spp; ++i)
			    dprintf1(",%g", cc.paint.values[i]);
			dputs("\n");
		    }
#endif
		}
		break;
	}
	mcode = remap_color(&cc, pcs, pdevc_next, pis, dev,
			   gs_color_select_source);
	if (mcode < 0)
	    goto fill;
f:
	if_debug7('B', "[B]0x%x,0x%x,0x%x,0x%x -> %ld,%ld,0x%lx\n",
		  next.v[0], next.v[1], next.v[2], next.v[3],
		  pdevc_next->colors.binary.color[0],
		  pdevc_next->colors.binary.color[1],
		  (ulong) pdevc_next->type);
	/* Even though the supplied colors don't match, */
	/* the device colors might. */
	if (!dev_color_eq(devc1, devc2)) {
	    /* Fill the region between xrun/irun and xl */
	    gx_device_color *ptemp;

fill:
	    if (posture != image_portrait) {	/* Parallelogram */
		code = (*dev_proc(dev, fill_parallelogram))
		    (dev, xrun, yrun,
		     xl - xrun, ytf - yrun, pdyx, pdyy,
		     pdevc, lop);
	    } else {		/* Rectangle */
		int xi = irun;
		int wi = (irun = fixed2int_var_rounded(xl)) - xi;

		if (wi < 0)
		    xi += wi, wi = -wi;
		code = gx_fill_rectangle_device_rop(xi, yt,
						  wi, iht, pdevc, dev, lop);
	    }
	    if (code < 0)
		goto err;
	    rsrc = psrc;
	    if ((code = mcode) < 0)
		goto err;
	    ptemp = pdevc;
	    pdevc = pdevc_next;
	    pdevc_next = ptemp;
	    xrun = xl;
	    yrun = ytf;
	}
	run = next;
inc:
	xl = dda_next(pnext.x);
	ytf = dda_next(pnext.y);
    }
    /* Fill the final run. */
    code = (*dev_proc(dev, fill_parallelogram))
	(dev, xrun, yrun, xl - xrun, ytf - yrun, pdyx, pdyy, pdevc, lop);
    return (code < 0 ? code : 1);

    /* Save position if error, in case we resume. */
err:
    penum->used.x = (rsrc - spp - psrc_initial) / spp;
    penum->used.y = 0;
    return code;
}
