/* Copyright (C) 1991, 2000 Aladdin Enterprises.  All rights reserved.
  
  This file is part of AFPL Ghostscript.
  
  AFPL Ghostscript is distributed with NO WARRANTY OF ANY KIND.  No author or
  distributor accepts any responsibility for the consequences of using it, or
  for whether it serves any particular purpose or works at all, unless he or
  she says so in writing.  Refer to the Aladdin Free Public License (the
  "License") for full details.
  
  Every copy of AFPL Ghostscript must include a copy of the License, normally
  in a plain ASCII text file named PUBLIC.  The License grants you the right
  to copy, modify and redistribute AFPL Ghostscript, but only under certain
  conditions described in the License.  Among other things, the License
  requires that the copyright notice and this notice be preserved on all
  copies.
*/

/*$Id: gscspace.h,v 1.5 2000/09/19 19:00:27 lpd Exp $ */
/* Client interface to color spaces */

#ifndef gscspace_INCLUDED
#  define gscspace_INCLUDED

#include "gsmemory.h"

/*
 * The handling of color spaces in the graphic library is somewhat
 * awkward because of historical artifacts.
 *
 * The PostScript Level 1 (DeviceGray/RGB) color spaces, and the "Level
 * 1 1/2" DeviceCMYK space, have no associated parameters.  Therefore,
 * they can be represented as simple objects (just a pointer to a type
 * vector containing procedures and parameters).  This was the original
 * design.
 *
 * PostScript Level 2 and LanguageLevel 3 add two new kinds of color spaces:
 * color spaces with parameters (CIEBased and DevicePixel spaces), and
 * compound color spaces (Indexed, Separation, Pattern, and DeviceN spaces),
 * which parameters that include specifying an alternate or underlying color
 * space.  To handle these spaces, we extended the original design to store
 * scalar parameters (i.e., parameters other than the complex color
 * transformation data for CIEBased spaces, the lookup table for Indexed
 * spaces, and the list of component names for DeviceN spaces) in-line in
 * the color space object.  For compound spaces, this requires storing a
 * color space in-line inside another color space, which is clearly
 * impossible.  Therefore, we defined a generality hierarchy for color
 * spaces:
 *
 *      - Base spaces (DeviceGray/RGB/CMYK/Pixel and CIEBased),
 *      whose parameters (if any) don't include other color spaces.
 *
 *	- Direct spaces (base spaces + Separation and DeviceN), which
 *	may have a base space as an alternative space.
 *
 *      - Paint spaces (direct spaces + Indexed), which may have a
 *      direct space as the underlying space.
 *
 *      - General spaces (paint spaces + Pattern), which may have a
 *      paint space as the underlying space.
 *
 * With this approach, a general space can include a paint space stored
 * in-line; a paint space (either in its own right or as the underlying
 * space of a Pattern space) can include a direct space in-line; and a
 * direct space can include a base space in-line.
 *
 * Note that because general, paint, direct, and base spaces are
 * (necessarily) of different sizes, assigning (copying the top object of)
 * color spaces must take into account the actual size of the color space
 * being assigned.  In principle, this also requires checking that the
 * source object will actually fit into the destination.  Currently we rely
 * on the caller to ensure that this is the case; in fact, the current API
 * (gs_cspace_init and gs_cspace_assign) doesn't even provide enough
 * information to make the check.
 *
 * In retrospect, we might have gotten a simpler design without significant
 * performance loss by always referencing underlying and alternate spaces
 * through a pointer; however, at this point, the cost and risk of changing
 * to such a design are too high.
 *
 * There are two aspects to memory management for color spaces: managing
 * the color space objects themselves, and managing the non-scalar
 * parameters that they reference (if any).
 *
 *      - Color space objects per se have no special management properties:
 *      they can be allocated on the stack or on the heap, and freed by
 *      scope exit, explicit deallocation, or garbage collection.
 *
 *      - Separately allocated (non-scalar) color space parameters are
 *      managed by reference counting.  Currently we do this for the
 *      CIEBased spaces, and for the first-level parameters of Indexed and
 *      Separation spaces: clients must deal with deallocating the other
 *      parameter structures mentioned above, including the Indexed lookup
 *      table if any. This is clearly not a good situation, but we don't
 *      envision fixing it any time soon.
 *
 * Here is the information associated with the various color space
 * structures.  Note that DevicePixel, DeviceN, and the ability to use
 * Separation or DeviceN spaces as the base space of an Indexed space
 * are LanguageLevel 3 additions.  Unfortunately, the terminology for the
 * different levels of generality is confusing and inconsistent for
 * historical reasons.
 *
 * For base spaces:
 *
 * Space        Space parameters                Color parameters
 * -----        ----------------                ----------------
 * DeviceGray   (none)                          1 real [0-1]
 * DeviceRGB    (none)                          3 reals [0-1]
 * DeviceCMYK   (none)                          4 reals [0-1]
 * DevicePixel  depth                           1 int [up to depth bits]
 * CIEBasedDEFG dictionary                      4 reals
 * CIEBasedDEF  dictionary                      3 reals
 * CIEBasedABC  dictionary                      3 reals
 * CIEBasedA    dictionary                      1 real
 *
 * For non-base direct spaces:
 *
 * Space        Space parameters                Color parameters
 * -----        ----------------                ----------------
 *
 * Separation   name, alt_space, tint_xform     1 real [0-1]
 * DeviceN      names, alt_space, tint_xform    N reals
 *
 * For non-direct paint spaces:
 *
 * Space        Space parameters                Color parameters
 * -----        ----------------                ----------------
 * Indexed      base_space, hival, lookup       1 int [0-hival]
 *
 * For non-paint spaces:
 *
 * Space        Space parameters                Color parameters
 * -----        ----------------                ----------------
 * Pattern      colored: (none)                 dictionary
 *              uncolored: base_space dictionary + base space params */

