/* Copyright (C) 1999, 2000 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: gsistate.c,v 1.4.6.1.2.1 2003/01/17 00:49:02 giles Exp $ */
/* Imager state housekeeping */
#include "gx.h"
#include "gserrors.h"
#include "gscspace.h"
#include "gscie.h"
#include "gsstruct.h"
#include "gsutil.h"		/* for gs_next_ids */
#include "gxbitmap.h"
#include "gxcmap.h"
#include "gxdht.h"
#include "gxistate.h"
#include "gzht.h"
#include "gzline.h"

/******************************************************************************
 * See gsstate.c for a discussion of graphics/imager state memory management. *
 ******************************************************************************/

/* Imported values */
/* The following should include a 'const', but for some reason */
/* the Watcom compiler won't accept it, even though it happily accepts */
/* the same construct everywhere else. */
extern /*const*/ gx_color_map_procs *const cmap_procs_default;

/* Structure descriptors */
private_st_imager_state_shared();

/* GC procedures for gx_line_params */
private
ENUM_PTRS_WITH(line_params_enum_ptrs, gx_line_params *plp) return 0;
    case 0: return ENUM_OBJ((plp->dash.pattern_size == 0 ?
			     NULL : plp->dash.pattern));
ENUM_PTRS_END
private RELOC_PTRS_WITH(line_params_reloc_ptrs, gx_line_params *plp)
{
    if (plp->dash.pattern_size)
	RELOC_VAR(plp->dash.pattern);
} RELOC_PTRS_END
private_st_line_params();

/* GC procedures for gs_imager_state */
public_st_imager_state();
private 
ENUM_PTRS_BEGIN(imager_state_enum_ptrs)
    ENUM_SUPER(gs_imager_state, st_line_params, line_params, st_imager_state_num_ptrs - st_line_params_num_ptrs);
    ENUM_PTR(0, gs_imager_state, shared);
    ENUM_PTR(1, gs_imager_state, client_data);
    ENUM_PTR(2, gs_imager_state, opacity.mask);
    ENUM_PTR(3, gs_imager_state, shape.mask);
    ENUM_PTR(4, gs_imager_state, transparency_stack);
#define E1(i,elt) ENUM_PTR(i+5,gs_imager_state,elt);
    gs_cr_state_do_ptrs(E1)
#undef E1
ENUM_PTRS_END
private RELOC_PTRS_BEGIN(imager_state_reloc_ptrs)
{
    RELOC_SUPER(gs_imager_state, st_line_params, line_params);
    RELOC_PTR(gs_imager_state, shared);
    RELOC_PTR(gs_imager_state, client_data);
    RELOC_PTR(gs_imager_state, opacity.mask);
    RELOC_PTR(gs_imager_state, shape.mask);
    RELOC_PTR(gs_imager_state, transparency_stack);
#define R1(i,elt) RELOC_PTR(gs_imager_state,elt);
    gs_cr_state_do_ptrs(R1)
#undef R1
} RELOC_PTRS_END

/* Free device color spaces. */
void
gx_device_color_spaces_free(gx_device_color_spaces_t *pdcs, gs_memory_t *mem,
			    client_name_t cname)
{
    int i;

    for (i = countof(pdcs->indexed); --i >= 0; ) {
	gs_color_space *pcs = pdcs->indexed[i];

	if (pcs) {
	    gs_cspace_release(pcs);
	    gs_free_object(mem, pcs, cname);
	}
    }
}

/* Initialize an imager state, other than the parts covered by */
/* gs_imager_state_initial. */
private float
imager_null_transfer(floatp gray, const gx_transfer_map * pmap)
{
    return gray;
}
private void
rc_free_imager_shared(gs_memory_t * mem, void *data, client_name_t cname)
{
    gs_imager_state_shared_t * const shared =
	(gs_imager_state_shared_t *)data;

    gx_device_color_spaces_free(&shared->device_color_spaces, mem,
				"shared device color space");
    rc_free_struct_only(mem, data, cname);
}

