/* Copyright (C) 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: gscrd.c,v 1.2.6.1.2.1 2003/01/17 00:49:02 giles Exp $ */
/* CIE color rendering dictionary creation */
#include "math_.h"
#include "memory_.h"
#include "string_.h"
#include "gx.h"
#include "gscdefs.h"		/* for gs_lib_device_list */
#include "gsdevice.h"
#include "gserrors.h"
#include "gsmatrix.h"		/* for gscolor2.h */
#include "gsparam.h"
#include "gsstruct.h"
#include "gsutil.h"
#include "gxcspace.h"
#include "gscolor2.h"		/* for gs_set/currentcolorrendering */
#include "gscrd.h"

/* Import gs_lib_device_list() */
extern_gs_lib_device_list();

/* Allocator structure type */
public_st_cie_render1();
private 
ENUM_PTRS_WITH(cie_render1_enum_ptrs, gs_cie_render *pcrd) return 0;
case 0: return ENUM_OBJ(pcrd->client_data);
case 1: return ENUM_OBJ(pcrd->RenderTable.lookup.table);
case 2: return (pcrd->RenderTable.lookup.table ?
		ENUM_CONST_STRING(&pcrd->TransformPQR.proc_data) :
		0);
ENUM_PTRS_END
private RELOC_PTRS_WITH(cie_render1_reloc_ptrs, gs_cie_render *pcrd);
RELOC_OBJ_VAR(pcrd->client_data);
if (pcrd->RenderTable.lookup.table)
{
RELOC_OBJ_VAR(pcrd->RenderTable.lookup.table);
RELOC_CONST_STRING_VAR(pcrd->TransformPQR.proc_data);
}
RELOC_PTRS_END

/* Default CRD procedures. */

private int
tpqr_identity(int index, floatp in, const gs_cie_wbsd * pwbsd,
	      gs_cie_render * pcrd, float *out)
{
    *out = in;
    return 0;
}

private int
tpqr_from_cache(int index, floatp in, const gs_cie_wbsd * pwbsd,
		gs_cie_render * pcrd, float *out)
{
    /*
     * Since the TransformPQR cache is in the joint caches, not in the
     * CRD cache, we can't actually implement this procedure.
     * Instead, the place that calls it checks for it specially.
     */
    *out = in;
    return 0;
}

private float
render_identity(floatp in, const gs_cie_render * pcrd)
{
    return in;
}
private frac
render_table_identity(byte in, const gs_cie_render * pcrd)
{
    return byte2frac(in);
}

/* Transformation procedures that just consult the cache. */

private float
EncodeABC_cached_A(floatp in, const gs_cie_render * pcrd)
{
    return gs_cie_cached_value(in, &pcrd->caches.EncodeABC[0].floats);
}
private float
EncodeABC_cached_B(floatp in, const gs_cie_render * pcrd)
{
    return gs_cie_cached_value(in, &pcrd->caches.EncodeABC[1].floats);
}
private float
EncodeABC_cached_C(floatp in, const gs_cie_render * pcrd)
{
    return gs_cie_cached_value(in, &pcrd->caches.EncodeABC[2].floats);
}
private float
EncodeLMN_cached_L(floatp in, const gs_cie_render * pcrd)
{
    return gs_cie_cached_value(in, &pcrd->caches.EncodeLMN[0].floats);
}
private float
EncodeLMN_cached_M(floatp in, const gs_cie_render * pcrd)
{
    return gs_cie_cached_value(in, &pcrd->caches.EncodeLMN[1].floats);
}
private float
EncodeLMN_cached_N(floatp in, const gs_cie_render * pcrd)
{
    return gs_cie_cached_value(in, &pcrd->caches.EncodeLMN[2].floats);
}

private frac
RTT_cached(byte in, const gs_cie_render * pcrd, int i)
{
    return pcrd->caches.RenderTableT[i].fracs.values[
	in * (gx_cie_cache_size - 1) / 255
    ];
}
private frac
RTT_cached_0(byte in, const gs_cie_render * pcrd)
{
    return RTT_cached(in, pcrd, 0);
}
private frac
RTT_cached_1(byte in, const gs_cie_render * pcrd)
{
    return RTT_cached(in, pcrd, 1);
}
private frac
RTT_cached_2(byte in, const gs_cie_render * pcrd)
{
    return RTT_cached(in, pcrd, 2);
}
private frac
RTT_cached_3(byte in, const gs_cie_render * pcrd)
{
    return RTT_cached(in, pcrd, 3);
}

/* Define the TransformPQR trampoline procedure that looks up proc_name. */