/*
 * Define color space type indices.  NOTE: PostScript code (gs_res.ps,
 * gs_ll3.ps) and the color space substitution code (gscssub.[hc] and its
 * clients) assumes values 0-2 for DeviceGray/RGB/CMYK respectively.
 */
typedef enum {

    /* Supported in all configurations */
    gs_color_space_index_DeviceGray = 0,
    gs_color_space_index_DeviceRGB,

    /* Supported in extended Level 1, and in Level 2 and above */
    gs_color_space_index_DeviceCMYK,

    /* Supported in LanguageLevel 3 only */
    gs_color_space_index_DevicePixel,
    gs_color_space_index_DeviceN,

    /* Supported in Level 2 and above only */
    /* DEC C truncates identifiers at 32 characters, so.... */
    gs_color_space_index_CIEDEFG,
    gs_color_space_index_CIEDEF,
    gs_color_space_index_CIEABC,
    gs_color_space_index_CIEA,
    gs_color_space_index_Separation,
    gs_color_space_index_Indexed,
    gs_color_space_index_Pattern

} gs_color_space_index;

/* We define the names only for debugging printout. */
#define GS_COLOR_SPACE_TYPE_NAMES\
  "DeviceGray", "DeviceRGB", "DeviceCMYK", "DevicePixel", "DeviceN",\
  "CIEBasedDEFG", "CIEBasedDEF", "CIEBasedABC", "CIEBasedA",\
  "Separation", "Indexed", "Pattern"

/* Define an abstract type for color space types (method structures). */
typedef struct gs_color_space_type_s gs_color_space_type;

/*
 * The common part of all color spaces. This structure now includes a memory
 * structure pointer, so that it may be released without providing this
 * information separately. (type is a pointer to the structure of methods.)
 *
 * Note that all color space structures consist of the basic information and
 * a union containing some additional information. The macro operand is that
 * union.
 */
#define gs_cspace_common(param_union)   \
    const gs_color_space_type * type;   \
    gs_memory_t *               pmem;   \
    gs_id                       id;     \
    union {                             \
	param_union;                    \
    }                           params

/*
 * Parameters for base color spaces. Of the base color spaces, only
 * DevicePixel and CIE spaces have parameters: see gscie.h for the structure
 * definitions for CIE space parameters.
 */
