/* Copyright (C) 1991, 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: gxclist.h,v 1.2.6.1.2.1 2003/01/17 00:49:03 giles Exp $ */
/* Command list definitions for Ghostscript. */
/* Requires gxdevice.h and gxdevmem.h */

#ifndef gxclist_INCLUDED
#  define gxclist_INCLUDED

#include "gscspace.h"
#include "gxband.h"
#include "gxbcache.h"
#include "gxclio.h"
#include "gxdevbuf.h"
#include "gxistate.h"
#include "gxrplane.h"

/*
 * A command list is essentially a compressed list of driver calls.
 * Command lists are used to record an image that must be rendered in bands
 * for high-resolution and/or limited-memory printers.
 *
 * Command lists work in two phases.  The first phase records driver calls,
 * sorting them according to the band(s) they affect.  The second phase
 * reads back the commands band-by-band to create the bitmap images.
 * When opened, a command list is in the recording state; it switches
 * automatically from recording to reading when its get_bits procedure
 * is called.  Currently, there is a hack to reopen the device after
 * each page is processed, in order to switch back to recording.
 */

/*
 * The command list contains both commands for particular bands (the vast
 * majority) and commands that apply to a range of bands.  In order to
 * synchronize the two, we maintain the following invariant for buffered
 * commands:
 *
 *      If there are any band-range commands in the buffer, they are the
 *      first commands in the buffer, before any specific-band commands.
 *
 * To maintain this invariant, whenever we are about to put an band-range
 * command in the buffer, we check to see if the buffer already has any
 * band-range commands in it, and if so, whether they are the last commands
 * in the buffer and are for the same range; if the answer to any of these
 * questions is negative, we flush the buffer.
 */

/* ---------------- Public structures ---------------- */

/*
 * Define a saved page object.  This consists of a snapshot of the device
 * structure, information about the page per se, and the num_copies
 * parameter of output_page.
 */
typedef struct gx_saved_page_s {
    gx_device device;
    char dname[8 + 1];		/* device name for checking */
    gx_band_page_info_t info;
    int num_copies;
} gx_saved_page;

/*
 * Define a saved page placed at a particular (X,Y) offset for rendering.
 */
typedef struct gx_placed_page_s {
    gx_saved_page *page;
    gs_int_point offset;
} gx_placed_page;
  
/*
 * Define a procedure to cause some bandlist memory to be freed up,
 * probably by rendering current bandlist contents.
 */
#define proc_free_up_bandlist_memory(proc)\
  int proc(P2(gx_device *dev, bool flush_current))

/* ---------------- Internal structures ---------------- */

/*
 * Currently, halftoning occurs during the first phase, producing calls
 * to tile_rectangle.  Both phases keep a cache of recently seen bitmaps
 * (halftone cells and characters), which allows writing only a short cache
 * index in the command list rather than the entire bitmap.
 *
 * We keep only a single cache for all bands, but since the second phase
 * reads the command lists for each band separately, we have to keep track
 * for each cache entry E and band B whether the definition of E has been
 * written into B's list.  We do this with a bit mask in each entry.
 *
 * Eventually, we will replace this entire arrangement with one in which
 * we pass the actual halftone screen (whitening order) to all bands
 * through the command list, and halftoning occurs on the second phase.
 * This not only will shrink the command list, but will allow us to apply
 * other rendering algorithms such as error diffusion in the second phase.
 */
typedef struct {
    ulong offset;		/* writing: offset from cdev->data, */
    /*   0 means unused */
    /* reading: offset from cdev->chunk.data */
} tile_hash;
typedef struct {
    gx_cached_bits_common;
    /* To save space, instead of storing rep_width and rep_height, */
    /* we store width / rep_width and height / rep_height. */
    byte x_reps, y_reps;
    ushort rep_shift;
    ushort index;		/* index in table (hash table when writing) */
    ushort num_bands;		/* # of 1-bits in the band mask */
    /* byte band_mask[]; */
#define ts_mask(pts) (byte *)((pts) + 1)
    /* byte bits[]; */
#define ts_bits(cldev,pts) (ts_mask(pts) + (cldev)->tile_band_mask_size)
} tile_slot;

/* Define the prefix on each command run in the writing buffer. */
typedef struct cmd_prefix_s cmd_prefix;
struct cmd_prefix_s {
    cmd_prefix *next;
    uint size;
};

/* Define the pointers for managing a list of command runs in the buffer. */
/* There is one of these for each band, plus one for band-range commands. */
typedef struct cmd_list_s {
    cmd_prefix *head, *tail;	/* list of commands for band */
} cmd_list;

/*
 * In order to keep the per-band state down to a reasonable size,
 * we store only a single set of the imager state parameters;
 * for each parameter, each band has a flag that says whether that band
 * 'knows' the current value of the parameters.
 */