private int
tpqr_do_lookup(gs_cie_render *pcrd, const gx_device *dev_proto)
{
    gx_device *dev;
    gs_memory_t *mem = pcrd->rc.memory;
    gs_c_param_list list;
    gs_param_string proc_addr;
    int code;

    /* Device prototypes are const, so we must create a copy. */
    code = gs_copydevice(&dev, dev_proto, mem);
    if (code < 0)
	return code;
    gs_c_param_list_write(&list, mem);
    code = param_request((gs_param_list *)&list,
			 pcrd->TransformPQR.proc_name);
    if (code >= 0) {
	code = gs_getdeviceparams(dev, (gs_param_list *)&list);
	if (code >= 0) {
	    gs_c_param_list_read(&list);
	    code = param_read_string((gs_param_list *)&list,
				     pcrd->TransformPQR.proc_name,
				     &proc_addr);
	    if (code == 0 && proc_addr.size == sizeof(gs_cie_transform_proc)) {
		memcpy(&pcrd->TransformPQR.proc, proc_addr.data,
		       sizeof(gs_cie_transform_proc));
	    } else
		code = gs_note_error(gs_error_rangecheck);
	}
    }
    gs_c_param_list_release(&list);
    gs_free_object(mem, dev, "tpqr_do_lookup(device)");
    return code;
}
private int
tpqr_lookup(int index, floatp in, const gs_cie_wbsd * pwbsd,
	    gs_cie_render * pcrd, float *out)
{
    const gx_device *const *dev_list;
    int count = gs_lib_device_list(&dev_list, NULL);
    int i;
    int code;

    for (i = 0; i < count; ++i)
	if (!strcmp(gs_devicename(dev_list[i]),
		    pcrd->TransformPQR.driver_name))
	    break;
    if (i < count)
	code = tpqr_do_lookup(pcrd, dev_list[i]);
    else
	code = gs_note_error(gs_error_undefined);
    if (code < 0)
	return code;
    return pcrd->TransformPQR.proc(index, in, pwbsd, pcrd, out);
}


/* Default vectors. */
const gs_cie_transform_proc3 TransformPQR_default = {
    tpqr_identity,
    0,				/* proc_name */
    {0, 0},			/* proc_data */
    0				/* driver_name */
};
const gs_cie_transform_proc3 TransformPQR_from_cache = {
    tpqr_from_cache,
    0,				/* proc_name */
    {0, 0},			/* proc_data */
    0				/* driver_name */
};
const gs_cie_transform_proc TransformPQR_lookup_proc_name = tpqr_lookup;
const gs_cie_render_proc3 Encode_default = {
    {render_identity, render_identity, render_identity}
};
const gs_cie_render_proc3 EncodeLMN_from_cache = {
    {EncodeLMN_cached_L, EncodeLMN_cached_M, EncodeLMN_cached_N}
};
const gs_cie_render_proc3 EncodeABC_from_cache = {
    {EncodeABC_cached_A, EncodeABC_cached_B, EncodeABC_cached_C}
};
const gs_cie_render_table_procs RenderTableT_default = {
    {render_table_identity, render_table_identity, render_table_identity,
     render_table_identity
    }
};
const gs_cie_render_table_procs RenderTableT_from_cache = {
    {RTT_cached_0, RTT_cached_1, RTT_cached_2, RTT_cached_3}
};

/*
 * Allocate and minimally initialize a CRD.  Note that this procedure sets
 * the reference count of the structure to 1, not 0.  gs_setcolorrendering
 * will increment the reference count again, so unless you want the
 * structure to stay allocated permanently (or until a garbage collection),
 * you should call rc_decrement(pcrd, "client name") *after* calling
 * gs_setcolorrendering.
 */
int
gs_cie_render1_build(gs_cie_render ** ppcrd, gs_memory_t * mem,
		     client_name_t cname)
{
    gs_cie_render *pcrd;

    rc_alloc_struct_1(pcrd, gs_cie_render, &st_cie_render1, mem,
		      return_error(gs_error_VMerror), cname);
    pcrd->id = gs_next_ids(1);
    /* Initialize pointers for the GC. */
    pcrd->client_data = 0;
    pcrd->RenderTable.lookup.table = 0;
    pcrd->status = CIE_RENDER_STATUS_BUILT;
    *ppcrd = pcrd;
    return 0;
}

/*
 * Initialize a CRD given all of the relevant parameters.
 * Any of the pointers except WhitePoint may be zero, meaning
 * use the default values.
 *
 * The actual point, matrix, range, and procedure values are copied into the
 * CRD, but only the pointer to the color lookup table is copied.
 *
 * If pfrom_crd is not NULL, then if the EncodeLMN, EncodeABC, or
 * RenderTable.T procedures indicate that the values exist only in the
 * cache, the corresponding values will be copied from pfrom_crd.
 * Note that NULL values for the individual pointers still represent
 * default values.
 */