typedef struct gs_device_pixel_params_s {
    int depth;
} gs_device_pixel_params;
typedef struct gs_cie_a_s gs_cie_a;
typedef struct gs_cie_abc_s gs_cie_abc;
typedef struct gs_cie_def_s gs_cie_def;
typedef struct gs_cie_defg_s gs_cie_defg;

#define gs_base_cspace_params           \
    gs_device_pixel_params   pixel;     \
    gs_cie_defg *            defg;      \
    gs_cie_def *             def;       \
    gs_cie_abc *             abc;       \
    gs_cie_a *               a

typedef struct gs_base_color_space_s {
    gs_cspace_common(gs_base_cspace_params);
} gs_base_color_space;

#define gs_base_color_space_size sizeof(gs_base_color_space)

/*
 * Non-base direct color spaces: Separation and DeviceN spaces.
 * These include a base alternative color space.
 */
typedef ulong gs_separation_name;	/* BOGUS */
typedef struct gs_indexed_map_s gs_indexed_map;

typedef struct gs_separation_params_s {
    gs_separation_name sname;
    gs_base_color_space alt_space;
    gs_indexed_map *map;
} gs_separation_params;

#ifndef gs_device_n_map_DEFINED
#  define gs_device_n_map_DEFINED
typedef struct gs_device_n_map_s gs_device_n_map;
#endif
typedef struct gs_device_n_params_s {
    gs_separation_name *names;
    uint num_components;
    gs_base_color_space alt_space;
    gs_device_n_map *map;
} gs_device_n_params;

#define gs_direct_cspace_params         \
    gs_base_cspace_params;              \
    gs_separation_params separation;    \
    gs_device_n_params device_n

typedef struct gs_direct_color_space_s {
    gs_cspace_common(gs_direct_cspace_params);
} gs_direct_color_space;

#define gs_direct_color_space_size sizeof(gs_direct_color_space)

/*
 * Non-direct paint space: Indexed space.
 *
 * Note that for indexed color spaces, hival is the highest support index,
 * which is one less than the number of entries in the palette (as defined
 * in PostScript).
 */

typedef struct gs_indexed_params_s {
    gs_direct_color_space base_space;
    int hival;			/* num_entries - 1 */
    union {
	gs_const_string table;	/* size is implicit */
	gs_indexed_map *map;
    } lookup;
    bool use_proc;		/* 0 = use table, 1 = use proc & map */
} gs_indexed_params;

#define gs_paint_cspace_params          \
    gs_direct_cspace_params;            \
    gs_indexed_params indexed

typedef struct gs_paint_color_space_s {
    gs_cspace_common(gs_paint_cspace_params);
} gs_paint_color_space;

#define gs_paint_color_space_size sizeof(gs_paint_color_space)

/*
 * Pattern parameter set. This may contain an instances of a paintable
 * color space. The boolean indicates if this is the case.
 */
typedef struct gs_pattern_params_s {
    bool has_base_space;
    gs_paint_color_space base_space;
} gs_pattern_params;

/*
 * Fully general color spaces.
 */
struct gs_color_space_s {
    gs_cspace_common(
	gs_paint_cspace_params;
	gs_pattern_params pattern
    );
};

#define gs_pattern_color_space_size sizeof(gs_color_space)

/*
 * Define the abstract type for color space objects.
 */
#ifndef gs_color_space_DEFINED
#  define gs_color_space_DEFINED
typedef struct gs_color_space_s gs_color_space;
#endif

					/*extern_st(st_color_space); *//* in gxcspace.h */
#define public_st_color_space()	/* in gscspace.c */  \
    gs_public_st_composite( st_color_space,         \
                            gs_color_space,         \
                            "gs_color_space",       \
                            color_space_enum_ptrs,  \
                            color_space_reloc_ptrs  \
                            )

#define st_color_space_max_ptrs 2	/* 1 base + 1 indexed */

/* ---------------- Procedures ---------------- */

/* ------ Create/copy/destroy ------ */