extern const gs_imager_state clist_imager_state_initial;

/*
 * Define the main structure for holding command list state.
 * Unless otherwise noted, all elements are used in both the writing (first)
 * and reading (second) phase.
 */
typedef struct gx_clist_state_s gx_clist_state;

#define gx_device_clist_common_members\
	gx_device_forward_common;	/* (see gxdevice.h) */\
		/* Following must be set before writing or reading. */\
		/* See gx_device_clist_writer, below, for more that must be init'd */\
	/* gx_device *target; */	/* device for which commands */\
					/* are being buffered */\
	gx_device_buf_procs_t buf_procs;\
	gs_memory_t *bandlist_memory;	/* allocator for in-memory bandlist files */\
	byte *data;			/* buffer area */\
	uint data_size;			/* size of buffer */\
	gx_band_params_t band_params;	/* band buffering parameters */\
	bool do_not_open_or_close_bandfiles;	/* if true, do not open/close bandfiles */\
		/* Following are used for both writing and reading. */\
	gx_bits_cache_chunk chunk;	/* the only chunk of bits */\
	gx_bits_cache bits;\
	uint tile_hash_mask;		/* size of tile hash table -1 */\
	uint tile_band_mask_size;	/* size of band mask preceding */\
					/* each tile in the cache */\
	tile_hash *tile_table;		/* table for tile cache: */\
					/* see tile_hash above */\
					/* (a hash table when writing) */\
	int ymin, ymax;			/* current band, <0 when writing */\
		/* Following are set when writing, read when reading. */\
	gx_band_page_info_t page_info;	/* page information */\
	int nbands		/* # of bands */

typedef struct gx_device_clist_common_s {
    gx_device_clist_common_members;
} gx_device_clist_common;

#define clist_band_height(cldev) ((cldev)->page_info.band_height)
#define clist_cfname(cldev) ((cldev)->page_info.cfname)
#define clist_cfile(cldev) ((cldev)->page_info.cfile)
#define clist_bfname(cldev) ((cldev)->page_info.bfname)
#define clist_bfile(cldev) ((cldev)->page_info.bfile)

/* Define the length of the longest dash pattern we are willing to store. */
/* (Strokes with longer patterns are converted to fills.) */
#define cmd_max_dash 11

/* Define the state of a band list when writing. */
typedef struct clist_color_space_s {
    byte byte1;			/* see cmd_opv_set_color_space in gxclpath.h */
    gs_id id;			/* space->id for comparisons */
    const gs_color_space *space;
} clist_color_space_t;
typedef struct gx_device_clist_writer_s {
    gx_device_clist_common_members;	/* (must be first) */
    int error_code;		/* error returned by cmd_put_op */
    gx_clist_state *states;	/* current state of each band */
    byte *cbuf;			/* start of command buffer */
    byte *cnext;		/* next slot in command buffer */
    byte *cend;			/* end of command buffer */
    cmd_list *ccl;		/* &clist_state.list of last command */
    cmd_list band_range_list;	/* list of band-range commands */
    int band_range_min, band_range_max;		/* range for list */
    uint tile_max_size;		/* max size of a single tile (bytes) */
    uint tile_max_count;	/* max # of hash table entries */
    gx_strip_bitmap tile_params;	/* current tile parameters */
    int tile_depth;		/* current tile depth */
    int tile_known_min, tile_known_max;  /* range of bands that knows the */
				/* current tile parameters */
    /*
     * NOTE: we must not set the line_params.dash.pattern member of the
     * imager state to point to the dash_pattern member of the writer
     * state (except transiently), since this would confuse the
     * garbage collector.
     */
    gs_imager_state imager_state;	/* current values of imager params */
    float dash_pattern[cmd_max_dash];	/* current dash pattern */
    const gx_clip_path *clip_path;	/* current clip path, */
				/* only non-transient for images */
    gs_id clip_path_id;		/* id of current clip path */
    clist_color_space_t color_space;	/* current color space, */
				/* only used for non-mask images */
    gs_id transfer_ids[4];	/* ids of transfer maps */
    gs_id black_generation_id;	/* id of black generation map */
    gs_id undercolor_removal_id;	/* id of u.c.r. map */
    gs_id device_halftone_id;	/* id of device halftone */
    gs_id image_enum_id;	/* non-0 if we are inside an image */
				/* that we are passing through */
	int error_is_retryable;		/* Extra status used to distinguish hard VMerrors */
	                           /* from warnings upgraded to VMerrors. */
	                           /* T if err ret'd by cmd_put_op et al can be retried */
	int permanent_error;		/* if < 0, error only cleared by clist_reset() */
	int driver_call_nesting;	/* nesting level of non-retryable driver calls */
	int ignore_lo_mem_warnings;	/* ignore warnings from clist file/mem */
		/* Following must be set before writing */
	proc_free_up_bandlist_memory((*free_up_bandlist_memory)); /* if nz, proc to free some bandlist memory */
	int disable_mask;		/* mask of routines to disable clist_disable_xxx */
} gx_device_clist_writer;