int
gs_cie_render1_init_from(gs_cie_render * pcrd, void *client_data,
			 const gs_cie_render * pfrom_crd,
			 const gs_vector3 * WhitePoint,
			 const gs_vector3 * BlackPoint,
			 const gs_matrix3 * MatrixPQR,
			 const gs_range3 * RangePQR,
			 const gs_cie_transform_proc3 * TransformPQR,
			 const gs_matrix3 * MatrixLMN,
			 const gs_cie_render_proc3 * EncodeLMN,
			 const gs_range3 * RangeLMN,
			 const gs_matrix3 * MatrixABC,
			 const gs_cie_render_proc3 * EncodeABC,
			 const gs_range3 * RangeABC,
			 const gs_cie_render_table_t * RenderTable)
{
    pcrd->id = gs_next_ids(1);
    pcrd->client_data = client_data;
    pcrd->points.WhitePoint = *WhitePoint;
    pcrd->points.BlackPoint =
	*(BlackPoint ? BlackPoint : &BlackPoint_default);
    pcrd->MatrixPQR = *(MatrixPQR ? MatrixPQR : &Matrix3_default);
    pcrd->RangePQR = *(RangePQR ? RangePQR : &Range3_default);
    pcrd->TransformPQR =
	*(TransformPQR ? TransformPQR : &TransformPQR_default);
    pcrd->MatrixLMN = *(MatrixLMN ? MatrixLMN : &Matrix3_default);
    pcrd->EncodeLMN = *(EncodeLMN ? EncodeLMN : &Encode_default);
    if (pfrom_crd &&
	!memcmp(&pcrd->EncodeLMN, &EncodeLMN_from_cache,
		sizeof(EncodeLMN_from_cache))
	)
	memcpy(pcrd->caches.EncodeLMN, pfrom_crd->caches.EncodeLMN,
	       sizeof(pcrd->caches.EncodeLMN));
    pcrd->RangeLMN = *(RangeLMN ? RangeLMN : &Range3_default);
    pcrd->MatrixABC = *(MatrixABC ? MatrixABC : &Matrix3_default);
    pcrd->EncodeABC = *(EncodeABC ? EncodeABC : &Encode_default);
    if (pfrom_crd &&
	!memcmp(&pcrd->EncodeABC, &EncodeABC_from_cache,
		sizeof(EncodeABC_from_cache))
	)
	memcpy(pcrd->caches.EncodeABC, pfrom_crd->caches.EncodeABC,
	       sizeof(pcrd->caches.EncodeABC));
    pcrd->RangeABC = *(RangeABC ? RangeABC : &Range3_default);
    if (RenderTable) {
	pcrd->RenderTable = *RenderTable;
	if (pfrom_crd &&
	    !memcmp(&pcrd->RenderTable.T, &RenderTableT_from_cache,
		    sizeof(RenderTableT_from_cache))
	    ) {
	    memcpy(pcrd->caches.RenderTableT, pfrom_crd->caches.RenderTableT,
		   sizeof(pcrd->caches.RenderTableT));
	    pcrd->caches.RenderTableT_is_identity =
		pfrom_crd->caches.RenderTableT_is_identity;
	}
    } else {
	pcrd->RenderTable.lookup.table = 0;
	pcrd->RenderTable.T = RenderTableT_default;
    }
    pcrd->status = CIE_RENDER_STATUS_BUILT;
    return 0;
}
/*
 * Initialize a CRD without the option of copying cached values.
 */
int
gs_cie_render1_initialize(gs_cie_render * pcrd, void *client_data,
			  const gs_vector3 * WhitePoint,
			  const gs_vector3 * BlackPoint,
			  const gs_matrix3 * MatrixPQR,
			  const gs_range3 * RangePQR,
			  const gs_cie_transform_proc3 * TransformPQR,
			  const gs_matrix3 * MatrixLMN,
			  const gs_cie_render_proc3 * EncodeLMN,
			  const gs_range3 * RangeLMN,
			  const gs_matrix3 * MatrixABC,
			  const gs_cie_render_proc3 * EncodeABC,
			  const gs_range3 * RangeABC,
			  const gs_cie_render_table_t * RenderTable)
{
    return gs_cie_render1_init_from(pcrd, client_data, NULL,
				    WhitePoint, BlackPoint,
				    MatrixPQR, RangePQR, TransformPQR,
				    MatrixLMN, EncodeLMN, RangeLMN,
				    MatrixABC, EncodeABC, RangeABC,
				    RenderTable);
}