/*
 * Note that many of the constructors take no parameters, and the
 * remainder take only a few (CIE color spaces constructures take a
 * client data pointer as an operand, the composite color space (Separation,
 * Indexed, and Pattern) constructurs take the base space as an operand,
 * and the Indexed color space constructors have a few additiona operands).
 * This is done to conserve memory. If initialization values for all the
 * color space parameters were provided to the constructors, these values
 * would need to have some fairly generic format. Different clients gather
 * this data in different forms, so they would need to allocate memory to
 * convert it to the generic form, only to immediately release this memory
 * once the construction is complete.
 *
 * The alternative approach employed here is to provide a number of access
 * methods (macros) that return pointers to individual fields of the
 * various color space structures. This requires exporting only fairly simple
 * data structures, and so does not violate modularity too severely.
 *
 * NB: Of necessity, the macros provide access to modifiable structures. If
 *     these structures are modified after the color space object is first
 *     initialized, unpredictable and, most likely, undesirable results will
 *     occur.
 *
 * The constructors return an integer, 0 on success and an
 * error code on failure (gs_error_VMerror or gs_error_rangecheck).
 *
 * In parallel with the constructors, we provide initializers that assume
 * the client has allocated the color space object (perhaps on the stack)
 * and takes responsibility for freeing it.
 */

extern int
    gs_cspace_init_DeviceGray(P1(gs_color_space *pcs)),
    gs_cspace_build_DeviceGray(P2(gs_color_space ** ppcspace,
				  gs_memory_t * pmem)),
    gs_cspace_init_DeviceRGB(P1(gs_color_space *pcs)),
    gs_cspace_build_DeviceRGB(P2(gs_color_space ** ppcspace,
				 gs_memory_t * pmem)),
    gs_cspace_init_DeviceCMYK(P1(gs_color_space *pcs)),
    gs_cspace_build_DeviceCMYK(P2(gs_color_space ** ppcspace,
				  gs_memory_t * pmem));

/*
 * We preallocate instances of the 3 device color spaces, and provide
 * procedures that return them.  Note that gs_cspace_DeviceCMYK() is
 * defined even if CMYK color support is not included in this configuration.
 */
#ifndef gs_imager_state_DEFINED
#  define gs_imager_state_DEFINED
typedef struct gs_imager_state_s gs_imager_state;
#endif

const gs_color_space * gs_cspace_DeviceGray(P1(const gs_imager_state * pis));
const gs_color_space * gs_cspace_DeviceRGB(P1(const gs_imager_state * pis));
const gs_color_space * gs_cspace_DeviceCMYK(P1(const gs_imager_state * pis));

/* Copy a color space into one newly allocated by the caller. */
void gs_cspace_init_from(P2(gs_color_space * pcsto,
			    const gs_color_space * pcsfrom));

/* Assign a color space into a previously initialized one. */
void gs_cspace_assign(P2(gs_color_space * pdest, const gs_color_space * psrc));

/* Prepare to free a color space. */
void gs_cspace_release(P1(gs_color_space * pcs));

/* ------ Accessors ------ */

/* Get the index of a color space. */
gs_color_space_index gs_color_space_get_index(P1(const gs_color_space *));

/* Get the number of components in a color space. */
int gs_color_space_num_components(P1(const gs_color_space *));

/*
 * Test whether two color spaces are equal.  Note that this test is
 * conservative: if it returns true, the color spaces are definitely
 * equal, while if it returns false, they might still be equivalent.
 */
bool gs_color_space_equal(P2(const gs_color_space *pcs1,
			     const gs_color_space *pcs2));

/* Restrict a color to its legal range. */
#ifndef gs_client_color_DEFINED
#  define gs_client_color_DEFINED
typedef struct gs_client_color_s gs_client_color;
#endif
void gs_color_space_restrict_color(P2(gs_client_color *,
				      const gs_color_space *));

/*
 * Get the base space of an Indexed or uncolored Pattern color space, or the
 * alternate space of a Separation or DeviceN space.  Return NULL if the
 * color space does not have a base/alternative color space.
 */
const gs_color_space *gs_cspace_base_space(P1(const gs_color_space * pcspace));

/* backwards compatibility */
#define gs_color_space_indexed_base_space(pcspace)\
    gs_cspace_base_space(pcspace)

#endif /* gscspace_INCLUDED */