/* Bits for gx_device_clist_writer.disable_mask. Bit set disables behavior */
#define clist_disable_fill_path	(1 << 0)
#define clist_disable_stroke_path (1 << 1)
#define clist_disable_hl_image (1 << 2)
#define clist_disable_complex_clip (1 << 3)
#define clist_disable_nonrect_hl_image (1 << 4)
#define clist_disable_pass_thru_params (1 << 5)	/* disable EXCEPT at top of page */

/* Define the state of a band list when reading. */
/* For normal rasterizing, pages and num_pages are both 0. */
typedef struct gx_device_clist_reader_s {
    gx_device_clist_common_members;	/* (must be first) */
    gx_render_plane_t yplane;		/* current plane, index = -1 */
					/* means all planes */
    const gx_placed_page *pages;
    int num_pages;
} gx_device_clist_reader;

typedef union gx_device_clist_s {
    gx_device_clist_common common;
    gx_device_clist_reader reader;
    gx_device_clist_writer writer;
} gx_device_clist;

extern_st(st_device_clist);
#define public_st_device_clist()	/* in gxclist.c */\
  gs_public_st_complex_only(st_device_clist, gx_device_clist,\
    "gx_device_clist", 0, device_clist_enum_ptrs, device_clist_reloc_ptrs,\
    gx_device_finalize)
#define st_device_clist_max_ptrs\
  (st_device_forward_max_ptrs + st_imager_state_num_ptrs + 1)

/* setup before opening clist device */
#define clist_init_params(xclist, xdata, xdata_size, xtarget, xbuf_procs, xband_params, xexternal, xmemory, xfree_bandlist, xdisable)\
    BEGIN\
	(xclist)->common.data = (xdata);\
	(xclist)->common.data_size = (xdata_size);\
	(xclist)->common.target = (xtarget);\
	(xclist)->common.buf_procs = (xbuf_procs);\
	(xclist)->common.band_params = (xband_params);\
	(xclist)->common.do_not_open_or_close_bandfiles = (xexternal);\
	(xclist)->common.bandlist_memory = (xmemory);\
	(xclist)->writer.free_up_bandlist_memory = (xfree_bandlist);\
	(xclist)->writer.disable_mask = (xdisable);\
    END

/* Determine whether this clist device is able to recover VMerrors */
#define clist_test_VMerror_recoverable(cldev)\
  ((cldev)->free_up_bandlist_memory != 0)

/* The device template itself is never used, only the procedures. */
extern const gx_device_procs gs_clist_device_procs;

/* Reset (or prepare to append to) the command list after printing a page. */
int clist_finish_page(P2(gx_device * dev, bool flush));

/* Close the band files and delete their contents. */
int clist_close_output_file(P1(gx_device *dev));

/*
 * Close and delete the contents of the band files associated with a
 * page_info structure (a page that has been separated from the device).
 */
int clist_close_page_info(P1(gx_band_page_info_t *ppi));

/*
 * Compute the colors-used information in the page_info structure from the
 * information in the individual writer bands.  This is only useful at the
 * end of a page.  gdev_prn_colors_used calls this procedure if it hasn't
 * been called since the page was started.  clist_end_page also calls it.
 */
void clist_compute_colors_used(P1(gx_device_clist_writer *cldev));

/* Define the abstract type for a printer device. */
#ifndef gx_device_printer_DEFINED
#  define gx_device_printer_DEFINED
typedef struct gx_device_printer_s gx_device_printer;
#endif

/* Do device setup from params passed in the command list. */
int clist_setup_params(P1(gx_device *dev));

/*
 * Render a rectangle to a client-supplied image.  This implements
 * gdev_prn_render_rectangle for devices that are using banding.
 * 
 * Note that clist_render_rectangle only guarantees to render *at least* the
 * requested rectangle to bdev, offset by (-prect->p.x, -prect->p.y):
 * anything it does to avoid rendering regions outside the rectangle is
 * merely an optimization.  If the client really wants the output clipped to
 * some rectangle smaller than ((0, 0), (bdev->width, bdev->height)), it
 * must set up a clipping device.
 */
int clist_render_rectangle(P5(gx_device_clist *cdev,
			      const gs_int_rect *prect, gx_device *bdev,
			      const gx_render_plane_t *render_plane,
			      bool clear));

#endif /* gxclist_INCLUDED */