int
gs_imager_state_initialize(gs_imager_state * pis, gs_memory_t * mem)
{
    pis->memory = mem;
    pis->client_data = 0;
    /* Preallocate color spaces. */
    {
	int code;
	gs_imager_state_shared_t *shared;

	rc_alloc_struct_1(shared, gs_imager_state_shared_t,
			  &st_imager_state_shared, mem,
			  return_error(gs_error_VMerror),
			  "gs_imager_state_init(shared)");
	shared->device_color_spaces.named.Gray =
	    shared->device_color_spaces.named.RGB =
	    shared->device_color_spaces.named.CMYK = 0; /* in case we bail out */
	shared->rc.free = rc_free_imager_shared;
	if ((code = gs_cspace_build_DeviceGray(&shared->device_color_spaces.named.Gray, mem)) < 0 ||
	    (code = gs_cspace_build_DeviceRGB(&shared->device_color_spaces.named.RGB, mem)) < 0 ||
	    (code = gs_cspace_build_DeviceCMYK(&shared->device_color_spaces.named.CMYK, mem)) < 0
	    ) {
	    rc_free_imager_shared(mem, shared, "gs_imager_state_init(shared)");
	    return code;
	}
	pis->shared = shared;
    }
    pis->opacity.mask = 0;
    pis->shape.mask = 0;
    pis->transparency_stack = 0;
    /* Color rendering state */
    pis->halftone = 0;
    {
	int i;

	for (i = 0; i < gs_color_select_count; ++i)
	    pis->screen_phase[i].x = pis->screen_phase[i].y = 0;
    }
    pis->dev_ht = 0;
    pis->ht_cache = 0;
    pis->cie_render = 0;
    pis->black_generation = 0;
    pis->undercolor_removal = 0;
    /* Allocate an initial transfer map. */
    rc_alloc_struct_n(pis->set_transfer.colored.gray,
		      gx_transfer_map, &st_transfer_map,
		      mem, return_error(gs_error_VMerror),
		      "gs_imager_state_init(transfer)", 4);
    pis->set_transfer.colored.gray->proc = imager_null_transfer;
    pis->set_transfer.colored.gray->id = gs_next_ids(1);
    pis->set_transfer.colored.gray->values[0] = frac_0;
    pis->set_transfer.colored.red =
	pis->set_transfer.colored.green =
	pis->set_transfer.colored.blue =
	pis->set_transfer.colored.gray;
    pis->effective_transfer = pis->set_transfer;
    pis->cie_joint_caches = 0;
    pis->cmap_procs = cmap_procs_default;
    pis->pattern_cache = 0;
    return 0;
}

/*
 * Make a temporary copy of a gs_imager_state.  Note that this does not
 * do all the necessary reference counting, etc.  However, it does
 * clear out the transparency stack in the destination.
 */
gs_imager_state *
gs_imager_state_copy(const gs_imager_state * pis, gs_memory_t * mem)
{
    gs_imager_state *pis_copy =
	gs_alloc_struct(mem, gs_imager_state, &st_imager_state,
			"gs_imager_state_copy");

    if (pis_copy) {
	*pis_copy = *pis;
	pis_copy->transparency_stack = 0;
    }
    return pis_copy;
}

/* Increment reference counts to note that an imager state has been copied. */
void
gs_imager_state_copied(gs_imager_state * pis)
{
    rc_increment(pis->shared);
    rc_increment(pis->opacity.mask);
    rc_increment(pis->shape.mask);
    rc_increment(pis->halftone);
    rc_increment(pis->dev_ht);
    rc_increment(pis->cie_render);
    rc_increment(pis->black_generation);
    rc_increment(pis->undercolor_removal);
    rc_increment(pis->set_transfer.colored.gray);
    rc_increment(pis->set_transfer.colored.red);
    rc_increment(pis->set_transfer.colored.green);
    rc_increment(pis->set_transfer.colored.blue);
    rc_increment(pis->cie_joint_caches);
}

/* Adjust reference counts before assigning one imager state to another. */
void
gs_imager_state_pre_assign(gs_imager_state *pto, const gs_imager_state *pfrom)
{
    const char *const cname = "gs_imager_state_pre_assign";

#define RCCOPY(element)\
    rc_pre_assign(pto->element, pfrom->element, cname)

    RCCOPY(cie_joint_caches);
    RCCOPY(set_transfer.colored.blue);
    RCCOPY(set_transfer.colored.green);
    RCCOPY(set_transfer.colored.red);
    RCCOPY(set_transfer.colored.gray);
    RCCOPY(undercolor_removal);
    RCCOPY(black_generation);
    RCCOPY(cie_render);
    RCCOPY(dev_ht);
    RCCOPY(halftone);
    RCCOPY(shape.mask);
    RCCOPY(opacity.mask);
    RCCOPY(shared);
#undef RCCOPY
}

/* Release an imager state. */
void
gs_imager_state_release(gs_imager_state * pis)
{
    const char *const cname = "gs_imager_state_release";
    gx_device_halftone *pdht = pis->dev_ht;

#define RCDECR(element)\
    rc_decrement(pis->element, cname)

    RCDECR(cie_joint_caches);
    RCDECR(set_transfer.colored.gray);
    RCDECR(set_transfer.colored.blue);
    RCDECR(set_transfer.colored.green);
    RCDECR(set_transfer.colored.red);
    RCDECR(undercolor_removal);
    RCDECR(black_generation);
    RCDECR(cie_render);
    /*
     * If we're going to free the device halftone, make sure we free the
     * dependent structures as well.
     */
    if (pdht != 0 && pdht->rc.ref_count == 1) {
	/* Make sure we don't leave dangling pointers in the cache. */
	gx_ht_cache *pcache = pis->ht_cache;

	if (pcache->order.bit_data == pdht->order.bit_data ||
	    pcache->order.levels == pdht->order.levels
	    )
	    gx_ht_clear_cache(pcache);
	gx_device_halftone_release(pdht, pdht->rc.memory);
    }
    RCDECR(dev_ht);
    RCDECR(halftone);
    RCDECR(shape.mask);
    RCDECR(opacity.mask);
    RCDECR(shared);
#undef RCDECR
}
