diff options
author | Henry Stiles <henry.stiles@artifex.com> | 1998-08-08 06:11:33 +0000 |
---|---|---|
committer | Henry Stiles <henry.stiles@artifex.com> | 1998-08-08 06:11:33 +0000 |
commit | 3305477b99710b8ce6223a0bdd5014ced4de6997 (patch) | |
tree | 2cd123878deab83af88cbfcbff04624712c5b46c /gs/src | |
parent | b8cb922d73b866149ca3da2288f1edcf959c45c9 (diff) |
Initial revision
git-svn-id: http://svn.ghostscript.com/ghostpcl/trunk/ghostpcl@277 06663e23-700e-0410-b217-a244a6096597
Diffstat (limited to 'gs/src')
92 files changed, 21748 insertions, 0 deletions
diff --git a/gs/src/gdevpdfo.c b/gs/src/gdevpdfo.c new file mode 100644 index 000000000..8b124845c --- /dev/null +++ b/gs/src/gdevpdfo.c @@ -0,0 +1,675 @@ +/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: gdevpdfo.c */ +/* Named object pdfmark processing */ +#include "memory_.h" +#include "gx.h" +#include "gserrors.h" +#include "gsutil.h" /* for bytes_compare */ +#include "gdevpdfx.h" +#include "strimpl.h" +#include "sstring.h" + +/* GC procedures */ + +private_st_pdf_named_element(); +public_st_pdf_named_object(); + +#define pne ((pdf_named_element *)vptr) +private +ENUM_PTRS_BEGIN(pdf_named_elt_enum_ptrs) return 0; + +ENUM_PTR(0, pdf_named_element, next); +ENUM_STRING_PTR(1, pdf_named_element, key); +/****** WRONG IF data = 0 ******/ +ENUM_STRING_PTR(2, pdf_named_element, value); +ENUM_PTRS_END +private RELOC_PTRS_BEGIN(pdf_named_elt_reloc_ptrs) +{ + RELOC_PTR(pdf_named_element, next); + if (pne->key.data != 0) + RELOC_STRING_PTR(pdf_named_element, key); + RELOC_STRING_PTR(pdf_named_element, value); +} +RELOC_PTRS_END +#undef pne + +#define pno ((pdf_named_object *)vptr) +private ENUM_PTRS_BEGIN(pdf_named_obj_enum_ptrs) return 0; +ENUM_PTR(0, pdf_named_object, next); +ENUM_STRING_PTR(1, pdf_named_object, key); +ENUM_PTR(2, pdf_named_object, elements); +case 3: +if (pno->type == named_graphics) + ENUM_RETURN(pno->graphics.enclosing); +else + return 0; +ENUM_PTRS_END +private RELOC_PTRS_BEGIN(pdf_named_obj_reloc_ptrs) +{ + RELOC_PTR(pdf_named_object, next); + RELOC_STRING_PTR(pdf_named_object, key); + RELOC_PTR(pdf_named_object, elements); + if (pno->type == named_graphics) + RELOC_PTR(pdf_named_object, graphics.enclosing); +} +RELOC_PTRS_END +#undef pno + +/* ---------------- pdfmark processing ---------------- */ + +/* Define the pdfmark types implemented here. */ +private pdfmark_proc(pdfmark_BP); +private pdfmark_proc(pdfmark_EP); +private pdfmark_proc(pdfmark_SP); +private pdfmark_proc(pdfmark_OBJ); +private pdfmark_proc(pdfmark_PUT); +private pdfmark_proc(pdfmark_PUTDICT); +private pdfmark_proc(pdfmark_PUTINTERVAL); +private pdfmark_proc(pdfmark_CLOSE); +const pdfmark_name pdfmark_names_named[] = +{ + {"BP", pdfmark_BP, pdfmark_nameable}, + {"EP", pdfmark_EP, 0}, + {"SP", pdfmark_SP, pdfmark_odd_ok | pdfmark_keep_name}, + {"OBJ", pdfmark_OBJ, pdfmark_nameable}, + {"PUT", pdfmark_PUT, pdfmark_odd_ok | pdfmark_keep_name}, + {".PUTDICT", pdfmark_PUTDICT, pdfmark_odd_ok | pdfmark_keep_name}, + {".PUTINTERVAL", pdfmark_PUTINTERVAL, pdfmark_odd_ok | pdfmark_keep_name}, + {"CLOSE", pdfmark_CLOSE, pdfmark_odd_ok | pdfmark_keep_name}, + {0, 0} +}; + +/* + * Predefined objects: + * {Catalog}, {DocInfo} + * {Page<#>}, {ThisPage}, {PrevPage}, {NextPage} + */ + +/* ---------------- Utilities ---------------- */ + +private int pdfmark_write_named(P2(gx_device_pdf * pdev, + const pdf_named_object * pno)); +private void pdfmark_free_named(P2(gx_device_pdf * pdev, + pdf_named_object * pno)); + +/* ------ Public ------ */ + +/* + * Look up an object name. Return e_rangecheck if the syntax is invalid. + * If the object is missing, then: + * - If create is false, return gs_error_undefined; + * - If create is true, create the object and return 1. + * Note that there is code in gdevpdf.c that relies on the fact that + * the named_objects list is sorted by decreasing object number. + */ +private int +pdfmark_find_named(gx_device_pdf * pdev, const gs_param_string * pname, + pdf_named_object ** ppno, bool create) +{ + const byte *data = pname->data; + uint size = pname->size; + gs_id key_id = + (size == 0 ? 0 : data[0] + data[size / 2] + data[size - 1]); + pdf_resource **plist = + &pdev->resources[resourceNamedObject]. + chains[gs_id_hash(key_id) % num_resource_chains]; + pdf_named_object *pno = (pdf_named_object *) * plist; + + if (!pdfmark_objname_is_valid(data, size)) + return_error(gs_error_rangecheck); + for (; pno; pno = pno->next) + if (!bytes_compare(data, size, pno->key.data, pno->key.size)) { + *ppno = pno; + return 0; + } + if (!create) + return_error(gs_error_undefined); + { + gs_memory_t *mem = pdev->pdf_memory; + byte *key = gs_alloc_string(mem, size, "pdf named object key"); + pdf_resource *pres; + int code; + + if (key == 0) + return_error(gs_error_VMerror); + code = pdf_alloc_resource(pdev, resourceNamedObject, key_id, &pres); + if (code < 0) { + gs_free_string(mem, key, size, "pdf named object key"); + return code; + } + pno = (pdf_named_object *) pres; + memcpy(key, data, size); + pno->id = pdf_obj_ref(pdev); + pno->type = named_unknown; /* caller may change */ + pno->key.data = key; + pno->key.size = size; + pno->elements = 0; + pno->open = true; + /* For now, just link the object onto the private list. */ + pno->next = pdev->named_objects; + pdev->named_objects = pno; + *ppno = pno; + return 1; + } +} + +/* Replace object names with object references in a (parameter) string. */ +private const byte * +pdfmark_next_object(const byte * scan, const byte * end, const byte ** pname, + pdf_named_object ** ppno, gx_device_pdf * pdev) +{ /* + * Starting at scan, find the next object reference, set *pname + * to point to it in the string, store the object at *ppno, + * and return a pointer to the first character beyond the + * reference. If there are no more object references, set + * *pname = end and return end. + */ + const byte *left; + const byte *lit; + const byte *right; + + *ppno = 0; + top:left = memchr(scan, '{', end - scan); + if (left == 0) + return (*pname = end); + lit = memchr(scan, '(', left - scan); + if (lit) { + /* Skip over the string. */ + byte buf[50]; /* size is arbitrary */ + stream_cursor_read r; + stream_cursor_write w; + stream_PSSD_state ss; + int status; + + s_PSSD_init_inline(&ss); + r.ptr = lit - 1; + r.limit = end - 1; + w.limit = buf + sizeof(buf) - 1; + do { + w.ptr = buf - 1; + status = (*s_PSSD_template.process) + ((stream_state *) & ss, &r, &w, true); + } + while (status == 1); + scan = r.ptr + 1; + goto top; + } + right = memchr(left + 1, '}', end - (left + 1)); + if (right == 0) /* malformed name */ + return (*pname = end); + *pname = left; + ++right; + { + gs_param_string sname; + + sname.data = left; + sname.size = right - left; + pdfmark_find_named(pdev, &sname, ppno, false); + } + return right; +} +int +pdfmark_replace_names(gx_device_pdf * pdev, const gs_param_string * from, + gs_param_string * to) +{ + const byte *start = from->data; + const byte *end = start + from->size; + const byte *scan; + uint size = 0; + pdf_named_object *pno; + bool any = false; + byte *sto; + char ref[1 + 10 + 5 + 1]; /* max obj number is 10 digits */ + + /* Do a first pass to compute the length of the result. */ + for (scan = start; scan < end;) { + const byte *sname; + const byte *next = + pdfmark_next_object(scan, end, &sname, &pno, pdev); + + size += sname - scan; + if (pno) { + sprintf(ref, " %ld 0 R ", pno->id); + size += strlen(ref); + } + scan = next; + any |= next != sname; + } + to->persistent = true; /* ??? */ + if (!any) { + to->data = start; + to->size = size; + return 0; + } + sto = gs_alloc_bytes(pdev->pdf_memory, size, + "pdfmark_replace_names"); + if (sto == 0) + return_error(gs_error_VMerror); + to->data = sto; + to->size = size; + /* Do a second pass to do the actual substitutions. */ + for (scan = start; scan < end;) { + const byte *sname; + const byte *next = + pdfmark_next_object(scan, end, &sname, &pno, pdev); + uint copy = sname - scan; + int rlen; + + memcpy(sto, scan, copy); + sto += copy; + if (pno) { + sprintf(ref, " %ld 0 R ", pno->id); + rlen = strlen(ref); + memcpy(sto, ref, rlen); + sto += rlen; + } + scan = next; + } + return 0; +} + +/* Write and free an entire list of named objects. */ +int +pdfmark_write_and_free_named(gx_device_pdf * pdev, pdf_named_object ** ppno) +{ + pdf_named_object *pno = *ppno; + pdf_named_object *next; + + for (; pno; pno = next) { + next = pno->next; + pdfmark_write_named(pdev, pno); + pdfmark_free_named(pdev, pno); + } + *ppno = 0; + return 0; +} + +/* ------ Private ------ */ + +/* Put an element of an array object. */ +private int +pdf_named_array_put(gx_device_pdf * pdev, pdf_named_object * pno, int index, + const gs_param_string * pvalue) +{ + gs_memory_t *mem = pdev->pdf_memory; + pdf_named_element **ppne = &pno->elements; + pdf_named_element *pne; + pdf_named_element *pnext; + gs_string value; + + while ((pnext = *ppne) != 0 && pnext->key.size > index) + ppne = &pnext->next; + value.data = gs_alloc_string(mem, pvalue->size, "named array value"); + if (value.data == 0) + return_error(gs_error_VMerror); + value.size = pvalue->size; + if (pnext && pnext->key.size == index) { + /* We're replacing an existing element. */ + gs_free_string(mem, pnext->value.data, pnext->value.size, + "named array old value"); + pne = pnext; + } else { + /* Create a new element. */ + pne = gs_alloc_struct(mem, pdf_named_element, &st_pdf_named_element, + "named array element"); + if (pne == 0) { + gs_free_string(mem, value.data, value.size, "named array value"); + return_error(gs_error_VMerror); + } + pne->key.data = 0; + pne->key.size = index; + pne->next = pnext; + *ppne = pne; + } + memcpy(value.data, pvalue->data, value.size); + pne->value = value; + return 0; +} + +/* Put an element of a dictionary object. */ +private int +pdf_named_dict_put(gx_device_pdf * pdev, pdf_named_object * pno, + const gs_param_string * pkey, const gs_param_string * pvalue) +{ + gs_memory_t *mem = pdev->pdf_memory; + pdf_named_element **ppne = &pno->elements; + pdf_named_element *pne; + pdf_named_element *pnext; + gs_string value; + + while ((pnext = *ppne) != 0 && + bytes_compare(pnext->key.data, pnext->key.size, + pkey->data, pkey->size) + ) + ppne = &pnext->next; + value.data = gs_alloc_string(mem, pvalue->size, "named dict value"); + if (value.data == 0) + return_error(gs_error_VMerror); + value.size = pvalue->size; + if (pnext) { + /* We're replacing an existing element. */ + gs_free_string(mem, pnext->value.data, pnext->value.size, + "named array old value"); + pne = pnext; + } else { + /* Create a new element. */ + gs_string key; + + key.data = gs_alloc_string(mem, pkey->size, "named dict key"); + key.size = pkey->size; + pne = gs_alloc_struct(mem, pdf_named_element, &st_pdf_named_element, + "named dict element"); + if (key.data == 0 || pne == 0) { + gs_free_object(mem, pne, "named dict element"); + if (key.data) + gs_free_string(mem, key.data, key.size, "named dict key"); + gs_free_string(mem, value.data, value.size, "named dict value"); + return_error(gs_error_VMerror); + } + pne->key = key; + memcpy(key.data, pkey->data, key.size); + pne->next = pnext; + *ppne = pne; + } + memcpy(value.data, pvalue->data, value.size); + pne->value = value; + return 0; +} + +/* Write out the definition of a named object. */ +private pdf_named_element * +pdf_reverse_elements(pdf_named_element * pne) +{ + pdf_named_element *prev = NULL; + pdf_named_element *next; + + for (; pne; pne = next) + next = pne->next, pne->next = prev, prev = pne; + return prev; +} +private int +pdfmark_write_named(gx_device_pdf * pdev, const pdf_named_object * pno) +{ + stream *s = pdev->strm; + pdf_named_element *pne = pno->elements; + + switch (pno->type) { + case named_array:{ + uint last_index = 0; + pdf_named_element *last = pne = pdf_reverse_elements(pne); + + pdf_open_obj(pdev, pno->id); + pputs(s, "["); + for (; pne; ++last_index, pne = pne->next) { + for (; pne->key.size > last_index; ++last_index) + pputs(s, "null\n"); + pdf_put_value(pdev, pne->value.data, pne->value.size); + pputc(s, '\n'); + } + pdf_reverse_elements(last); + pputs(s, "]"); + } + break; + case named_dict: + pdf_open_obj(pdev, pno->id); + pputs(s, "<<"); + dict:for (; pne; pne = pne->next) { + pdf_put_value(pdev, pne->key.data, pne->key.size); + pputc(s, ' '); + pdf_put_value(pdev, pne->value.data, pne->value.size); + pputc(s, '\n'); + } + pputs(s, ">>"); + break; + case named_stream:{ + pdf_named_element *last = pne = pdf_reverse_elements(pne); + +/****** NYI ******/ +#if 0 + pdf_open_stream(pdev, pno->id); +/****** DOESN'T EXIST ******/ + for (; pne; pne = pne->next) + pwrite(s, pne->value.data, pne->value.size); + pdf_close_stream(pdev); +/****** DITTO ******/ +#endif + pdf_reverse_elements(last); + } + break; +/****** WHAT TO DO WITH GRAPHICS/UNDEFINED? ******/ + default: + return 0; + } + pdf_end_obj(pdev); + return 0; +} + +/* Free the definition of a named object. */ +private void +pdfmark_free_named(gx_device_pdf * pdev, pdf_named_object * pno) +{ + pdf_named_element *pne = pno->elements; + pdf_named_element *next; + + for (; pne; pne = next) { + next = pne->next; + gs_free_string(pdev->pdf_memory, pne->value.data, pne->value.size, + "named object element value"); + if (pne->key.data) + gs_free_string(pdev->pdf_memory, pne->key.data, pne->key.size, + "named object element key"); + } + gs_free_string(pdev->pdf_memory, pno->key.data, pno->key.size, + "named object key"); + gs_free_object(pdev->pdf_memory, pno, "named object"); +} + +/* ---------------- Individual pdfmarks ---------------- */ + +/* [ /BBox [llx lly urx ury] /_objdef {obj} /BP pdfmark */ +private int +pdfmark_BP(gx_device_pdf * pdev, gs_param_string * pairs, uint count, + const gs_matrix * pctm, const gs_param_string * objname) +{ + gs_rect bbox; + pdf_named_object *pno; + int code; + + if (objname == 0 || count != 2 || !pdf_key_eq(&pairs[0], "BBox")) + return_error(gs_error_rangecheck); + if (sscanf((const char *)pairs[1].data, "[%lg %lg %lg %lg]", + &bbox.p.x, &bbox.p.y, &bbox.q.x, &bbox.q.y) != 4 + ) + return_error(gs_error_rangecheck); + code = pdfmark_find_named(pdev, objname, &pno, true); + if (code < 0) + return code; + if (pno->type != named_unknown) + return_error(gs_error_rangecheck); + code = pdf_named_dict_put(pdev, pno, &pairs[0], &pairs[1]); + if (code < 0) + return code; + pno->type = named_graphics; + pno->graphics.enclosing = pdev->open_graphics; + pdev->open_graphics = pno; +/****** NYI ******/ + return 0; +} + +/* [ /EP pdfmark */ +private int +pdfmark_EP(gx_device_pdf * pdev, gs_param_string * pairs, uint count, + const gs_matrix * pctm, const gs_param_string * no_objname) +{ + pdf_named_object *pno = pdev->open_graphics; + + if (count != 0 || pno == 0 || pno->type != named_graphics || + !pno->open + ) + return_error(gs_error_rangecheck); + pno->open = false; + pdev->open_graphics = pno->graphics.enclosing; +/****** NYI ******/ + return 0; +} + +/* [ {obj} /SP pdfmark */ +private int +pdfmark_SP(gx_device_pdf * pdev, gs_param_string * pairs, uint count, + const gs_matrix * pctm, const gs_param_string * no_objname) +{ + pdf_named_object *pno; + int code; + + if (count != 1) + return_error(gs_error_rangecheck); + if ((code = pdfmark_find_named(pdev, &pairs[0], &pno, false)) < 0) + return code; + if (pno->type != named_graphics || pno->open) + return_error(gs_error_rangecheck); + code = pdf_open_contents(pdev, pdf_in_stream); + if (code < 0) + return code; + pdf_put_matrix(pdev, "q ", pctm, "cm\n"); + pprintld1(pdev->strm, "/R%ld Do Q\n", pno->id); + return 0; +} + +/* [ /_objdef {array} /type /array /OBJ pdfmark */ +/* [ /_objdef {dict} /type /dict /OBJ pdfmark */ +/* [ /_objdef {stream} /type /stream /OBJ pdfmark */ +private int +pdfmark_OBJ(gx_device_pdf * pdev, gs_param_string * pairs, uint count, + const gs_matrix * pctm, const gs_param_string * objname) +{ + pdf_named_object_type type; + pdf_named_object *pno; + int code; + + if (objname == 0 || count != 2 || !pdf_key_eq(&pairs[0], "type")) + return_error(gs_error_rangecheck); + if (pdf_key_eq(&pairs[1], "/array")) + type = named_array; + else if (pdf_key_eq(&pairs[1], "/dict")) + type = named_dict; + else if (pdf_key_eq(&pairs[1], "/stream")) + type = named_stream; + else + return_error(gs_error_rangecheck); + if ((code = pdfmark_find_named(pdev, objname, &pno, true)) < 0) + return code; + if (pno->type != named_unknown) + return_error(gs_error_rangecheck); + pno->type = type; + return 0; +} + +/* [ {array} index value /PUT pdfmark */ +/* [ {stream} string|file /PUT pdfmark */ +/* Dictionaries are converted to .PUTDICT */ +private int +pdfmark_PUT(gx_device_pdf * pdev, gs_param_string * pairs, uint count, + const gs_matrix * pctm, const gs_param_string * no_objname) +{ + pdf_named_object *pno; + int code; + + if ((code = pdfmark_find_named(pdev, &pairs[0], &pno, false)) < 0) + return code; + switch (pno->type) { + case named_array:{ + int index; + + if (count != 3) + return_error(gs_error_rangecheck); + if ((code = pdfmark_scan_int(&pairs[1], &index)) < 0) + return code; + if (index < 0) + return_error(gs_error_rangecheck); + code = pdf_named_array_put(pdev, pno, index, pairs + 2); + break; + } + case named_stream: + if (count != 2) + return_error(gs_error_rangecheck); +/****** NYI ******/ + break; + default: + return_error(gs_error_rangecheck); + } + return code; +} + +/* [ {dict} key value ... /.PUTDICT pdfmark */ +private int +pdfmark_PUTDICT(gx_device_pdf * pdev, gs_param_string * pairs, uint count, + const gs_matrix * pctm, const gs_param_string * no_objname) +{ + pdf_named_object *pno; + int code, i; + + if ((code = pdfmark_find_named(pdev, &pairs[0], &pno, false)) < 0) + return code; + if (!(count & 1) || pno->type != named_dict) + return_error(gs_error_rangecheck); + for (i = 1; code >= 0 && i < count; i += 2) + code = pdf_named_dict_put(pdev, pno, pairs + i, pairs + i + 1); + return code; +} + +/* [ {array} index value ... /.PUTINTERVAL pdfmark */ +private int +pdfmark_PUTINTERVAL(gx_device_pdf * pdev, gs_param_string * pairs, uint count, + const gs_matrix * pctm, const gs_param_string * no_objname) +{ + pdf_named_object *pno; + int code, index, i; + + if ((code = pdfmark_find_named(pdev, &pairs[0], &pno, false)) < 0) + return code; + if (count < 2 || pno->type != named_array) + return_error(gs_error_rangecheck); + if ((code = pdfmark_scan_int(&pairs[1], &index)) < 0) + return code; + if (index < 0) + return_error(gs_error_rangecheck); + for (i = 2; code >= 0 && i < count; ++i) + code = pdf_named_array_put(pdev, pno, index + i - 2, &pairs[i]); + return code; +} + +/* [ {stream} /CLOSE pdfmark */ +private int +pdfmark_CLOSE(gx_device_pdf * pdev, gs_param_string * pairs, uint count, + const gs_matrix * pctm, const gs_param_string * no_objname) +{ + pdf_named_object *pno; + int code; + + if (count != 1) + return_error(gs_error_rangecheck); + if ((code = pdfmark_find_named(pdev, &pairs[0], &pno, false)) < 0) + return code; + if (pno->type != named_stream || !pno->open) + return_error(gs_error_rangecheck); + /* Currently we don't do anything special when closing a stream. */ + pno->open = false; + return 0; +} diff --git a/gs/src/gdevpsci.c b/gs/src/gdevpsci.c new file mode 100644 index 000000000..45742205e --- /dev/null +++ b/gs/src/gdevpsci.c @@ -0,0 +1,130 @@ +/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: gdevpsci.c */ +/* PostScript color image output device */ +#include "gdevprn.h" +#include "stream.h" +#include "strimpl.h" +#include "srlx.h" + +/* + * This driver produces plane-separated, run-length-encoded, 24-bit RGB + * images suitable for a PostScript Level 2 printer. LZW compression would + * be better, but Unisys' claim to own the compression algorithm and their + * demand for licensing and payment even for freely distributed software + * rule this out. + */ + +/* Define the device parameters. */ +#ifndef X_DPI +# define X_DPI 300 +#endif +#ifndef Y_DPI +# define Y_DPI 300 +#endif + +/* The device descriptor */ +private dev_proc_print_page(psrgb_print_page); + +private const gx_device_procs psrgb_procs = +prn_color_procs(gdev_prn_open, gdev_prn_output_page, gdev_prn_close, + gx_default_rgb_map_rgb_color, gx_default_rgb_map_color_rgb); + +const gx_device_printer gs_psrgb_device = +{ + prn_device_body(gx_device_printer, psrgb_procs, "psrgb", + DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS, + X_DPI, Y_DPI, + 0, 0, 0, 0, /* margins */ + 3, 24, 255, 255, 256, 256, psrgb_print_page) +}; + +/* + * The following setup code gets written to the PostScript file. + * We would have to break it up anyway because the Watcom compiler has + * a limit of 512 characters in a single token, so we make a virtue out of + * necessity and make each line a separate string. + */ +private const char *const psrgb_setup[] = +{ + "%!PS", + "currentpagedevice /PageSize get aload pop scale", + "/rgbimage {", /* <width> <height> rgbimage - */ + " /h exch def /w exch def save", + " /s1 w string def /s2 w string def /s3 w string def", + " /f currentfile /RunLengthDecode filter def", + " w h 8 [w 0 0 h neg 0 h]", + " { f s1 readstring pop} { f s2 readstring pop} { f s3 readstring pop}", + " true 3 colorimage restore", + "} bind def" +}; + +/* Send the page to the printer. */ +private int +psrgb_print_page(gx_device_printer * pdev, FILE * prn_stream) +{ + gs_memory_t *mem = pdev->memory; + int width = pdev->width; + byte *lbuf = gs_alloc_bytes(mem, width * 3, + "psrgb_print_page(lbuf)"); + int lnum; + stream fs, rls; + stream_RLE_state rlstate; + byte fsbuf[200]; /* arbitrary, must be >2 */ + byte rlsbuf[200]; /* arbitrary, must be >128 */ + + if (lbuf == 0) + return_error(gs_error_VMerror); + if (gdev_prn_file_is_new(pdev)) { + int i; + + for (i = 0; i < countof(psrgb_setup); i++) + fprintf(prn_stream, "%s\n", psrgb_setup[i]); + } + fprintf(prn_stream, "%d %d rgbimage\n", width, pdev->height); + swrite_file(&fs, prn_stream, fsbuf, sizeof(fsbuf)); + fs.memory = 0; + (*s_RLE_template.set_defaults) ((stream_state *) & rlstate); + s_std_init(&rls, rlsbuf, sizeof(rlsbuf), &s_filter_write_procs, + s_mode_write); + rls.memory = 0; + rlstate.memory = 0; + rlstate.template = &s_RLE_template; + (*s_RLE_template.init) ((stream_state *) & rlstate); + rls.state = (stream_state *) & rlstate; + rls.procs.process = s_RLE_template.process; + rls.strm = &fs; + for (lnum = 0; lnum < pdev->height; ++lnum) { + byte *data; + int i, c; + + gdev_prn_get_bits(pdev, lnum, lbuf, &data); + for (c = 0; c < 3; ++c) { + const byte *p; + + for (i = 0, p = data + c; i < width; ++i, p += 3) + sputc(&rls, *p); + } + } + sclose(&rls); + sflush(&fs); + fputs("\nshowpage\n", prn_stream); + gs_free_object(mem, lbuf, "psrgb_print_page(lbuf)"); + return 0; +} diff --git a/gs/src/gdevpsde.c b/gs/src/gdevpsde.c new file mode 100644 index 000000000..c68562a65 --- /dev/null +++ b/gs/src/gdevpsde.c @@ -0,0 +1,276 @@ +/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: gdevpsde.c */ +/* Embedded font writing */ +#include "memory_.h" +#include "gx.h" +#include "gserrors.h" +#include "gsccode.h" +#include "gsmatrix.h" +#include "gxfixed.h" +#include "gxfont.h" +#include "gxfont1.h" +#include "stream.h" +#include "gdevpstr.h" +#include "gdevpsdf.h" + +private int +embed_table(gs_param_list * plist, const char *key, const float *values, + int count) +{ + if (count != 0) { + gs_param_float_array fa; + + fa.size = count; + fa.data = values; + return param_write_float_array(plist, key, &fa); + } + return 0; +} + +private void +embed_uid(stream * s, const gs_uid * puid) +{ + if (uid_is_UniqueID(puid)) + pprintld1(s, "/UniqueID %ld def\n", puid->id); + else if (uid_is_XUID(puid)) { + uint i, n = uid_XUID_size(puid); + + pputs(s, "/XUID ["); + for (i = 0; i < n; ++i) + pprintld1(s, "%ld ", uid_XUID_values(puid)[i]); + pputs(s, "] def\n"); + } +} + +/* Write an embedded Type 1 font. */ +int +psdf_embed_type1_font(stream * s, gs_font_type1 * pfont) +{ + const gs_type1_data *const pdata = &pfont->data; + gs_param_list *plist; + param_printer_params_t ppp; + int code; + + ppp = param_printer_params_default; + ppp.item_suffix = " def\n"; + code = psdf_alloc_param_printer(&plist, &ppp, s, + print_binary_ok, s->memory); + if (code < 0) + return 0; + + /* Write the font header. */ + + pputs(s, "%!PS-AdobeFont-1.0: "); + pwrite(s, pfont->font_name.chars, pfont->font_name.size); + pputs(s, "\n11 dict begin\n"); + + /* Write FontInfo. Currently we don't write anything there. */ + + pputs(s, "/FontInfo 1 dict dup begin\n"); + pputs(s, "end readonly def\n"); + + /* Write the main font dictionary. */ + + pputs(s, "/FontName /"); + pwrite(s, pfont->font_name.chars, pfont->font_name.size); + pputs(s, " def\n"); + pputs(s, "/Encoding "); + switch (pfont->encoding_index) { + case 0: + pputs(s, "StandardEncoding"); + break; + case 1: + pputs(s, "ISOLatin1Encoding"); + break; + default:{ + gs_char i; + + pputs(s, "256 array\n"); + pputs(s, "0 1 255 {1 index exch /.notdef put} for\n"); + for (i = 0; i < 256; ++i) { + gs_glyph glyph = + (*pfont->procs.encode_char) (NULL, (gs_font *) pfont, &i); + const char *namestr; + uint namelen; + + if (glyph != gs_no_glyph && + (namestr = (*pfont->procs.callbacks.glyph_name) (glyph, &namelen)) != 0 && + !(namelen == 7 && !memcmp(namestr, ".notdef", 7)) + ) { + pprintd1(s, "dup %d /", (int)i); + pwrite(s, namestr, namelen); + pputs(s, " put\n"); + } + } + pputs(s, "readonly"); + } + } + pputs(s, " def\n"); + pprintg6(s, "/FontMatrix [%g %g %g %g %g %g] readonly def\n", + pfont->FontMatrix.xx, pfont->FontMatrix.xy, + pfont->FontMatrix.yx, pfont->FontMatrix.yy, + pfont->FontMatrix.tx, pfont->FontMatrix.ty); + embed_uid(s, &pfont->UID); + pprintg4(s, "/FontBBox {%g %g %g %g} readonly def\n", + pfont->FontBBox.p.x, pfont->FontBBox.p.y, + pfont->FontBBox.q.x, pfont->FontBBox.q.y); + { + private const gs_param_item_t font_items[] = + { + {"FontType", gs_param_type_int, + offset_of(gs_font_type1, FontType)}, + {"PaintType", gs_param_type_int, + offset_of(gs_font_type1, PaintType)}, + {"StrokeWidth", gs_param_type_float, + offset_of(gs_font_type1, StrokeWidth)}, + gs_param_item_end + }; + + code = gs_param_write_items(plist, pfont, NULL, font_items); + if (code < 0) + return code; + } + pputs(s, "currentdict end\n"); + + /* Write the Private dictionary. */ + + pputs(s, "dup /Private 17 dict dup begin\n"); + pputs(s, "/-|{string currentfile exch readstring pop}executeonly def\n"); + pputs(s, "/|-{noaccess def}executeonly def\n"); + pputs(s, "/|{noaccess put}executeonly def\n"); + { + private const gs_param_item_t private_items[] = + { + {"lenIV", gs_param_type_int, + offset_of(gs_type1_data, lenIV)}, + {"BlueFuzz", gs_param_type_int, + offset_of(gs_type1_data, BlueFuzz)}, + {"BlueScale", gs_param_type_float, + offset_of(gs_type1_data, BlueScale)}, + {"BlueShift", gs_param_type_float, + offset_of(gs_type1_data, BlueShift)}, + {"ExpansionFactor", gs_param_type_float, + offset_of(gs_type1_data, ExpansionFactor)}, + {"ForceBold", gs_param_type_bool, + offset_of(gs_type1_data, ForceBold)}, + {"LanguageGroup", gs_param_type_int, + offset_of(gs_type1_data, LanguageGroup)}, + {"RndStemUp", gs_param_type_bool, + offset_of(gs_type1_data, RndStemUp)}, + gs_param_item_end + }; + gs_type1_data defaults; + + defaults.lenIV = 4; + defaults.BlueFuzz = 1; + defaults.BlueScale = 0.039625; + defaults.BlueShift = 7.0; + defaults.ExpansionFactor = 0.06; + defaults.ForceBold = false; + defaults.LanguageGroup = 0; + defaults.RndStemUp = true; + code = gs_param_write_items(plist, pdata, &defaults, private_items); + if (code < 0) + return code; + embed_table(plist, "BlueValues", pdata->BlueValues.values, + pdata->BlueValues.count); + embed_table(plist, "OtherBlues", pdata->OtherBlues.values, + pdata->OtherBlues.count); + embed_table(plist, "FamilyBlues", pdata->FamilyBlues.values, + pdata->FamilyBlues.count); + embed_table(plist, "FamilyOtherBlues", pdata->FamilyOtherBlues.values, + pdata->FamilyOtherBlues.count); + embed_table(plist, "StdHW", pdata->StdHW.values, + pdata->StdHW.count); + embed_table(plist, "StemSnapH", pdata->StemSnapH.values, + pdata->StemSnapH.count); + embed_table(plist, "StemSnapV", pdata->StemSnapV.values, + pdata->StemSnapV.count); + } + embed_uid(s, &pfont->UID); + pputs(s, "/MinFeature{16 16} |-\n"); + pputs(s, "/password 5839 def\n"); + + /* Write the Subrs. */ + + { + int n, i; + gs_const_string str; + + for (n = 0; + (*pdata->procs->subr_data) (pfont, n, false, &str) != + gs_error_rangecheck; + ) + ++n; + pprintd1(s, "/Subrs %d array\n", n); + for (i = 0; i < n; ++i) + if ((*pdata->procs->subr_data) (pfont, i, false, &str) >= 0) { + char buf[50]; + + sprintf(buf, "dup %d %u -| ", i, str.size); + pputs(s, buf); + pwrite(s, str.data, str.size); + pputs(s, " |\n"); + } + pputs(s, "|-\n"); + } + + /* We don't write OtherSubrs -- there had better not be any! */ + + /* Write the CharStrings. */ + + { + int num_chars = 0; + gs_glyph glyph; + int index = 0; + gs_const_string gdata; + int code; + + for (glyph = gs_no_glyph, index = 0; + code = (*pdata->procs->next_glyph) (pfont, &index, &glyph), + index != 0; + ) + if (code == 0 && (*pdata->procs->glyph_data) (pfont, glyph, &gdata) >= 0) + ++num_chars; + pprintd1(s, "2 index /CharStrings %d dict dup begin\n", num_chars); + for (glyph = gs_no_glyph, index = 0; + code = (*pdata->procs->next_glyph) (pfont, &index, &glyph), + index != 0; + ) + if (code == 0 && (*pdata->procs->glyph_data) (pfont, glyph, &gdata) >= 0) { + uint gssize; + const char *gstr = + (*pfont->procs.callbacks.glyph_name) (glyph, &gssize); + + pputs(s, "/"); + pwrite(s, gstr, gssize); + pprintd1(s, " %d -| ", gdata.size); + pwrite(s, gdata.data, gdata.size); + pputs(s, " |-\n"); + } + } + + /* Wrap up. */ + + pputs(s, "end\nend\nreadonly put\nnoaccess put\n"); + pputs(s, "dup/FontName get exch definefont pop\n"); + psdf_free_param_printer(plist); + return 0; +} diff --git a/gs/src/gdevpsdi.c b/gs/src/gdevpsdi.c new file mode 100644 index 000000000..ce7e4d931 --- /dev/null +++ b/gs/src/gdevpsdi.c @@ -0,0 +1,317 @@ +/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: gdevpsdi.c */ +/* Image compression for PostScript and PDF writers */ +#include "math_.h" +#include "gx.h" +#include "gserrors.h" +#include "gscspace.h" +#include "gdevpsdf.h" +#include "gdevpsds.h" +#include "jpeglib.h" /* for sdct.h */ +#include "strimpl.h" +#include "scfx.h" +#include "sdct.h" +#include "slzwx.h" +#include "spngpx.h" +#include "srlx.h" +#include "szlibx.h" + +/* ---------------- Image compression ---------------- */ + +/* Add a filter to expand or reduce the pixel width if needed. */ +/* At least one of bpc_in and bpc_out is 8; the other is 1, 2, 4, or 8. */ +private int +pixel_resize(psdf_binary_writer * pbw, int width, int num_components, + int bpc_in, int bpc_out) +{ + gs_memory_t *mem = pbw->dev->v_memory; + const stream_template *template; + stream_1248_state *st; + int code; + + if (bpc_out == bpc_in) + return 0; + if (bpc_in < 8) { + static const stream_template *const exts[5] = + { + 0, &s_1_8_template, &s_2_8_template, 0, &s_4_8_template + }; + + template = exts[bpc_in]; + } else { + static const stream_template *const rets[5] = + { + 0, &s_8_1_template, &s_8_2_template, 0, &s_8_4_template + }; + + template = rets[bpc_out]; + } + st = (stream_1248_state *) + s_alloc_state(mem, template->stype, "pixel_resize state"); + if (st == 0) + return_error(gs_error_VMerror); + code = psdf_encode_binary(pbw, template, (stream_state *) st); + if (code < 0) { + gs_free_object(mem, st, "pixel_resize state"); + return code; + } + s_1248_init(st, width, num_components); + return 0; +} + +/* Add the appropriate image compression filter, if any. */ +private int +setup_image_compression(psdf_binary_writer * pbw, const psdf_image_params * pdip, + const gs_image_t * pim) +{ + gx_device_psdf *pdev = pbw->dev; + const stream_template *template = pdip->filter_template; + stream_state *st; + + if (pdip->AutoFilter) { +/****** AutoFilter IS NYI ******/ + /* + * Even though this isn't obvious from the Adobe Tech Note, + * it appears that if UseFlateCompression is true, the default + * compressor for AutoFilter is FlateEncode, not LZWEncode. + */ + template = + (pdev->params.UseFlateCompression && + pdev->version >= psdf_version_ll3 ? + &s_zlibE_template : &s_LZWE_template); + } + if (!pdip->Encode || template == 0) /* no compression */ + return 0; + /* Only use DCTE for 8-bit data. */ + if (template == &s_DCTE_template && + !(pdip->Downsample ? + pdip->Depth == 8 || + (pdip->Depth == -1 && pim->BitsPerComponent == 8) : + pim->BitsPerComponent == 8) + ) { + /* Use LZW instead. */ + template = &s_LZWE_template; + } + st = s_alloc_state(pdev->v_memory, template->stype, + "setup_image_compression"); + if (st == 0) + return_error(gs_error_VMerror); + if (template->set_defaults) + (*template->set_defaults) (st); + if (template == &s_CFE_template) { + stream_CFE_state *const ss = (stream_CFE_state *) st; + + if (pdip->Dict != 0 && pdip->Dict->template == &s_CFE_template) { + stream_state common; + + common = *st; /* save generic info */ + *ss = *(const stream_CFE_state *)pdip->Dict; + *st = common; + } else { + ss->K = -1; + ss->BlackIs1 = true; + } + ss->Columns = pim->Width; + ss->Rows = (ss->EndOfBlock ? 0 : pim->Height); + } else if (template == &s_LZWE_template || + template == &s_zlibE_template) { + /* Add a PNGPredictor filter. */ + int code = psdf_encode_binary(pbw, template, st); + + if (code < 0) { + gs_free_object(pdev->v_memory, st, "setup_image_compression"); + return code; + } + template = &s_PNGPE_template; + st = s_alloc_state(pdev->v_memory, template->stype, + "setup_image_compression"); + if (st == 0) + return_error(gs_error_VMerror); + if (template->set_defaults) + (*template->set_defaults) (st); + { + stream_PNGP_state *const ss = (stream_PNGP_state *) st; + + ss->Colors = gs_color_space_num_components(pim->ColorSpace); + ss->Columns = pim->Width; + } + } else if (template == &s_DCTE_template) { +/****** ADD PARAMETERS FROM pdip->Dict ******/ + } { + int code = psdf_encode_binary(pbw, template, st); + + if (code < 0) { + gs_free_object(pdev->v_memory, st, "setup_image_compression"); + return code; + } + } + return 0; +} + +/* Add downsampling, antialiasing, and compression filters. */ +/* Uses AntiAlias, Depth, DownsampleType, Resolution. */ +private int +setup_downsampling(psdf_binary_writer * pbw, const psdf_image_params * pdip, + gs_image_t * pim, floatp resolution) +{ + gx_device_psdf *pdev = pbw->dev; + const stream_template *template = + (pdip->DownsampleType == ds_Average ? + &s_Average_template : &s_Subsample_template); + int factor = (int)(resolution / pdip->Resolution); + int orig_bpc = pim->BitsPerComponent; + stream_state *st; + int code; + + if (factor <= 1) + return setup_image_compression(pbw, pdip, pim); /* no downsampling */ + st = s_alloc_state(pdev->v_memory, template->stype, + "setup_downsampling"); + if (st == 0) + return_error(gs_error_VMerror); + if (template->set_defaults) + (*template->set_defaults) (st); + { + stream_Downsample_state *const ss = (stream_Downsample_state *) st; + + ss->Colors = gs_color_space_num_components(pim->ColorSpace); + ss->Columns = pim->Width; + ss->XFactor = ss->YFactor = factor; + ss->AntiAlias = pdip->AntiAlias; + if (template->init) + (*template->init) (st); + pim->Width /= factor; + pim->Height /= factor; + pim->BitsPerComponent = 8; + gs_matrix_scale(&pim->ImageMatrix, 1.0 / factor, 1.0 / factor, + &pim->ImageMatrix); +/****** NO ANTI-ALIASING YET ******/ + if ((code = setup_image_compression(pbw, pdip, pim)) < 0 || + (code = psdf_encode_binary(pbw, template, st)) < 0 || + (code = pixel_resize(pbw, pim->Width, ss->Colors, + orig_bpc, pim->BitsPerComponent)) < 0 + ) { + gs_free_object(pdev->v_memory, st, "setup_image_compression"); + return code; + } + } + return 0; +} + +/* Set up compression and downsampling filters for an image. */ +/* Note that this may modify the image parameters. */ +int +psdf_setup_image_filters(gx_device_psdf * pdev, psdf_binary_writer * pbw, + gs_image_t * pim, const gs_matrix * pctm, const gs_imager_state * pis) +{ /* + * The following algorithms are per Adobe Tech Note # 5151, + * "Acrobat Distiller Parameters", revised 16 September 1996 + * for Acrobat(TM) Distiller(TM) 3.0. + * + * The control structure is a little tricky, because filter + * pipelines must be constructed back-to-front. + */ + int code = 0; + psdf_image_params params; + + if (pim->ImageMask) { + params = pdev->params.MonoImage; + params.Depth = 1; + } else { + int ncomp = gs_color_space_num_components(pim->ColorSpace); + int bpc = pim->BitsPerComponent; + + /* + * We can compute the image resolution by: + * W / (W * ImageMatrix^-1 * CTM / HWResolution). + * We can replace W by 1 to simplify the computation. + */ + double resolution; + + if (pctm == 0) + resolution = -1; + else { + gs_point pt; + + /* We could do both X and Y, but why bother? */ + gs_distance_transform_inverse(1.0, 0.0, &pim->ImageMatrix, &pt); + gs_distance_transform(pt.x, pt.y, pctm, &pt); + resolution = 1.0 / hypot(pt.x / pdev->HWResolution[0], + pt.y / pdev->HWResolution[1]); + } + if (ncomp == 1) { + /* Monochrome or gray */ + if (bpc == 1) + params = pdev->params.MonoImage; + else + params = pdev->params.GrayImage; + if (params.Depth == -1) + params.Depth = bpc; + /* Check for downsampling. */ + if (params.Downsample && params.Resolution <= resolution / 2) { + /* Use the downsampled depth, not the original data depth. */ + if (params.Depth == 1) { + params.Filter = pdev->params.MonoImage.Filter; + params.filter_template = pdev->params.MonoImage.filter_template; + params.Dict = pdev->params.MonoImage.Dict; + } else { + params.Filter = pdev->params.GrayImage.Filter; + params.filter_template = pdev->params.GrayImage.filter_template; + params.Dict = pdev->params.GrayImage.Dict; + } + code = setup_downsampling(pbw, ¶ms, pim, resolution); + } else { + code = setup_image_compression(pbw, ¶ms, pim); + } + } else { + /* Color */ + bool cmyk_to_rgb = pdev->params.ConvertCMYKImagesToRGB && + pis != 0 && + gs_color_space_get_index(pim->ColorSpace) == + gs_color_space_index_DeviceCMYK; + + if (cmyk_to_rgb) + pim->ColorSpace = gs_cspace_DeviceRGB(pis); + params = pdev->params.ColorImage; + if (params.Depth == -1) + params.Depth = (cmyk_to_rgb ? 8 : bpc); + if (params.Downsample && params.Resolution <= resolution / 2) { + code = setup_downsampling(pbw, ¶ms, pim, resolution); + } else { + code = setup_image_compression(pbw, ¶ms, pim); + } + if (cmyk_to_rgb) { + gs_memory_t *mem = pdev->v_memory; + stream_C2R_state *ss = (stream_C2R_state *) + s_alloc_state(mem, s_C2R_template.stype, "C2R state"); + int code = pixel_resize(pbw, pim->Width, 3, 8, bpc); + + if (code < 0 || + (code = psdf_encode_binary(pbw, &s_C2R_template, + (stream_state *) ss)) < 0 || + (code = pixel_resize(pbw, pim->Width, 4, bpc, 8)) < 0 + ) + return code; + s_C2R_init(ss, pis); + } + } + } + return code; +} diff --git a/gs/src/gdevpsdp.c b/gs/src/gdevpsdp.c new file mode 100644 index 000000000..d95bb5173 --- /dev/null +++ b/gs/src/gdevpsdp.c @@ -0,0 +1,641 @@ +/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: gdevpsdp.c */ +/* (Distiller) parameter handling for PostScript and PDF writers */ +#include "string_.h" +#include "gx.h" +#include "gserrors.h" +#include "gxdevice.h" +#include "gdevpsdf.h" +#include "gdevpstr.h" +#include "strimpl.h" /* for short-sighted compilers */ +#include "scfx.h" +#include "jpeglib.h" /* for sdct.h */ +#include "sdct.h" +#include "slzwx.h" +#include "srlx.h" +#include "szlibx.h" + +/* ---------------- Get/put Distiller parameters ---------------- */ + +/* + * This code handles all the Distiller parameters except the *ACSDict and + * *ImageDict parameter dictionaries. (It doesn't cause any of the + * parameters actually to have any effect.) + */ + +typedef struct psdf_image_filter_name_s { + const char *pname; + const stream_template *template; + psdf_version min_version; +} psdf_image_filter_name; +typedef struct psdf_image_param_names_s { + const char *ACSDict; /* not used for mono */ + const char *AntiAlias; + const char *AutoFilter; /* not used for mono */ + const char *Depth; + const char *Dict; + const char *Downsample; + const char *DownsampleType; + const char *Encode; + const char *Filter; + const char *Resolution; +} psdf_image_param_names; +private const psdf_image_param_names Color_names = +{ + "ColorACSImageDict", "AntiAliasColorImages", "AutoFilterColorImages", + "ColorImageDepth", "ColorImageDict", + "DownsampleColorImages", "ColorImageDownsampleType", "EncodeColorImages", + "ColorImageFilter", "ColorImageResolution" +}; +private const psdf_image_filter_name Poly_filters[] = +{ + {"DCTEncode", &s_DCTE_template}, + {"FlateEncode", &s_zlibE_template, psdf_version_ll3}, + {"LZWEncode", &s_LZWE_template}, + {0, 0} +}; +private const psdf_image_param_names Gray_names = +{ + "GrayACSImageDict", "AntiAliasGrayImages", "AutoFilterGrayImages", + "GrayImageDepth", "GrayImageDict", + "DownsampleGrayImages", "GrayImageDownsampleType", "EncodeGrayImages", + "GrayImageFilter", "GrayImageResolution" +}; +private const psdf_image_param_names Mono_names = +{ + 0, "AntiAliasMonoImages", 0, + "MonoImageDepth", "MonoImageDict", + "DownsampleMonoImages", "MonoImageDownsampleType", "EncodeMonoImages", + "MonoImageFilter", "MonoImageResolution" +}; +private const psdf_image_filter_name Mono_filters[] = +{ + {"CCITTFaxEncode", &s_CFE_template}, + {"FlateEncode", &s_zlibE_template, psdf_version_ll3}, + {"LZWEncode", &s_LZWE_template}, + {"RunLengthEncode", &s_RLE_template}, + {0, 0} +}; +private const char *const AutoRotatePages_names[] = +{ + "None", "All", "PageByPage", 0 +}; +private const char *const ColorConversionStrategy_names[] = +{ + "LeaveColorUnchanged", "UseDeviceDependentColor", + "UseDeviceIndependentColor", 0 +}; +private const char *const DownsampleType_names[] = +{ + "Average", "Subsample", 0 +}; +private const char *const TransferFunctionInfo_names[] = +{ + "Preserve", "Apply", "Remove", 0 +}; +private const char *const UCRandBGInfo_names[] = +{ + "Preserve", "Remove", 0 +}; + +/* -------- Get parameters -------- */ + +extern stream_state_proc_get_params(s_DCTE_get_params, stream_DCT_state); +extern stream_state_proc_get_params(s_CF_get_params, stream_CF_state); +typedef stream_state_proc_get_params((*ss_get_params_t), stream_state); + +private int +psdf_CF_get_params(gs_param_list * plist, const stream_state * ss, bool all) +{ + return s_CF_get_params(plist, (const stream_CF_state *)ss, all); +} +private int +psdf_DCT_get_params(gs_param_list * plist, const stream_state * ss, bool all) +{ + return s_DCTE_get_params(plist, (const stream_DCT_state *)ss, all); +} + +/* + * Get an image Dict parameter. Note that we return an empty dictionary if + * the parameter has never been set. + */ +private int +psdf_get_image_dict_param(gs_param_list * plist, const gs_param_name pname, + stream_state * ss, ss_get_params_t get_params) +{ + gs_param_dict dict; + int code; + + if (pname == 0) + return 0; + dict.size = 12; /* enough for all param dicts we know about */ + if ((code = param_begin_write_dict(plist, pname, &dict, false)) < 0) + return code; + if (ss != 0) + code = (*get_params) (dict.list, ss, false); + param_end_write_dict(plist, pname, &dict); + return code; +} + +/* Get a set of image-related parameters. */ +private int +psdf_get_image_params(gs_param_list * plist, + const psdf_image_param_names * pnames, psdf_image_params * params) +{ + int code; + gs_param_string dsts, fs; + + param_string_from_string(dsts, + DownsampleType_names[params->DownsampleType]); + if ( + (code = psdf_get_image_dict_param(plist, pnames->ACSDict, + params->ACSDict, + psdf_DCT_get_params)) < 0 || + (code = param_write_bool(plist, pnames->AntiAlias, + ¶ms->AntiAlias)) < 0 || + (pnames->AutoFilter != 0 && + (code = param_write_bool(plist, pnames->AutoFilter, + ¶ms->AutoFilter)) < 0) || + (code = param_write_int(plist, pnames->Depth, + ¶ms->Depth)) < 0 || + (code = psdf_get_image_dict_param(plist, pnames->Dict, + params->Dict, + (params->Dict == 0 || + params->Dict->template == + &s_CFE_template ? + psdf_CF_get_params : + psdf_DCT_get_params))) < 0 || + (code = param_write_bool(plist, pnames->Downsample, + ¶ms->Downsample)) < 0 || + (code = param_write_name(plist, pnames->DownsampleType, + &dsts)) < 0 || + (code = param_write_bool(plist, pnames->Encode, + ¶ms->Encode)) < 0 || + (code = (params->Filter == 0 ? 0 : + (param_string_from_string(fs, params->Filter), + param_write_name(plist, pnames->Filter, &fs)))) < 0 || + (code = param_write_int(plist, pnames->Resolution, + ¶ms->Resolution)) < 0 + ) + DO_NOTHING; + return code; +} + +/* Get parameters. */ +int +gdev_psdf_get_params(gx_device * dev, gs_param_list * plist) +{ + gx_device_psdf *pdev = (gx_device_psdf *) dev; + int code = gdev_vector_get_params(dev, plist); + gs_param_string arps, ccss, tfis, ucrbgis; + + if (code < 0) + return code; + param_string_from_string(arps, + AutoRotatePages_names[(int)pdev->params.AutoRotatePages]); + param_string_from_string(ccss, + ColorConversionStrategy_names[(int)pdev->params.ColorConversionStrategy]); + param_string_from_string(tfis, + TransferFunctionInfo_names[(int)pdev->params.TransferFunctionInfo]); + param_string_from_string(ucrbgis, + UCRandBGInfo_names[(int)pdev->params.UCRandBGInfo]); + if ( + /* General parameters */ + + (code = param_write_bool(plist, "ASCII85EncodePages", + &pdev->params.ASCII85EncodePages)) < 0 || + (code = param_write_name(plist, "AutoRotatePages", + &arps)) < 0 || + (code = param_write_bool(plist, "CompressPages", + &pdev->params.CompressPages)) < 0 || + (code = param_write_long(plist, "ImageMemory", + &pdev->params.ImageMemory)) < 0 || + (code = param_write_bool(plist, "LZWEncodePages", + &pdev->params.LZWEncodePages)) < 0 || + (code = param_write_bool(plist, "PreserveHalftoneInfo", + &pdev->params.PreserveHalftoneInfo)) < 0 || + (code = param_write_bool(plist, "PreserveOPIComments", + &pdev->params.PreserveOPIComments)) < 0 || + (code = param_write_bool(plist, "PreserveOverprintSettings", + &pdev->params.PreserveOverprintSettings)) < 0 || + (code = param_write_name(plist, "TransferFunctionInfo", &tfis)) < 0 || + (code = param_write_name(plist, "UCRandBGInfo", &ucrbgis)) < 0 || + (code = param_write_bool(plist, "UseFlateCompression", + &pdev->params.UseFlateCompression)) < 0 || + + /* Color sampled image parameters */ + + (code = psdf_get_image_params(plist, &Color_names, &pdev->params.ColorImage)) < 0 || + (code = param_write_name(plist, "ColorConversionStrategy", + &ccss)) < 0 || + (code = param_write_bool(plist, "ConvertCMYKImagesToRGB", + &pdev->params.ConvertCMYKImagesToRGB)) < 0 || + (code = param_write_bool(plist, "ConvertImagesToIndexed", + &pdev->params.ConvertImagesToIndexed)) < 0 || + + /* Gray sampled image parameters */ + + (code = psdf_get_image_params(plist, &Gray_names, &pdev->params.GrayImage)) < 0 || + + /* Mono sampled image parameters */ + + (code = psdf_get_image_params(plist, &Mono_names, &pdev->params.MonoImage)) < 0 || + + /* Font embedding parameters */ + + (code = param_write_name_array(plist, "AlwaysEmbed", &pdev->params.AlwaysEmbed)) < 0 || + (code = param_write_name_array(plist, "NeverEmbed", &pdev->params.NeverEmbed)) < 0 || + (code = param_write_bool(plist, "EmbedAllFonts", &pdev->params.EmbedAllFonts)) < 0 || + (code = param_write_bool(plist, "SubsetFonts", &pdev->params.SubsetFonts)) < 0 || + (code = param_write_int(plist, "MaxSubsetPct", &pdev->params.MaxSubsetPct)) < 0 + ); + return code; +} + +/* -------- Put parameters -------- */ + +extern stream_state_proc_put_params(s_DCTE_put_params, stream_DCT_state); +extern stream_state_proc_put_params(s_CF_put_params, stream_CF_state); +typedef stream_state_proc_put_params((*ss_put_params_t), stream_state); + +private int +psdf_CF_put_params(gs_param_list * plist, stream_state * st) +{ + stream_CFE_state *const ss = (stream_CFE_state *) st; + + (*s_CFE_template.set_defaults) (st); + ss->K = -1; + ss->BlackIs1 = true; + return s_CF_put_params(plist, (stream_CF_state *) ss); +} +private int +psdf_DCT_put_params(gs_param_list * plist, stream_state * ss) +{ + return s_DCTE_put_params(plist, (stream_DCT_state *) ss); +} + +/* Compare a C string and a gs_param_string. */ +bool +psdf_key_eq(const gs_param_string * pcs, const char *str) +{ + return (strlen(str) == pcs->size && + !strncmp(str, (const char *)pcs->data, pcs->size)); +} + +/* Put an enumerated value. */ +private int +psdf_put_enum_param(gs_param_list * plist, gs_param_name param_name, + int *pvalue, const char *const pnames[], int ecode) +{ + gs_param_string ens; + int code = param_read_name(plist, param_name, &ens); + + switch (code) { + case 1: + return ecode; + case 0: + { + int i; + + for (i = 0; pnames[i] != 0; ++i) + if (psdf_key_eq(&ens, pnames[i])) { + *pvalue = i; + return 0; + } + } + code = gs_error_rangecheck; + default: + ecode = code; + param_signal_error(plist, param_name, code); + } + return code; +} + +/* Put a Boolean or integer parameter. */ +int +psdf_put_bool_param(gs_param_list * plist, gs_param_name param_name, + bool * pval, int ecode) +{ + int code; + + switch (code = param_read_bool(plist, param_name, pval)) { + default: + ecode = code; + param_signal_error(plist, param_name, ecode); + case 0: + case 1: + break; + } + return ecode; +} +int +psdf_put_int_param(gs_param_list * plist, gs_param_name param_name, + int *pval, int ecode) +{ + int code; + + switch (code = param_read_int(plist, param_name, pval)) { + default: + ecode = code; + param_signal_error(plist, param_name, ecode); + case 0: + case 1: + break; + } + return ecode; +} + +/* Put [~](Always|Never)Embed parameters. */ +private int +psdf_put_embed_param(gs_param_list * plist, gs_param_name notpname, + gs_param_string_array * psa, int ecode) +{ + gs_param_name pname = notpname + 1; + int code; + gs_param_string_array nsa; + +/***** Storage management is incomplete ******/ +/***** Doesn't do incremental add/delete ******/ + switch (code = param_read_name_array(plist, pname, psa)) { + default: + ecode = code; + param_signal_error(plist, pname, ecode); + case 0: + case 1: + break; + } + switch (code = param_read_name_array(plist, notpname, &nsa)) { + default: + ecode = code; + param_signal_error(plist, notpname, ecode); + case 0: + case 1: + break; + } + return ecode; +} + +/* Put an image Dict parameter. */ +private int +psdf_put_image_dict_param(gs_param_list * plist, const gs_param_name pname, + stream_state ** pss, const stream_template * template, + ss_put_params_t put_params, gs_memory_t * mem) +{ + gs_param_dict dict; + stream_state *ss = *pss; + int code; + + switch (code = param_begin_read_dict(plist, pname, &dict, false)) { + default: + param_signal_error(plist, pname, code); + return code; + case 1: + ss = 0; + break; + case 0:{ + stream_state *ss_new = + s_alloc_state(mem, template->stype, pname); + + if (ss_new == 0) + return_error(gs_error_VMerror); + ss_new->template = template; + code = (*put_params) (dict.list, ss_new); + if (code < 0) { + param_signal_error(plist, pname, code); + /* Make sure we free the new state. */ + *pss = ss_new; + } else + ss = ss_new; + } + param_end_read_dict(plist, pname, &dict); + } + if (*pss != ss) { + if (ss) { +/****** FREE SUBSIDIARY OBJECTS -- HOW? ******/ + gs_free_object(mem, *pss, pname); + } + *pss = ss; + } + return code; +} + +/* Put a set of image-related parameters. */ +private int +psdf_put_image_params(const gx_device_psdf * pdev, gs_param_list * plist, + const psdf_image_param_names * pnames, const psdf_image_filter_name * pifn, + psdf_image_params * params, int ecode) +{ + gs_param_string fs; + + /* + * Since this procedure can be called before the device is open, + * we must use pdev->memory rather than pdev->v_memory. + */ + gs_memory_t *mem = pdev->memory; + gs_param_name pname; + int dsti = params->DownsampleType; + int code; + + if ((pname = pnames->ACSDict) != 0) { + code = psdf_put_image_dict_param(plist, pname, ¶ms->ACSDict, + &s_DCTE_template, + psdf_DCT_put_params, mem); + if (code < 0) + ecode = code; + } + ecode = psdf_put_bool_param(plist, pnames->AntiAlias, + ¶ms->AntiAlias, ecode); + if (pnames->AutoFilter) + ecode = psdf_put_bool_param(plist, pnames->AutoFilter, + ¶ms->AutoFilter, ecode); + ecode = psdf_put_int_param(plist, pnames->Depth, + ¶ms->Depth, ecode); + if ((pname = pnames->Dict) != 0) { + const stream_template *template; + ss_put_params_t put_params; + + /* Hack to determine what kind of a Dict we want: */ + if (pnames->Dict[0] == 'M') + template = &s_CFE_template, + put_params = psdf_CF_put_params; + else + template = &s_DCTE_template, + put_params = psdf_DCT_put_params; + code = psdf_put_image_dict_param(plist, pname, ¶ms->Dict, + template, put_params, mem); + if (code < 0) + ecode = code; + } + ecode = psdf_put_bool_param(plist, pnames->Downsample, + ¶ms->Downsample, ecode); + if ((ecode = psdf_put_enum_param(plist, pnames->DownsampleType, + &dsti, DownsampleType_names, + ecode)) >= 0 + ) + params->DownsampleType = (enum psdf_downsample_type)dsti; + ecode = psdf_put_bool_param(plist, pnames->Encode, + ¶ms->Encode, ecode); + switch (code = param_read_string(plist, pnames->Filter, &fs)) { + case 0: + { + const psdf_image_filter_name *pn = pifn; + + while (pn->pname != 0 && !psdf_key_eq(&fs, pn->pname)) + pn++; + if (pn->pname == 0 || pn->min_version > pdev->version) { + ecode = gs_error_rangecheck; + goto ipe; + } + params->Filter = pn->pname; + params->filter_template = pn->template; + break; + } + default: + ecode = code; + ipe:param_signal_error(plist, pnames->Filter, ecode); + case 1: + break; + } + ecode = psdf_put_int_param(plist, pnames->Resolution, + ¶ms->Resolution, ecode); + if (ecode >= 0) { /* Force parameters to acceptable values. */ + if (params->Resolution < 1) + params->Resolution = 1; + switch (params->Depth) { + default: + params->Depth = -1; + case 1: + case 2: + case 4: + case 8: + case -1: + break; + } + } + return ecode; +} + +/* Put parameters. */ +int +gdev_psdf_put_params(gx_device * dev, gs_param_list * plist) +{ + gx_device_psdf *pdev = (gx_device_psdf *) dev; + int ecode = 0; + int code; + gs_param_name param_name; + psdf_distiller_params params; + + /* General parameters. */ + + params = pdev->params; + + ecode = psdf_put_bool_param(plist, "ASCII85EncodePages", + ¶ms.ASCII85EncodePages, ecode); + { + int arpi = params.AutoRotatePages; + + ecode = psdf_put_enum_param(plist, "AutoRotatePages", &arpi, + AutoRotatePages_names, ecode); + params.AutoRotatePages = (enum psdf_auto_rotate_pages)arpi; + } + ecode = psdf_put_bool_param(plist, "CompressPages", + ¶ms.CompressPages, ecode); + switch (code = param_read_long(plist, (param_name = "ImageMemory"), ¶ms.ImageMemory)) { + default: + ecode = code; + param_signal_error(plist, param_name, ecode); + case 0: + case 1: + break; + } + ecode = psdf_put_bool_param(plist, "LZWEncodePages", + ¶ms.LZWEncodePages, ecode); + ecode = psdf_put_bool_param(plist, "PreserveHalftoneInfo", + ¶ms.PreserveHalftoneInfo, ecode); + ecode = psdf_put_bool_param(plist, "PreserveOPIComments", + ¶ms.PreserveOPIComments, ecode); + ecode = psdf_put_bool_param(plist, "PreserveOverprintSettings", + ¶ms.PreserveOverprintSettings, ecode); + { + int tfii = params.TransferFunctionInfo; + + ecode = psdf_put_enum_param(plist, "TransferFunctionInfo", &tfii, + TransferFunctionInfo_names, ecode); + params.TransferFunctionInfo = (enum psdf_transfer_function_info)tfii; + } + { + int ucrbgi = params.UCRandBGInfo; + + ecode = psdf_put_enum_param(plist, "UCRandBGInfo", &ucrbgi, + UCRandBGInfo_names, ecode); + params.UCRandBGInfo = (enum psdf_ucr_and_bg_info)ucrbgi; + } + ecode = psdf_put_bool_param(plist, "UseFlateCompression", + ¶ms.UseFlateCompression, ecode); + + /* Color sampled image parameters */ + + ecode = psdf_put_image_params(pdev, plist, &Color_names, Poly_filters, + ¶ms.ColorImage, ecode); + { + int ccsi = params.ColorConversionStrategy; + + ecode = psdf_put_enum_param(plist, "ColorConversionStrategy", &ccsi, + ColorConversionStrategy_names, ecode); + params.ColorConversionStrategy = + (enum psdf_color_conversion_strategy)ccsi; + } + ecode = psdf_put_bool_param(plist, "ConvertCMYKImagesToRGB", + ¶ms.ConvertCMYKImagesToRGB, ecode); + ecode = psdf_put_bool_param(plist, "ConvertImagesToIndexed", + ¶ms.ConvertImagesToIndexed, ecode); + + /* Gray sampled image parameters */ + + ecode = psdf_put_image_params(pdev, plist, &Gray_names, Poly_filters, + ¶ms.GrayImage, ecode); + + /* Mono sampled image parameters */ + + ecode = psdf_put_image_params(pdev, plist, &Mono_names, Mono_filters, + ¶ms.MonoImage, ecode); + + /* Font embedding parameters */ + + ecode = psdf_put_embed_param(plist, "~AlwaysEmbed", + ¶ms.AlwaysEmbed, ecode); + ecode = psdf_put_embed_param(plist, "~NeverEmbed", + ¶ms.NeverEmbed, ecode); + ecode = psdf_put_bool_param(plist, "EmbedAllFonts", + ¶ms.EmbedAllFonts, ecode); + ecode = psdf_put_bool_param(plist, "SubsetFonts", + ¶ms.SubsetFonts, ecode); + ecode = psdf_put_int_param(plist, "MaxSubsetPct", + ¶ms.MaxSubsetPct, ecode); + + if (ecode < 0) + return ecode; + code = gdev_vector_put_params(dev, plist); + if (code < 0) + return code; + + pdev->params = params; /* OK to update now */ + return 0; +} diff --git a/gs/src/gdevpsds.c b/gs/src/gdevpsds.c new file mode 100644 index 000000000..a18e4b587 --- /dev/null +++ b/gs/src/gdevpsds.c @@ -0,0 +1,451 @@ +/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: gdevpsds.c */ +/* Image processing streams for PostScript and PDF writers */ +#include "gx.h" +#include "memory_.h" +#include "gserrors.h" +#include "gxdcconv.h" +#include "gdevpsds.h" + +/* ---------------- Convert between 1/2/4 and 8 bits ---------------- */ +gs_private_st_simple(st_1248_state, stream_1248_state, "stream_1248_state"); + +/* Initialize the state. */ +private int +s_1_init(stream_state * st) +{ + stream_1248_state *const ss = (stream_1248_state *) st; + + ss->left = ss->samples_per_row; + ss->bits_per_sample = 1; + return 0; +} +private int +s_2_init(stream_state * st) +{ + stream_1248_state *const ss = (stream_1248_state *) st; + + ss->left = ss->samples_per_row; + ss->bits_per_sample = 2; + return 0; +} +private int +s_4_init(stream_state * st) +{ + stream_1248_state *const ss = (stream_1248_state *) st; + + ss->left = ss->samples_per_row; + ss->bits_per_sample = 4; + return 0; +} + +/* Process one buffer. */ +#define begin_1248\ + stream_1248_state * const ss = (stream_1248_state *)st;\ + const byte *p = pr->ptr;\ + const byte *rlimit = pr->limit;\ + byte *q = pw->ptr;\ + byte *wlimit = pw->limit;\ + uint left = ss->left;\ + int status;\ + int n +#define end_1248\ + pr->ptr = p;\ + pw->ptr = q;\ + ss->left = left;\ + return status + +/* N-to-8 expansion */ +#define foreach_N_8(in, nout)\ + status = 0;\ + for ( ; p < rlimit; left -= n, q += n, ++p ) {\ + byte in = p[1];\ + n = min(left, nout);\ + if ( wlimit - q < n ) {\ + status = 1;\ + break;\ + }\ + switch ( n ) {\ + case 0: left = ss->samples_per_row; continue; +#define end_foreach\ + }\ + } +private int +s_N_8_process(stream_state * st, stream_cursor_read * pr, + stream_cursor_write * pw, bool last) +{ + begin_1248; + + switch (ss->bits_per_sample) { + + case 1:{ + foreach_N_8(in, 8) + case 8: + q[8] = (byte) - (in & 1); + case 7: + q[7] = (byte) - ((in >> 1) & 1); + case 6: + q[6] = (byte) - ((in >> 2) & 1); + case 5: + q[5] = (byte) - ((in >> 3) & 1); + case 4: + q[4] = (byte) - ((in >> 4) & 1); + case 3: + q[3] = (byte) - ((in >> 5) & 1); + case 2: + q[2] = (byte) - ((in >> 6) & 1); + case 1: + q[1] = (byte) - (in >> 7); + end_foreach; + } + break; + + case 2:{ + static const byte b2[4] = + {0x00, 0x55, 0xaa, 0xff}; + + foreach_N_8(in, 4) + case 4: + q[4] = b2[in & 3]; + case 3: + q[3] = b2[(in >> 2) & 3]; + case 2: + q[2] = b2[(in >> 4) & 3]; + case 1: + q[1] = b2[in >> 6]; + end_foreach; + } + break; + + case 4:{ + static const byte b4[16] = + { + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff + }; + + foreach_N_8(in, 2) + case 2: + q[2] = b4[in & 0xf]; + case 1: + q[1] = b4[in >> 4]; + end_foreach; + } + break; + + default: + return ERRC; + } + + end_1248; +} + +/* 8-to-N reduction */ +#define foreach_8_N(out, nin)\ + byte out;\ + status = 1;\ + for ( ; q < wlimit; left -= n, p += n, *++q = out ) {\ + n = min(left, nin);\ + if ( rlimit - p < n ) {\ + status = 0;\ + break;\ + }\ + out = 0;\ + switch ( n ) {\ + case 0: left = ss->samples_per_row; continue; +private int +s_8_N_process(stream_state * st, stream_cursor_read * pr, + stream_cursor_write * pw, bool last) +{ + begin_1248; + + switch (ss->bits_per_sample) { + + case 1:{ + foreach_8_N(out, 8) + case 8: + out = p[8] >> 7; + case 7: + out |= (p[7] >> 7) << 1; + case 6: + out |= (p[6] >> 7) << 2; + case 5: + out |= (p[5] >> 7) << 3; + case 4: + out |= (p[4] >> 7) << 4; + case 3: + out |= (p[3] >> 7) << 5; + case 2: + out |= (p[2] >> 7) << 6; + case 1: + out |= p[1] & 0x80; + end_foreach; + } + break; + + case 2:{ + foreach_8_N(out, 4) + case 4: + out |= p[4] >> 6; + case 3: + out |= (p[3] >> 6) << 2; + case 2: + out |= (p[2] >> 6) << 4; + case 1: + out |= p[1] & 0xc0; + end_foreach; + } + break; + + case 4:{ + foreach_8_N(out, 2) + case 2: + out |= p[2] >> 4; + case 1: + out |= p[1] & 0xf0; + end_foreach; + } + break; + + default: + return ERRC; + } + + end_1248; +} + +const stream_template s_1_8_template = +{ + &st_1248_state, s_1_init, s_N_8_process, 1, 8 +}; +const stream_template s_2_8_template = +{ + &st_1248_state, s_2_init, s_N_8_process, 1, 4 +}; +const stream_template s_4_8_template = +{ + &st_1248_state, s_4_init, s_N_8_process, 1, 2 +}; + +const stream_template s_8_1_template = +{ + &st_1248_state, s_1_init, s_8_N_process, 8, 1 +}; +const stream_template s_8_2_template = +{ + &st_1248_state, s_2_init, s_8_N_process, 4, 1 +}; +const stream_template s_8_4_template = +{ + &st_1248_state, s_4_init, s_8_N_process, 2, 1 +}; + +/* ---------------- CMYK => RGB conversion ---------------- */ + +private_st_C2R_state(); + +/* Process one buffer. */ +private int +s_C2R_process(stream_state * st, stream_cursor_read * pr, + stream_cursor_write * pw, bool last) +{ + stream_C2R_state *const ss = (stream_C2R_state *) st; + const byte *p = pr->ptr; + const byte *rlimit = pr->limit; + byte *q = pw->ptr; + byte *wlimit = pw->limit; + + for (; rlimit - p >= 4 && wlimit - q >= 3; p += 4, q += 3) { + byte bc = p[1], bm = p[2], by = p[3], bk = p[4]; + frac rgb[3]; + + color_cmyk_to_rgb(byte2frac(bc), byte2frac(bm), byte2frac(by), + byte2frac(bk), ss->pis, rgb); + q[1] = frac2byte(rgb[0]); + q[2] = frac2byte(rgb[1]); + q[3] = frac2byte(rgb[2]); + } + pr->ptr = p; + pw->ptr = q; + return (rlimit - p < 4 ? 0 : 1); +} + +const stream_template s_C2R_template = +{ + &st_C2R_state, 0 /*NULL */ , s_C2R_process, 4, 3 +}; + +/* ---------------- Downsampling ---------------- */ + +/* Subsample */ + +gs_private_st_simple(st_Subsample_state, stream_Subsample_state, + "stream_Subsample_state"); + +/* Initialize the state. */ +private int +s_Subsample_init(stream_state * st) +{ + stream_Subsample_state *const ss = (stream_Subsample_state *) st; + + ss->x = ss->y = 0; + return 0; +} + +/* Process one buffer. */ +private int +s_Subsample_process(stream_state * st, stream_cursor_read * pr, + stream_cursor_write * pw, bool last) +{ + stream_Subsample_state *const ss = (stream_Subsample_state *) st; + const byte *p = pr->ptr; + const byte *rlimit = pr->limit; + byte *q = pw->ptr; + byte *wlimit = pw->limit; + int spp = ss->Colors; + int width = ss->Columns; + int xf = ss->XFactor, yf = ss->YFactor; + int xf2 = xf / 2, yf2 = yf / 2; + int xlimit = (width / xf) * xf; + int x = ss->x, y = ss->y; + int status = 0; + + for (; rlimit - p >= spp; p += spp) { + if (y == yf2 && x % xf == xf2 && x < xlimit) { + if (wlimit - q < spp) { + status = 1; + break; + } + memcpy(q + 1, p + 1, spp); + q += spp; + } + if (++x == width) { + x = 0; + if (++y == yf) { + y = 0; + } + } + } + pr->ptr = p; + pw->ptr = q; + ss->x = x, ss->y = y; + return status; +} + +const stream_template s_Subsample_template = +{ + &st_Subsample_state, s_Subsample_init, s_Subsample_process, 4, 4 +}; + +/* Average */ + +private_st_Average_state(); + +/* Initialize the state. */ +private int +s_Average_init(stream_state * st) +{ + stream_Average_state *const ss = (stream_Average_state *) st; + + ss->sum_size = ss->Colors * (ss->Columns / ss->XFactor); + /* + * We allocate an extra element of sums to avoid treating the extra + * samples of each input scan line as a special case, but we never + * do anything with it (except accumulate into it). + */ + ss->sums = + (uint *) gs_alloc_byte_array(st->memory, ss->sum_size + 1, + sizeof(uint), "Average sums"); + if (ss->sums == 0) + return ERRC; +/****** WRONG ******/ + memset(ss->sums, 0, ss->sum_size * sizeof(uint)); + return s_Subsample_init(st); +} + +/* Release the state. */ +private void +s_Average_release(stream_state * st) +{ + stream_Average_state *const ss = (stream_Average_state *) st; + + gs_free_object(st->memory, ss->sums, "Average sums"); +} + +/* Process one buffer. */ +private int +s_Average_process(stream_state * st, stream_cursor_read * pr, + stream_cursor_write * pw, bool last) +{ + stream_Average_state *const ss = (stream_Average_state *) st; + const byte *p = pr->ptr; + const byte *rlimit = pr->limit; + byte *q = pw->ptr; + byte *wlimit = pw->limit; + int spp = ss->Colors; + int width = ss->Columns; + int xf = ss->XFactor, yf = ss->YFactor; + int x = ss->x, y = ss->y; + uint *sums = ss->sums; + int status = 0; + + top:while (y == yf) { + /* We're copying averaged values to the output. */ + int ncopy = min(ss->sum_size - x, wlimit - q); + + if (ncopy) { + int scale = xf * yf; + + while (--ncopy >= 0) + *++q = (byte) (sums[x++] / scale); + continue; + } + if (x < ss->sum_size) { + status = 1; + goto out; + } + /* Done copying. */ + x = y = 0; + memset(sums, 0, ss->sum_size * sizeof(uint)); + break; + } + while (rlimit - p >= spp) { + uint *bp = sums + x / xf * spp; + int i; + + for (i = spp; --i >= 0;) + *bp++ += *++p; + if (++x == width) { + x = 0; + ++y; + goto top; + } + } + out:pr->ptr = p; + pw->ptr = q; + ss->x = x, ss->y = y; + return status; +} + +const stream_template s_Average_template = +{ + &st_Average_state, s_Average_init, s_Average_process, 4, 4, + s_Average_release +}; diff --git a/gs/src/gdevpsds.h b/gs/src/gdevpsds.h new file mode 100644 index 000000000..70d71ba87 --- /dev/null +++ b/gs/src/gdevpsds.h @@ -0,0 +1,101 @@ +/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: gdevpsds.h */ +/* Image processing stream interface for PostScript and PDF writers */ + +#ifndef gdevpsds_INCLUDED +# define gdevpsds_INCLUDED + +#include "strimpl.h" + +/* Convert between 1/2/4 bits and 8 bits. */ +typedef struct stream_1248_state_s { + stream_state_common; + /* The following are set at initialization time. */ + uint samples_per_row; /* >0 */ + int bits_per_sample; /* 1, 2, 4 */ + /* The following are updated dynamically. */ + uint left; /* # of samples left in current row */ +} stream_1248_state; + +/* Expand N (1, 2, 4) bits to 8. */ +extern const stream_template s_1_8_template; +extern const stream_template s_2_8_template; +extern const stream_template s_4_8_template; + +/* Reduce 8 bits to N (1, 2, 4) */ +extern const stream_template s_8_1_template; +extern const stream_template s_8_2_template; +extern const stream_template s_8_4_template; + +/* Initialize an expansion or reduction stream. */ +#define s_1248_init(ss, Columns, samples_per_pixel)\ + ((ss)->samples_per_row = (Columns) * (samples_per_pixel),\ + (*(ss)->template->init)((stream_state *)(ss))) + +/* Convert (8-bit) CMYK to RGB. */ +typedef struct stream_C2R_state_s { + stream_state_common; + /* The following are set at initialization time. */ + const gs_imager_state *pis; /* for UCR & BG */ +} stream_C2R_state; + +#define private_st_C2R_state() /* in gdevpsds.c */\ + gs_private_st_ptrs1(st_C2R_state, stream_C2R_state, "stream_C2R_state",\ + c2r_enum_ptrs, c2r_reloc_ptrs, pis) +extern const stream_template s_C2R_template; + +#define s_C2R_init(ss, pisv)\ + ((ss)->pis = (pisv), 0) + +/* Downsample, possibly with anti-aliasing. */ +#define stream_Downsample_state_common\ + stream_state_common;\ + /* The client sets the following before initialization. */\ + int Colors;\ + int Columns; /* # of input columns */\ + int XFactor, YFactor;\ + bool AntiAlias;\ + /* The following are updated dynamically. */\ + int x, y /* position within input image */ +#define s_Downsample_set_defaults(ss)\ + ((ss)->AntiAlias = false) +typedef struct stream_Downsample_state_s { + stream_Downsample_state_common; +} stream_Downsample_state; + +/* Subsample */ +typedef struct stream_Subsample_state_s { + stream_Downsample_state_common; +} stream_Subsample_state; +extern const stream_template s_Subsample_template; + +/* Average */ +typedef struct stream_Average_state_s { + stream_Downsample_state_common; + uint sum_size; + uint *sums; /* accumulated sums for average */ +} stream_Average_state; + +#define private_st_Average_state() /* in gdevpsds.c */\ + gs_private_st_ptrs1(st_Average_state, stream_Average_state,\ + "stream_Average_state", avg_enum_ptrs, avg_reloc_ptrs, sums) +extern const stream_template s_Average_template; + +#endif /* gdevpsds_INCLUDED */ diff --git a/gs/src/gdevrops.c b/gs/src/gdevrops.c new file mode 100644 index 000000000..97cc291e7 --- /dev/null +++ b/gs/src/gdevrops.c @@ -0,0 +1,196 @@ +/* Copyright (C) 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: gdevrops.c */ +/* RasterOp source device */ +#include "gx.h" +#include "gserrors.h" +#include "gxdcolor.h" +#include "gxdevice.h" +#include "gdevmrop.h" + +/* GC procedures */ +private_st_device_rop_texture(); +#define rtdev ((gx_device_rop_texture *)vptr) +private ENUM_PTRS_BEGIN(device_rop_texture_enum_ptrs) { + if (index < st_device_color_max_ptrs) { + gs_ptr_type_t ptype = + ENUM_SUPER_ELT(gx_device_rop_texture, st_device_color, texture, 0); + + if (ptype) + return ptype; + return ENUM_OBJ(NULL); /* don't stop early */ + } + ENUM_PREFIX(st_device_forward, st_device_color_max_ptrs); +} ENUM_PTRS_END +private RELOC_PTRS_BEGIN(device_rop_texture_reloc_ptrs) { + RELOC_PREFIX(st_device_forward); + RELOC_SUPER(gx_device_rop_texture, st_device_color, texture); +} RELOC_PTRS_END +#undef rtdev + +/* Device for providing source data for RasterOp. */ +private dev_proc_fill_rectangle(rop_texture_fill_rectangle); +private dev_proc_copy_mono(rop_texture_copy_mono); +private dev_proc_copy_color(rop_texture_copy_color); + +/* The device descriptor. */ +private const gx_device_rop_texture gs_rop_texture_device = { + std_device_std_body(gx_device_rop_texture, 0, "rop source", + 0, 0, 1, 1), + {NULL, /* open_device */ + gx_forward_get_initial_matrix, + NULL, /* default_sync_output */ + NULL, /* output_page */ + NULL, /* close_device */ + gx_forward_map_rgb_color, + gx_forward_map_color_rgb, + rop_texture_fill_rectangle, + NULL, /* tile_rectangle */ + rop_texture_copy_mono, + rop_texture_copy_color, + NULL, /* draw_line */ + NULL, /* get_bits */ + gx_forward_get_params, + gx_forward_put_params, + gx_forward_map_cmyk_color, + gx_forward_get_xfont_procs, + gx_forward_get_xfont_device, + gx_forward_map_rgb_alpha_color, + gx_forward_get_page_device, + NULL, /* get_alpha_bits (no alpha) */ + gx_no_copy_alpha, /* shouldn't be called */ + gx_forward_get_band, + gx_no_copy_rop, /* shouldn't be called */ + NULL, /* fill_path */ + NULL, /* stroke_path */ + NULL, /* fill_mask */ + NULL, /* fill_trapezoid */ + NULL, /* fill_parallelogram */ + NULL, /* fill_triangle */ + NULL, /* draw_thin_line */ + NULL, /* begin_image */ + NULL, /* image_data */ + NULL, /* end_image */ + NULL, /* strip_tile_rectangle */ + NULL, /* strip_copy_rop */ + gx_forward_get_clipping_box, + NULL, /* begin_typed_image */ + NULL, /* get_bits_rectangle */ + gx_forward_map_color_rgb_alpha, + NULL, /* create_compositor */ + gx_forward_get_hardware_params, + NULL /* text_begin */ + }, + 0, /* target */ + lop_default /* log_op */ + /* */ /* texture */ +}; + +/* Create a RasterOp source device. */ +int +gx_alloc_rop_texture_device(gx_device_rop_texture ** prsdev, gs_memory_t * mem, + client_name_t cname) +{ + *prsdev = gs_alloc_struct(mem, gx_device_rop_texture, + &st_device_rop_texture, cname); + return (*prsdev == 0 ? gs_note_error(gs_error_VMerror) : 0); +} + +/* Initialize a RasterOp source device. */ +void +gx_make_rop_texture_device(gx_device_rop_texture * dev, gx_device * target, + gs_logical_operation_t log_op, const gx_device_color * texture) +{ + gx_device_init((gx_device *) dev, + (const gx_device *)&gs_rop_texture_device, + NULL, true); + /* Drawing operations are defaulted, non-drawing are forwarded. */ + gx_device_fill_in_procs((gx_device *) dev); + dev->color_info = target->color_info; + dev->target = target; + dev->log_op = log_op; + dev->texture = *texture; +} + +/* Fill a rectangle */ +private int +rop_texture_fill_rectangle(gx_device * dev, int x, int y, int w, int h, + gx_color_index color) +{ + gx_device_rop_texture *const rtdev = (gx_device_rop_texture *)dev; + gx_rop_source_t source; + + source.sdata = NULL; + source.sourcex = 0; + source.sraster = 0; + source.id = gx_no_bitmap_id; + source.scolors[0] = source.scolors[1] = color; + source.use_scolors = true; + return gx_device_color_fill_rectangle(&rtdev->texture, + x, y, w, h, rtdev->target, + rtdev->log_op, &source); +} + +/* Copy a monochrome rectangle */ +private int +rop_texture_copy_mono(gx_device * dev, + const byte * data, int sourcex, int raster, gx_bitmap_id id, + int x, int y, int w, int h, + gx_color_index color0, gx_color_index color1) +{ + gx_device_rop_texture *const rtdev = (gx_device_rop_texture *)dev; + gx_rop_source_t source; + gs_logical_operation_t lop = rtdev->log_op; + + source.sdata = data; + source.sourcex = sourcex; + source.sraster = raster; + source.id = id; + source.scolors[0] = color0; + source.scolors[1] = color1; + source.use_scolors = true; + /* Adjust the logical operation per transparent colors. */ + if (color0 == gx_no_color_index) + lop = rop3_use_D_when_S_0(lop); + else if (color1 == gx_no_color_index) + lop = rop3_use_D_when_S_1(lop); + return gx_device_color_fill_rectangle(&rtdev->texture, + x, y, w, h, rtdev->target, + lop, &source); +} + +/* Copy a color rectangle */ +private int +rop_texture_copy_color(gx_device * dev, + const byte * data, int sourcex, int raster, gx_bitmap_id id, + int x, int y, int w, int h) +{ + gx_device_rop_texture *const rtdev = (gx_device_rop_texture *)dev; + gx_rop_source_t source; + + source.sdata = data; + source.sourcex = sourcex; + source.sraster = raster; + source.id = id; + source.scolors[0] = source.scolors[1] = gx_no_color_index; + source.use_scolors = false; + return gx_device_color_fill_rectangle(&rtdev->texture, + x, y, w, h, rtdev->target, + rtdev->log_op, &source); +} diff --git a/gs/src/gendev.c b/gs/src/gendev.c new file mode 100644 index 000000000..f5398814d --- /dev/null +++ b/gs/src/gendev.c @@ -0,0 +1,363 @@ +/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: gendev.c */ +/* Generate .dev configuration files */ +#include "stdpre.h" +#include <assert.h> +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> /* for calloc */ +#include <string.h> + +/* + * This program generates .dev configuration files. + * + * Usage: + * gendev [-Z] [-n [<name_prefix> | -]] [-C [<dir> | -]] + * (-d <devfile> | -m <modfile> | -a <modfile>) + * (-<category> | <item>)* + * + * The name_prefix is for device[2], font, init, and iodev resources. + * ****** DOESN'T HANDLE -replace YET ****** + * ****** DOESN'T MERGE device AND device2 ****** + */ + +#define DEFAULT_NAME_PREFIX "gs_" +#define INITIAL_CATEGORY "obj" + +typedef struct config_s { + /* Set once */ + FILE *out; + bool debug; + const char *name_prefix; + const char *file_prefix; + ulong file_id; /* for uniq_last detection */ + /* Updated dynamically */ +#define ITEM_ID_BITS 5 + uint item_id; + bool any_scan_items; + bool in_res_scan; + bool in_category; /* in_category implies in_res_scan */ + const char *current_category; +} config; +static const config init_config = +{ + 0, /* out */ + 0, /* debug */ + DEFAULT_NAME_PREFIX, /* name_prefix */ + "", /* file_prefix */ + 0, /* file_id */ + 0, /* item_id */ + 0, /* any_scan_items */ + 0, /* in_res_scan */ + 0, /* in_category */ + "" /* current_category */ +}; + +static const char *indent_INCLUDED = "\t\t\t\t"; +static const char *indent_RES_SCAN = " "; +static const char *indent_category = "\t"; +static const char *indent_SEEN = "\t "; +static const char *indent_include = " "; +static const char *indent_scan_item = "\t"; +static const char *indent_item = ""; + +/* Forward definitions */ +void add_entry(P4(config *, const char *, const char *, bool)); + +main(int argc, char *argv[]) +{ + config conf; + int i, j; + bool dev, append; + const char *category = INITIAL_CATEGORY; + const char *fnarg; + FILE *out; + long pos; + + conf = init_config; + /* Process command line arguments. */ + for (i = 1; i < argc; i++) { + const char *arg = argv[i]; + + if (*arg != '-') { + fprintf(stderr, "-d|m|a must precede non-switches.\n", arg); + exit(1); + } + switch (arg[1]) { + case 'C': /* change directory, by analogy with make */ + conf.file_prefix = + (argv[i + 1][0] == '-' ? "" : argv[i + 1]); + ++i; + continue; + case 'n': + conf.name_prefix = + (argv[i + 1][0] == '-' ? "" : argv[i + 1]); + ++i; + continue; + case 'a': + dev = false, append = true; + break; + case 'd': + dev = true, append = false; + break; + case 'm': + dev = false, append = false; + break; + case 'Z': + conf.debug = true; + continue; + default: + fprintf(stderr, "Unknown switch %s.\n", argv[i]); + exit(1); + } + break; + } + if (i == argc - 1) { + fprintf(stderr, "No output file name given, last argument is %s.\n", + argv[i]); + exit(1); + } + /* Must be the output file. */ + fnarg = argv[++i]; + { + char fname[100]; + + strcpy(fname, fnarg); + strcat(fname, ".dev"); + out = fopen(fname, (append ? "a" : "w")); + if (out == 0) { + fprintf(stderr, "Can't open %s for output.\n", fname); + exit(1); + } + if (!append) + fprintf(out, + "/*\n * File %s created automatically by gendev.\n */\n", + fname); + } + conf.out = out; + pos = ftell(out); + /* We need a separate _INCLUDED flag for each batch of definitions. */ + fprintf(out, "\n#%sifndef %s_%ld_INCLUDED\n", + indent_INCLUDED, fnarg, pos); + fprintf(out, "#%s define %s_%ld_INCLUDED\n", + indent_INCLUDED, fnarg, pos); + /* Create a "unique" hash for the output file. */ + for (j = 0; fnarg[j] != 0; ++j) + conf.file_id = conf.file_id * 41 + fnarg[j]; + conf.item_id <<= ITEM_ID_BITS; + /* Add the real entries. */ + if (dev) + add_entry(&conf, "dev", fnarg, false); + for (j = i + 1; j < argc; ++j) { + const char *arg = argv[j]; + + if (arg[0] == '-') + category = arg + 1; + else + add_entry(&conf, category, arg, false); + } + if (conf.in_category) + fprintf(out, "#%sendif /* -%s */\n", + indent_category, conf.current_category); + /* Add the scanning entries, if any. */ + if (conf.any_scan_items) { + if (conf.in_res_scan) + fprintf(out, "#%selse /* RES_SCAN */\n", indent_RES_SCAN); + else + fprintf(out, "#%sifdef RES_SCAN\n", indent_RES_SCAN); + conf.in_res_scan = true; + category = INITIAL_CATEGORY; + conf.item_id = 0; + for (j = i + 1; j < argc; ++j) { + const char *arg = argv[j]; + + if (arg[0] == '-') + category = arg + 1; + else + add_entry(&conf, category, arg, true); + } + } + if (conf.in_res_scan) + fprintf(out, "#%sendif /* RES_SCAN */\n", indent_RES_SCAN); + fprintf(out, "#%sendif /* !%s_%ld_INCLUDED */\n", + indent_INCLUDED, fnarg, pos); + fclose(out); + exit(0); +} + +/* Add an entry to the output. */ +typedef enum { + uniq_none = 0, + uniq_first, + uniq_last +} uniq_mode; +void +write_item(config * pconf, const char *str, const char *category, + const char *item, uniq_mode mode) +{ + FILE *out = pconf->out; + char cati[80]; + + if (!pconf->in_res_scan) { + fprintf(out, "#%sifndef RES_SCAN\n", indent_RES_SCAN); + pconf->in_res_scan = true; + } + if (strcmp(pconf->current_category, category)) { + const char *paren = strchr(str, '('); + + if (pconf->in_category) + fprintf(out, "#%sendif /* -%s */\n", + indent_category, pconf->current_category); + fprintf(out, "#%sifdef ", indent_category); + fwrite(str, sizeof(char), paren - str, out); + + fprintf(out, "\n"); + pconf->current_category = category; + pconf->in_category = true; + } + sprintf(cati, "%s_%s_", category, item); + switch (mode) { + case uniq_none: + fprintf(out, "%s%s\n", indent_item, str); + break; + case uniq_first: + fprintf(out, "#%sifndef %sSEEN\n", indent_SEEN, cati); + fprintf(out, "#%s define %sSEEN\n", indent_SEEN, cati); + write_item(pconf, str, category, item, uniq_none); + fprintf(out, "#%sendif\n", indent_SEEN, cati); + break; + case uniq_last: + fprintf(out, "#%sif %sSEEN == %lu\n", indent_SEEN, cati, + pconf->file_id + pconf->item_id++); + write_item(pconf, str, category, item, uniq_none); + fprintf(out, "#%sendif\n", indent_SEEN, cati); + pconf->any_scan_items = true; + break; + } +} +void +write_scan_item(config * pconf, const char *str, const char *category, + const char *item, uniq_mode mode) +{ + FILE *out = pconf->out; + char cati[80]; + + sprintf(cati, "%s_%s_", category, item); + switch (mode) { + case uniq_none: + break; + case uniq_first: + break; + case uniq_last: + fprintf(out, "#%sundef %sSEEN\n", indent_scan_item, cati); + fprintf(out, "#%s define %sSEEN %lu\n", indent_scan_item, cati, + pconf->file_id + pconf->item_id++); + } +} +void +add_entry(config * pconf, const char *category, const char *item, bool scan) +{ + char str[80]; + uniq_mode mode = uniq_first; + + if (pconf->debug && !scan) + printf("Adding %s %s;\n", category, item); + str[0] = 0; + switch (category[0]) { /* just an accelerator */ +#define is_cat(str) !strcmp(category, str) + case 'd': + if (is_cat("dev")) + sprintf(str, "device_(%s%s_device)\n", + pconf->name_prefix, item); + else if (is_cat("dev2")) + sprintf(str, "device2_(%s%s_device)\n", + pconf->name_prefix, item); + break; + case 'e': + if (is_cat("emulator")) + sprintf(str, "emulator_(\"%s\",%d)", + item, strlen(item)); + break; + case 'f': + if (is_cat("font")) + sprintf(str, "font_(\"0.font_%s\",%sf_%s,zf_%s)", + item, pconf->name_prefix, item, item); + break; + case 'i': + if (is_cat("include")) { + int len = strlen(item); + + if (scan) + return; + if (strcmp(pconf->current_category, category)) { + if (pconf->in_category) { + fprintf(pconf->out, "#%sendif /* -%s */\n", + indent_category, pconf->current_category); + pconf->in_category = false; + } + pconf->current_category = category; + } + if (pconf->in_res_scan) { + fprintf(pconf->out, "#%sendif /* RES_SCAN */\n", + indent_RES_SCAN); + pconf->in_res_scan = false; + } + if (len < 5 || strcmp(item + len - 4, ".dev")) + fprintf(pconf->out, "#%sinclude \"%s.dev\"\n", + indent_include, item); + else + fprintf(pconf->out, "#%sinclude \"%s\"\n", + indent_include, item); + return; + } else if (is_cat("init")) + sprintf(str, "init_(%s%s_init)", pconf->name_prefix, item); + else if (is_cat("iodev")) + sprintf(str, "io_device_(%siodev_%s)", pconf->name_prefix, item); + break; + case 'l': + if (is_cat("lib")) { + sprintf(str, "lib_(%s)", item); + mode = uniq_last; + } + break; + case 'o': + if (is_cat("obj")) + sprintf(str, "obj_(%s%s)", pconf->file_prefix, item); + else if (is_cat("oper")) + sprintf(str, "oper_(%s_op_defs)", item); + break; + case 'p': + if (is_cat("ps")) + sprintf(str, "psfile_(\"%s.ps\",%d)", + item, strlen(item) + 3); + break; +#undef is_cat + default: + ; + } + if (str[0] == 0) { + fprintf(stderr, "Unknown category %s.\n", category); + exit(1); + } + if (scan) + write_scan_item(pconf, str, category, item, mode); + else + write_item(pconf, str, category, item, mode); +} diff --git a/gs/src/gp_getnv.c b/gs/src/gp_getnv.c new file mode 100644 index 000000000..84c70374d --- /dev/null +++ b/gs/src/gp_getnv.c @@ -0,0 +1,54 @@ +/* Copyright (C) 1997 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: gp_getnv.c */ +/* Standard implementation of gp_getenv */ +#include "stdio_.h" +#include "string_.h" +#include "gsmemory.h" +#include "gstypes.h" +#include "gp.h" + +/* Import the C getenv function. */ +extern char *getenv(P1(const char *)); + +/* Get the value of an environment variable. See gp.h for details. */ +int +gp_getenv(const char *key, char *ptr, int *plen) +{ + const char *str = getenv(key); + + if (str) { + int len = strlen(str); + + if (len < *plen) { + /* string fits */ + strcpy(ptr, str); + *plen = len + 1; + return 0; + } + /* string doesn't fit */ + *plen = len + 1; + return -1; + } + /* missing key */ + if (*plen > 0) + *ptr = 0; + *plen = 1; + return 1; +} diff --git a/gs/src/gp_wgetv.c b/gs/src/gp_wgetv.c new file mode 100644 index 000000000..23ea0ed9f --- /dev/null +++ b/gs/src/gp_wgetv.c @@ -0,0 +1,130 @@ +/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: gp_wgetv.c */ +/* MS Windows implementation of gp_getenv */ + +#include <windows.h> +#include <stdio.h> +#include <stdlib.h> /* for getenv */ +#include <string.h> +#include "gscdefs.h" /* for gs_product and gs_revision */ + +/* prototypes */ +int gp_getenv_registry(HKEY hkeyroot, const char *key, const char *name, + char *ptr, int *plen); + +/* ------ Environment variables ------ */ + +/* Get the value of an environment variable. See gp.h for details. */ +int +gp_getenv(const char *name, char *ptr, int *plen) +{ + const char *str = getenv(name); + + if (str) { + int len = strlen(str); + + if (len < *plen) { + /* string fits */ + strcpy(ptr, str); + *plen = len + 1; + return 0; + } + /* string doesn't fit */ + *plen = len + 1; + return -1; + } + /* environment variable was not found */ + +#ifdef __WIN32__ + { + /* If using Win32, look in the registry for a value with + * the given name. The registry value will be under the key + * HKEY_CURRENT_USER\Software\Aladdin Ghostscript\N.NN + * or if that fails under the key + * HKEY_LOCAL_MACHINE\Software\Aladdin Ghostscript\N.NN + * where "Aladdin Ghostscript" is actually gs_product + * and N.NN is obtained from gs_revision. + */ + DWORD version = GetVersion(); + + if (!(((HIWORD(version) & 0x8000) != 0) + && ((HIWORD(version) & 0x4000) == 0))) { + /* not Win32s */ + int code; + char key[256]; + sprintf(key, "Software\\%s\\%d.%d", gs_product, + (int)(gs_revision / 100), (int)(gs_revision % 100)); + + code = gp_getenv_registry(HKEY_CURRENT_USER, key, name, ptr, plen); + if ( code <= 0 ) + return code; /* found it */ + + code = gp_getenv_registry(HKEY_LOCAL_MACHINE, key, name, ptr, plen); + if ( code <= 0 ) + return code; /* found it */ + } + } +#endif + + /* nothing found at all */ + + if (*plen > 0) + *ptr = 0; + *plen = 1; + return 1; +} + + +/* + * Get a named registry value. + * Key = hkeyroot\\key, named value = name. + * name, ptr, plen and return values are the same as in gp_getenv(); + */ + +int +gp_getenv_registry(HKEY hkeyroot, const char *key, const char *name, + char *ptr, int *plen) +{ + HKEY hkey; + DWORD cbData, keytype; + BYTE b; + LONG rc; + BYTE *bptr = (BYTE *)ptr; + + if (RegOpenKeyEx(hkeyroot, key, 0, KEY_READ, &hkey) + == ERROR_SUCCESS) { + keytype = REG_SZ; + cbData = *plen; + if (bptr == (char *)NULL) + bptr = &b; /* Registry API won't return ERROR_MORE_DATA */ + /* if ptr is NULL */ + rc = RegQueryValueEx(hkey, (char *)name, 0, &keytype, bptr, &cbData); + RegCloseKey(hkey); + if (rc == ERROR_SUCCESS) { + *plen = cbData; + return 0; /* found environment variable and copied it */ + } else if (rc == ERROR_MORE_DATA) { + /* buffer wasn't large enough */ + *plen = cbData; + return -1; + } + } + return 1; /* not found */ +} diff --git a/gs/src/gpgetenv.h b/gs/src/gpgetenv.h new file mode 100644 index 000000000..231e8bc46 --- /dev/null +++ b/gs/src/gpgetenv.h @@ -0,0 +1,44 @@ +/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: gpgetenv.h */ +/* Interface to platform-specific getenv routine */ + +#ifndef gpgetenv_INCLUDED +# define gpgetenv_INCLUDED + +/* + * Get a value from the environment (getenv). + * + * If the key is missing, set *ptr = 0 (if *plen > 0), set *plen = 1, + * and return 1. + * + * If the key is present and the length len of the value (not counting + * the terminating \0) is less than *plen, copy the value to ptr, set + * *plen = len + 1, and return 0. + * + * If the key is present and len >= *plen, set *plen = len + 1, + * don't store anything at ptr, and return -1. + * + * Note that *plen is the size of the buffer, not the length of the string: + * because of the terminating \0, the maximum string length is 1 less than + * the size of the buffer. + */ +int gp_getenv(P3(const char *key, char *ptr, int *plen)); + +#endif /* gpgetenv_INCLUDED */ diff --git a/gs/src/gsalpha.c b/gs/src/gsalpha.c new file mode 100644 index 000000000..6602d3b95 --- /dev/null +++ b/gs/src/gsalpha.c @@ -0,0 +1,42 @@ +/* Copyright (C) 1997 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: gsalpha.c */ +/* Graphics state alpha value access */ +#include "gx.h" +#include "gsalpha.h" +#include "gxdcolor.h" +#include "gzstate.h" + +/* setalpha */ +int +gs_setalpha(gs_state * pgs, floatp alpha) +{ + pgs->alpha = + (gx_color_value) (alpha < 0 ? 0 : alpha > 1 ? gx_max_color_value : + alpha * gx_max_color_value); + gx_unset_dev_color(pgs); + return 0; +} + +/* currentalpha */ +float +gs_currentalpha(const gs_state * pgs) +{ + return (float)pgs->alpha / gx_max_color_value; +} diff --git a/gs/src/gsalpha.h b/gs/src/gsalpha.h new file mode 100644 index 000000000..0067fd043 --- /dev/null +++ b/gs/src/gsalpha.h @@ -0,0 +1,35 @@ +/* Copyright (C) 1997 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: gsalpha.h */ +/* API for alpha value in graphics state */ + +#ifndef gsalpha_INCLUDED +# define gsalpha_INCLUDED + +/* + * This tiny little file is separate so that it can be included by + * gsstate.c for initializing the alpha value, even in configurations + * that don't have full alpha support. + */ + +/* Set/read alpha value. */ +int gs_setalpha(P2(gs_state *, floatp)); +float gs_currentalpha(P1(const gs_state *)); + +#endif /* gsalpha_INCLUDED */ diff --git a/gs/src/gsalphac.c b/gs/src/gsalphac.c new file mode 100644 index 000000000..00045ad46 --- /dev/null +++ b/gs/src/gsalphac.c @@ -0,0 +1,830 @@ +/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: gsalphac.c */ +/* Alpha-compositing implementation */ +#include "memory_.h" +#include "gx.h" +#include "gserrors.h" +#include "gsalphac.h" +#include "gsiparam.h" /* for gs_image_alpha_t */ +#include "gsutil.h" /* for gs_next_ids */ +#include "gxalpha.h" +#include "gxcomp.h" +#include "gxdevice.h" +#include "gxgetbit.h" +#include "gxlum.h" + +/* ---------------- Internal definitions ---------------- */ + +/* Define the parameters for a compositing operation. */ +typedef struct gs_composite_params_s { + gs_composite_op_t cop; + float delta; /* only for dissolve */ + uint source_alpha; /* only if !psource->alpha */ + uint source_values[4]; /* only if !psource->data */ +} gs_composite_params_t; + +/* Define the source or destination for a compositing operation. */ +#define pixel_row_fields(elt_type)\ + elt_type *data;\ + int bits_per_value; /* 1, 2, 4, 8, 12, 16 */\ + int initial_x;\ + gs_image_alpha_t alpha +typedef struct pixel_row_s { + pixel_row_fields(byte); +} pixel_row_t; +typedef struct const_pixel_row_s { + pixel_row_fields(const byte); +} const_pixel_row_t; + +/* + * Composite two arrays of (premultiplied) pixel values. Legal values of + * values_per_pixel are 1-4, not including alpha. Note that if pdest->alpha + * is "none", the alpha value for all destination pixels will be taken as + * unity, and any operation that could generate alpha values other than + * unity will return an error. "Could generate" means that there are + * possible values of the source and destination alpha values for which the + * result has non-unity alpha: the error check does not scan the actual + * alpha data to test whether there are any actual values that would + * generate a non-unity alpha result. + */ +int composite_values(P5(const pixel_row_t * pdest, + const const_pixel_row_t * psource, + int values_per_pixel, uint num_pixels, + const gs_composite_params_t * pcp)); + +/* ---------------- Alpha-compositing objects ---------------- */ + +/* + * Define which operations can generate non-unity alpha values in 3 of the 4 + * cases of source and destination not having unity alphas. (This is always + * possible in the fourth case, both S & D non-unity, except for CLEAR.) We + * do this with a bit mask indexed by the operation, counting from the LSB. + * The name indicates whether S and/or D has non-unity alphas. + */ +#define alpha_out_notS_notD\ + (1<<composite_Dissolve) +#define _alpha_out_either\ + (alpha_out_notS_notD|(1<<composite_Satop)|(1<<composite_Datop)|\ + (1<<composite_Xor)|(1<<composite_PlusD)|(1<<composite_PlusL)) +#define alpha_out_S_notD\ + (_alpha_out_either|(1<<composite_Copy)|(1<<composite_Sover)|\ + (1<<composite_Din)|(1<<composite_Dout)) +#define alpha_out_notS_D\ + (_alpha_out_either|(1<<composite_Sin)|(1<<composite_Sout)|\ + (1<<composite_Dover)|(1<<composite_Highlight)) + +/* ------ Object definition and creation ------ */ + +/* Define alpha-compositing objects. */ +private composite_create_default_compositor_proc(c_alpha_create_default_compositor); +private composite_equal_proc(c_alpha_equal); +private composite_write_proc(c_alpha_write); +private composite_read_proc(c_alpha_read); +private const gs_composite_type_t gs_composite_alpha_type = +{ + { + c_alpha_create_default_compositor, + c_alpha_equal, + c_alpha_write, + c_alpha_read + } +}; +typedef struct gs_composite_alpha_s { + gs_composite_common; + gs_composite_alpha_params_t params; +} gs_composite_alpha_t; + +gs_private_st_simple(st_composite_alpha, gs_composite_alpha_t, + "gs_composite_alpha_t"); + +/* Create an alpha-compositing object. */ +int +gs_create_composite_alpha(gs_composite_t ** ppcte, + const gs_composite_alpha_params_t * params, gs_memory_t * mem) +{ + gs_composite_alpha_t *pcte; + + rc_alloc_struct_0(pcte, gs_composite_alpha_t, &st_composite_alpha, + mem, return_error(gs_error_VMerror), + "gs_create_composite_alpha"); + pcte->type = &gs_composite_alpha_type; + pcte->id = gs_next_ids(1); + pcte->params = *params; + *ppcte = (gs_composite_t *) pcte; + return 0; +} + +/* ------ Object implementation ------ */ + +#define pacte ((const gs_composite_alpha_t *)pcte) + +private bool +c_alpha_equal(const gs_composite_t * pcte, const gs_composite_t * pcte2) +{ + return (pcte2->type == pcte->type && +#define pacte2 ((const gs_composite_alpha_t *)pcte2) + pacte2->params.op == pacte->params.op && + (pacte->params.op != composite_Dissolve || + pacte2->params.delta == pacte->params.delta)); +#undef pacte2 +} + +private int +c_alpha_write(const gs_composite_t * pcte, byte * data, uint * psize) +{ + uint size = *psize; + uint used; + + if (pacte->params.op == composite_Dissolve) { + used = 1 + sizeof(pacte->params.delta); + if (size < used) { + *psize = used; + return_error(gs_error_rangecheck); + } + memcpy(data + 1, &pacte->params.delta, sizeof(pacte->params.delta)); + } else { + used = 1; + if (size < used) { + *psize = used; + return_error(gs_error_rangecheck); + } + } + *data = (byte) pacte->params.op; + *psize = used; + return 0; +} + +private int +c_alpha_read(gs_composite_t ** ppcte, const byte * data, uint size, + gs_memory_t * mem) +{ + gs_composite_alpha_params_t params; + + if (size < 1 || *data > composite_op_last) + return_error(gs_error_rangecheck); + params.op = *data; + if (params.op == composite_Dissolve) { + if (size != 1 + sizeof(params.delta)) + return_error(gs_error_rangecheck); + memcpy(¶ms.delta, data + 1, sizeof(params.delta)); + } else { + if (size != 1) + return_error(gs_error_rangecheck); + } + return gs_create_composite_alpha(ppcte, ¶ms, mem); +} + +/* ---------------- Alpha-compositing device ---------------- */ + +/* Define the default alpha-compositing device. */ +typedef struct gx_device_composite_alpha_s { + gx_device_forward_common; + gs_composite_alpha_params_t params; +} gx_device_composite_alpha; + +gs_private_st_suffix_add0_final(st_device_composite_alpha, + gx_device_composite_alpha, "gx_device_composite_alpha", + device_c_alpha_enum_ptrs, device_c_alpha_reloc_ptrs, gx_device_finalize, + st_device_forward); +/* The device descriptor. */ +private dev_proc_close_device(dca_close); +private dev_proc_fill_rectangle(dca_fill_rectangle); +private dev_proc_map_rgb_color(dca_map_rgb_color); +private dev_proc_map_color_rgb(dca_map_color_rgb); +private dev_proc_copy_mono(dca_copy_mono); +private dev_proc_copy_color(dca_copy_color); +private dev_proc_map_rgb_alpha_color(dca_map_rgb_alpha_color); +private dev_proc_map_color_rgb_alpha(dca_map_color_rgb_alpha); +private dev_proc_copy_alpha(dca_copy_alpha); +private const gx_device_composite_alpha gs_composite_alpha_device = +{std_device_std_body_open(gx_device_composite_alpha, 0, + "alpha compositor", 0, 0, 1, 1), + {gx_default_open_device, + gx_forward_get_initial_matrix, + gx_default_sync_output, + gx_default_output_page, + dca_close, + dca_map_rgb_color, + dca_map_color_rgb, + dca_fill_rectangle, + gx_default_tile_rectangle, + dca_copy_mono, + dca_copy_color, + gx_default_draw_line, + gx_default_get_bits, + gx_forward_get_params, + gx_forward_put_params, + gx_default_cmyk_map_cmyk_color, /* only called for CMYK */ + gx_forward_get_xfont_procs, + gx_forward_get_xfont_device, + dca_map_rgb_alpha_color, + gx_forward_get_page_device, + gx_forward_get_alpha_bits, + dca_copy_alpha, + gx_forward_get_band, + gx_default_copy_rop, + gx_default_fill_path, + gx_default_stroke_path, + gx_default_fill_mask, + gx_default_fill_trapezoid, + gx_default_fill_parallelogram, + gx_default_fill_triangle, + gx_default_draw_thin_line, + gx_default_begin_image, + gx_default_image_data, + gx_default_end_image, + gx_default_strip_tile_rectangle, + gx_default_strip_copy_rop, + gx_forward_get_clipping_box, + gx_default_begin_typed_image, + gx_forward_get_bits_rectangle, + dca_map_color_rgb_alpha, + gx_no_create_compositor + } +}; + +/* Create an alpha compositor. */ +int +c_alpha_create_default_compositor(const gs_composite_t * pcte, + gx_device ** pcdev, gx_device * dev, const gs_imager_state * pis, + gs_memory_t * mem) +{ + gx_device_composite_alpha *cdev; + + if (pacte->params.op == composite_Copy) { + /* Just use the original device. */ + *pcdev = dev; + return 0; + } + cdev = + gs_alloc_struct_immovable(mem, gx_device_composite_alpha, + &st_device_composite_alpha, + "create default alpha compositor"); + *pcdev = (gx_device *) cdev; + if (cdev == 0) + return_error(gs_error_VMerror); + *(gx_device *) cdev = *dev; + cdev->dname = gs_composite_alpha_device.dname; + cdev->memory = mem; + cdev->stype = &st_device_composite_alpha; + /* + * Set the color_info and depth to be compatible with the target, + * but using standard chunky color storage, including alpha. + ****** CURRENTLY ALWAYS USE 8-BIT COLOR ****** + */ + cdev->color_info.depth = + (dev->color_info.num_components == 4 ? 32 /* CMYK, no alpha */ : + (dev->color_info.num_components + 1) * 8); + cdev->color_info.max_gray = cdev->color_info.max_color = 255; + /* No halftoning will occur, but we fill these in anyway.... */ + cdev->color_info.dither_grays = cdev->color_info.dither_colors = 256; + assign_dev_procs(cdev, &gs_composite_alpha_device); + /* + * We could speed things up a little by tailoring the procedures in + * the device to the specific num_components, but for simplicity, + * we'll defer considering that until there is a demonstrated need. + */ + cdev->target = dev; + cdev->params = pacte->params; + return 0; +} + +/* Close the device and free its storage. */ +private int +dca_close(gx_device * dev) +{ /* + * Finalization will call close again: avoid a recursion loop. + */ + set_dev_proc(dev, close_device, gx_default_close_device); + gs_free_object(dev->memory, dev, "dca_close"); + return 0; +} + +/* ------ (RGB) color mapping ------ */ + +private gx_color_index +dca_map_rgb_color(gx_device * dev, + gx_color_value r, gx_color_value g, gx_color_value b) +{ + return dca_map_rgb_alpha_color(dev, r, g, b, gx_max_color_value); +} +private gx_color_index +dca_map_rgb_alpha_color(gx_device * dev, + gx_color_value red, gx_color_value green, gx_color_value blue, + gx_color_value alpha) +{ /* + * We work exclusively with premultiplied color values, so we + * have to premultiply the color components by alpha here. + */ + byte a = gx_color_value_to_byte(alpha); + +#define premult_(c)\ + (((c) * a + gx_max_color_value / 2) / gx_max_color_value) +#ifdef PREMULTIPLY_TOWARDS_WHITE + byte bias = ~a; + +# define premult(c) (premult_(c) + bias) +#else +# define premult(c) premult_(c) +#endif + gx_color_index color; + + if (dev->color_info.num_components == 1) { + uint lum = + (red * lum_red_weight + green * lum_green_weight + + blue * lum_blue_weight + lum_all_weights / 2) / + lum_all_weights; + + if (a == 0xff) + color = gx_color_value_to_byte(lum); + else /* Premultiplication is necessary. */ + color = premult(lum); + } else { + if (a == 0xff) + color = + ((uint) gx_color_value_to_byte(red) << 16) + + ((uint) gx_color_value_to_byte(green) << 8) + + gx_color_value_to_byte(blue); + else /* Premultiplication is necessary. */ + color = + (premult(red) << 16) + (premult(green) << 8) + premult(blue); + } +#undef premult + return (color << 8) + a; +} +private int +dca_map_color_rgb(gx_device * dev, gx_color_index color, + gx_color_value prgb[3]) +{ + gx_color_value red = gx_color_value_from_byte((byte) (color >> 24)); + byte a = (byte) color; + +#define postdiv_(c)\ + (((c) * 0xff + a / 2) / a) +#ifdef PREMULTIPLY_TOWARDS_WHITE + byte bias = ~a; + +# define postdiv(c) postdiv_(c - bias) +#else +# define postdiv(c) postdiv_(c) +#endif + + if (dev->color_info.num_components == 1) { + if (a != 0xff) { + /* Undo premultiplication. */ + if (a == 0) + red = 0; + else + red = postdiv(red); + } + prgb[0] = prgb[1] = prgb[2] = red; + } else { + gx_color_value + green = gx_color_value_from_byte((byte) (color >> 16)), + blue = gx_color_value_from_byte((byte) (color >> 8)); + + if (a != 0xff) { + /* Undo premultiplication. */ +/****** WHAT TO DO ABOUT BIG LOSS OF PRECISION? ******/ + if (a == 0) + red = green = blue = 0; + else { + red = postdiv(red); + green = postdiv(green); + blue = postdiv(blue); + } + } + prgb[0] = red, prgb[1] = green, prgb[2] = blue; + } +#undef postdiv + return 0; +} +private int +dca_map_color_rgb_alpha(gx_device * dev, gx_color_index color, + gx_color_value prgba[4]) +{ + prgba[3] = gx_color_value_from_byte((byte) color); + return dca_map_color_rgb(dev, color, prgba); +} + +/* ------ Imaging ------ */ + +private int +dca_fill_rectangle(gx_device * dev, int x, int y, int w, int h, + gx_color_index color) +{ /* This is where all the real work gets done! */ + gx_device_composite_alpha *adev = (gx_device_composite_alpha *) dev; + gx_device *target = adev->target; + byte *std_row; + byte *native_row; + gs_int_rect rect; + gs_get_bits_params_t std_params, native_params; + int code = 0; + int yi; + gs_composite_params_t cp; + const_pixel_row_t source; + pixel_row_t dest; + + fit_fill(dev, x, y, w, h); + std_row = gs_alloc_bytes(dev->memory, + (dev->color_info.depth * w + 7) >> 3, + "dca_fill_rectangle(std)"); + native_row = gs_alloc_bytes(dev->memory, + (target->color_info.depth * w + 7) >> 3, + "dca_fill_rectangle(native)"); + if (std_row == 0 || native_row == 0) { + code = gs_note_error(gs_error_VMerror); + goto out; + } + rect.p.x = x, rect.q.x = x + w; + std_params.options = + gb_colors_for_device(dev) | + (GB_ALPHA_LAST | GB_DEPTH_8 | GB_PACKING_CHUNKY | + GB_RETURN_COPY | GB_RETURN_POINTER | GB_ALIGN_ANY | + GB_OFFSET_0 | GB_OFFSET_ANY | GB_RASTER_STANDARD | + GB_RASTER_ANY); + cp.cop = adev->params.op; + if (cp.cop == composite_Dissolve) + cp.delta = adev->params.delta; + { + gx_color_value rgba[4]; + +/****** DOESN'T HANDLE CMYK ******/ + (*dev_proc(dev, map_color_rgb_alpha)) (dev, color, rgba); + cp.source_values[0] = gx_color_value_to_byte(rgba[0]); + cp.source_values[1] = gx_color_value_to_byte(rgba[1]); + cp.source_values[2] = gx_color_value_to_byte(rgba[2]); + cp.source_alpha = gx_color_value_to_byte(rgba[3]); + } + source.data = 0; + source.bits_per_value = 8; + source.alpha = gs_image_alpha_none; + for (yi = y; yi < y + h; ++yi) { + /* Read a row in standard representation. */ + rect.p.y = yi, rect.q.y = yi + 1; + std_params.data[0] = std_row; + code = (*dev_proc(target, get_bits_rectangle)) + (target, &rect, &std_params, NULL); + if (code < 0) + break; + /* Do the work. */ + dest.data = std_params.data[0]; + dest.bits_per_value = 8; + dest.initial_x = + (std_params.options & GB_OFFSET_ANY ? std_params.x_offset : 0); + dest.alpha = + (std_params.options & GB_ALPHA_FIRST ? gs_image_alpha_first : + std_params.options & GB_ALPHA_LAST ? gs_image_alpha_last : + gs_image_alpha_none); + code = composite_values(&dest, &source, + dev->color_info.num_components, w, &cp); + if (code < 0) + break; + if (std_params.data[0] == std_row) { + /* Convert the row back to native representation. */ + /* (Otherwise, we had a direct pointer to device data.) */ + native_params.options = + (GB_COLORS_NATIVE | GB_PACKING_CHUNKY | GB_RETURN_COPY | + GB_OFFSET_0 | GB_RASTER_ALL | GB_ALIGN_STANDARD); + native_params.data[0] = native_row; + code = gx_get_bits_copy(target, 0, w, 1, &native_params, + std_params.options, std_row, + 0 /* raster is irrelevant */ ); + if (code < 0) + break; + code = (*dev_proc(target, copy_color)) + (target, native_row, 0, 0 /* raster is irrelevant */ , + gx_no_bitmap_id, x, yi, w, 1); + if (code < 0) + break; + } + } + out:gs_free_object(dev->memory, native_row, "dca_fill_rectangle(native)"); + gs_free_object(dev->memory, std_row, "dca_fill_rectangle(std)"); + return code; +} + +private int +dca_copy_mono(gx_device * dev, const byte * data, + int dx, int raster, gx_bitmap_id id, int x, int y, int w, int h, + gx_color_index zero, gx_color_index one) +{ +/****** TEMPORARY ******/ + return gx_default_copy_mono(dev, data, dx, raster, id, x, y, w, h, + zero, one); +} + +private int +dca_copy_color(gx_device * dev, const byte * data, + int dx, int raster, gx_bitmap_id id, + int x, int y, int w, int h) +{ +/****** TEMPORARY ******/ + return gx_default_copy_color(dev, data, dx, raster, id, x, y, w, h); +} + +private int +dca_copy_alpha(gx_device * dev, const byte * data, int data_x, + int raster, gx_bitmap_id id, int x, int y, int width, int height, + gx_color_index color, int depth) +{ +/****** TEMPORARY ******/ + return gx_default_copy_alpha(dev, data, data_x, raster, id, x, y, + width, height, color, depth); +} + +/* + * Composite two arrays of (premultiplied) pixel values. + * See gsdpnext.h for the specification. + * + * The current implementation is simple but inefficient. We'll speed it up + * later if necessary. + */ +int +composite_values(const pixel_row_t * pdest, const const_pixel_row_t * psource, + int values_per_pixel, uint num_pixels, const gs_composite_params_t * pcp) +{ + int dest_bpv = pdest->bits_per_value; + int source_bpv = psource->bits_per_value; + + /* + * source_alpha_j gives the source component index for the alpha value, + * if the source has alpha. + */ + int source_alpha_j = + (psource->alpha == gs_image_alpha_last ? values_per_pixel : + psource->alpha == gs_image_alpha_first ? 0 : -1); + + /* dest_alpha_j does the same for the destination. */ + int dest_alpha_j = + (pdest->alpha == gs_image_alpha_last ? values_per_pixel : + pdest->alpha == gs_image_alpha_first ? 0 : -1); + + /* dest_vpp is the number of stored destination values. */ + int dest_vpp = values_per_pixel + (dest_alpha_j >= 0); + + /* source_vpp is the number of stored source values. */ + int source_vpp = values_per_pixel + (source_alpha_j >= 0); + + bool constant_colors = psource->data == 0; + uint highlight_value = (1 << dest_bpv) - 1; + + sample_load_declare(sptr, sbit); + sample_store_declare(dptr, dbit, dbyte); + + { + uint xbit = pdest->initial_x * dest_bpv * dest_vpp; + + sample_store_setup(dbit, xbit & 7, dest_bpv); + dptr = pdest->data + (xbit >> 3); + } + { + uint xbit = psource->initial_x * source_bpv * source_vpp; + + sbit = xbit & 7; + sptr = psource->data + (xbit >> 3); + } + { + uint source_max = (1 << source_bpv) - 1; + uint dest_max = (1 << dest_bpv) - 1; + + /* + * We could save a little work by only setting up source_delta + * and dest_delta if the operation is Dissolve. + */ + float source_delta = pcp->delta * dest_max / source_max; + float dest_delta = 1.0 - pcp->delta; + uint source_alpha = pcp->source_alpha; + uint dest_alpha = dest_max; + +#ifdef PREMULTIPLY_TOWARDS_WHITE + uint source_bias = source_max - source_alpha; + uint dest_bias = 0; + uint result_bias = 0; + +#endif + uint x; + + if (!pdest->alpha) { + uint mask = + (psource->alpha || source_alpha != source_max ? + alpha_out_S_notD : alpha_out_notS_notD); + + if ((mask >> pcp->cop) & 1) { + /* + * The operation could produce non-unity alpha values, but + * the destination can't store them. Return an error. + */ + return_error(gs_error_rangecheck); + } + } + /* Preload the output byte buffer if necessary. */ + sample_store_preload(dbyte, dptr, dbit, dest_bpv); + + for (x = 0; x < num_pixels; ++x) { + int j; + uint result_alpha = dest_alpha; + +/* get_value does not increment the source pointer. */ +#define get_value(v, ptr, bit, bpv, vmax)\ + sample_load16(v, ptr, bit, bpv) + +/* put_value increments the destination pointer. */ +#define put_value(v, ptr, bit, bpv, bbyte)\ + sample_store_next16(v, ptr, bit, bpv, bbyte) + +#define advance(ptr, bit, bpv)\ + sample_next(ptr, bit, bpv) + + /* Get destination alpha value. */ + if (dest_alpha_j >= 0) { + int dabit = dbit + dest_bpv * dest_alpha_j; + const byte *daptr = dptr + (dabit >> 3); + + get_value(dest_alpha, daptr, dabit & 7, dest_bpv, dest_max); +#ifdef PREMULTIPLY_TOWARDS_WHITE + dest_bias = dest_max - dest_alpha; +#endif + } + /* Get source alpha value. */ + if (source_alpha_j >= 0) { + int sabit = sbit; + const byte *saptr = sptr; + + if (source_alpha_j == 0) + advance(sptr, sbit, source_bpv); + else + advance(saptr, sabit, source_bpv * source_alpha_j); + get_value(source_alpha, saptr, sabit, source_bpv, source_max); +#ifdef PREMULTIPLY_TOWARDS_WHITE + source_bias = source_max - source_alpha; +#endif + } +/* + * We are always multiplying a dest value by a source value to compute a + * dest value, so the denominator is always source_max. (Dissolve is the + * one exception.) + */ +#define fr(v, a) ((v) * (a) / source_max) +#define nfr(v, a, maxv) ((v) * (maxv - (a)) / source_max) + + /* + * Iterate over the components of a single pixel. + * j = 0 for alpha, 1 .. values_per_pixel for color + * components, regardless of the actual storage order; + * we arrange things so that sptr/sbit and dptr/dbit + * always point to the right place. + */ + for (j = 0; j <= values_per_pixel; ++j) { + uint dest_v, source_v, result; + +#define set_clamped(r, v)\ + BEGIN if ( (r = (v)) > dest_max ) r = dest_max; END + + if (j == 0) { + source_v = source_alpha; + dest_v = dest_alpha; + } else { + if (constant_colors) + source_v = pcp->source_values[j - 1]; + else { + get_value(source_v, sptr, sbit, source_bpv, source_max); + advance(sptr, sbit, source_bpv); + } + get_value(dest_v, dptr, dbit, dest_bpv, dest_max); +#ifdef PREMULTIPLY_TOWARDS_WHITE + source_v -= source_bias; + dest_v -= dest_bias; +#endif + } + + switch (pcp->cop) { + case composite_Clear: + /* + * The NeXT documentation doesn't say this, but the CLEAR + * operation sets not only alpha but also all the color + * values to 0. + */ + result = 0; + break; + case composite_Copy: + result = source_v; + break; + case composite_PlusD: + /* + * This is the only case where we have to worry about + * clamping a possibly negative result. + */ + result = source_v + dest_v; + result = (result < dest_max ? 0 : result - dest_max); + break; + case composite_PlusL: + set_clamped(result, source_v + dest_v); + break; + case composite_Sover: + set_clamped(result, source_v + nfr(dest_v, source_alpha, source_max)); + break; + case composite_Dover: + set_clamped(result, nfr(source_v, dest_alpha, dest_max) + dest_v); + break; + case composite_Sin: + result = fr(source_v, dest_alpha); + break; + case composite_Din: + result = fr(dest_v, source_alpha); + break; + case composite_Sout: + result = nfr(source_v, dest_alpha, dest_max); + break; + case composite_Dout: + result = nfr(dest_v, source_alpha, source_max); + break; + case composite_Satop: + set_clamped(result, fr(source_v, dest_alpha) + + nfr(dest_v, source_alpha, source_max)); + break; + case composite_Datop: + set_clamped(result, nfr(source_v, dest_alpha, dest_max) + + fr(dest_v, source_alpha)); + break; + case composite_Xor: + set_clamped(result, nfr(source_v, dest_alpha, dest_max) + + nfr(dest_v, source_alpha, source_max)); + break; + case composite_Highlight: + /* + * Bizarre but true: this operation converts white and + * light gray into each other, and leaves all other values + * unchanged. We only implement it properly for gray-scale + * devices. + */ + if (j != 0 && !((source_v ^ highlight_value) & ~1)) + result = source_v ^ 1; + else + result = source_v; + break; + case composite_Dissolve: + /* + * In this case, and only this case, we need to worry about + * source and dest having different bpv values. For the + * moment, we wimp out and do everything in floating point. + */ + result = (uint) (source_v * source_delta + dest_v * dest_delta); + break; + default: + return_error(gs_error_rangecheck); + } + /* + * Store the result. We don't have to worry about + * destinations that don't store alpha, because we don't + * even compute an alpha value in that case. + */ +#ifdef PREMULTIPLY_TOWARDS_WHITE + if (j == 0) { + result_alpha = result; + result_bias = dest_max - result_alpha; + if (dest_alpha_j != 0) + continue; + } else { + result += result_bias; + } +#else + if (j == 0 && dest_alpha_j != 0) { + result_alpha = result; + continue; + } +#endif + put_value(result, dptr, dbit, dest_bpv, dbyte); + } + /* Skip a trailing source alpha value. */ + if (source_alpha_j > 0) + advance(sptr, sbit, source_bpv); + /* Store a trailing destination alpha value. */ + if (dest_alpha_j > 0) + put_value(result_alpha, dptr, dbit, dest_bpv, dbyte); +#undef get_value +#undef put_value +#undef advance + } + /* Store any partial output byte. */ + sample_store_flush(dptr, dbit, dest_bpv, dbyte); + } + return 0; +} diff --git a/gs/src/gsalphac.h b/gs/src/gsalphac.h new file mode 100644 index 000000000..69d70a03e --- /dev/null +++ b/gs/src/gsalphac.h @@ -0,0 +1,65 @@ +/* Copyright (C) 1997 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: gsalphac.h */ +/* Alpha-compositing interface */ + +#ifndef gsalphac_INCLUDED +# define gsalphac_INCLUDED + +#include "gscompt.h" + +/* + * Define the compositing operations. These values must match the ones in + * dpsNeXT.h. + */ +typedef enum { + composite_Clear = 0, + composite_Copy, + composite_Sover, + composite_Sin, + composite_Sout, + composite_Satop, + composite_Dover, + composite_Din, + composite_Dout, + composite_Datop, + composite_Xor, + composite_PlusD, + composite_PlusL, +#define composite_last composite_PlusL + composite_Highlight, /* (only for compositerect) */ +#define compositerect_last composite_Highlight + composite_Dissolve /* (not for PostScript composite operators) */ +#define composite_op_last composite_Dissolve +} gs_composite_op_t; + +/* + * Define parameters for alpha-compositing. + */ +typedef struct gs_composite_alpha_params_s { + gs_composite_op_t op; + float delta; /* only for Dissolve */ +} gs_composite_alpha_params_t; + +/* Create an alpha-compositing object. */ +int gs_create_composite_alpha(P3(gs_composite_t ** ppcte, + const gs_composite_alpha_params_t * params, + gs_memory_t * mem)); + +#endif /* gsalphac_INCLUDED */ diff --git a/gs/src/gscdevn.c b/gs/src/gscdevn.c new file mode 100644 index 000000000..4f3b4f760 --- /dev/null +++ b/gs/src/gscdevn.c @@ -0,0 +1,178 @@ +/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: gscdevn.c */ +/* DeviceN color space and operation definition */ +#include "gx.h" +#include "gserrors.h" +#include "gsrefct.h" +#include "gsmatrix.h" /* for gscolor2.h */ +#include "gxcspace.h" + +gs_private_st_composite(st_color_space_DeviceN, gs_paint_color_space, + "gs_color_space_DeviceN", cs_DeviceN_enum_ptrs, cs_DeviceN_reloc_ptrs); + +/* Define the DeviceN color space type. */ +cs_declare_procs(private, gx_concretize_DeviceN, gx_install_DeviceN, + gx_adjust_cspace_DeviceN); +private cs_proc_num_components(gx_num_components_DeviceN); +private cs_proc_base_space(gx_alt_space_DeviceN); +private cs_proc_restrict_color(gx_restrict_DeviceN); +private cs_proc_concrete_space(gx_concrete_space_DeviceN); +private cs_proc_remap_concrete_color(gx_remap_concrete_DeviceN); +private cs_proc_init_color(gx_init_DeviceN); +const gs_color_space_type gs_color_space_type_DeviceN = { + gs_color_space_index_DeviceN, true, false, + &st_color_space_DeviceN, gx_num_components_DeviceN, + gx_alt_space_DeviceN, + gx_init_DeviceN, gx_restrict_DeviceN, + gx_concrete_space_DeviceN, + gx_concretize_DeviceN, gx_remap_concrete_DeviceN, + gx_default_remap_color, gx_install_DeviceN, + gx_adjust_cspace_DeviceN, gx_no_adjust_color_count +}; + +/* ------ Internal routines ------ */ + +/* Return the number of components of a DeviceN space. */ +private int +gx_num_components_DeviceN(const gs_color_space * pcs) +{ + return pcs->params.device_n.num_components; +} + +/* Return the alternate space of a DeviceN space. */ +private const gs_color_space * +gx_alt_space_DeviceN(const gs_color_space * pcs) +{ + return (const gs_color_space *)&(pcs->params.device_n.alt_space); +} + +/* Initialize a DeviceN color. */ +/****** DOESN'T WORK IF num_components > 4 ******/ +private void +gx_init_DeviceN(gs_client_color * pcc, const gs_color_space * pcs) +{ + int i; + + for (i = 0; i < pcs->params.device_n.num_components; ++i) + pcc->paint.values[i] = 1.0; +} + +/* Force a DeviceN color into legal range. */ +private void +gx_restrict_DeviceN(gs_client_color * pcc, const gs_color_space * pcs) +{ + int i; + + for (i = 0; i < pcs->params.device_n.num_components; ++i) { + floatp value = pcc->paint.values[i]; + + pcc->paint.values[i] = (value <= 0 ? 0 : value >= 1 ? 1 : value); + } +} + +/* Remap a DeviceN color. */ +private const gs_color_space * +gx_concrete_space_DeviceN(const gs_color_space * pcs, + const gs_imager_state * pis) +{ /* We don't support concrete DeviceN spaces yet. */ + const gs_color_space *pacs = + (const gs_color_space *)&pcs->params.device_n.alt_space; + + return cs_concrete_space(pacs, pis); +} + +private int +gx_concretize_DeviceN(const gs_client_color * pc, const gs_color_space * pcs, + frac * pconc, const gs_imager_state * pis) +{ + int code; + gs_client_color cc; + const gs_color_space *pacs = + (const gs_color_space *)&pcs->params.device_n.alt_space; + + /* We always map into the alternate color space. */ + code = (*pcs->params.device_n.tint_transform) + (&pcs->params.device_n, pc->paint.values, &cc.paint.values[0], + pcs->params.device_n.tint_transform_data); + if (code < 0) + return code; + return (*pacs->type->concretize_color) (&cc, pacs, pconc, pis); +} + +private int +gx_remap_concrete_DeviceN(const frac * pconc, + gx_device_color * pdc, const gs_imager_state * pis, gx_device * dev, + gs_color_select_t select) +{ /* We don't support concrete DeviceN colors yet. */ + return_error(gs_error_rangecheck); +} + +/* Install a DeviceN color space. */ +private int +gx_install_DeviceN(gs_color_space * pcs, gs_state * pgs) +{ /* + * Give an error if any of the separation names are duplicated. + * We can't check this any earlier. + */ + const gs_separation_name *names = pcs->params.device_n.names; + uint i, j; + + for (i = 1; i < pcs->params.device_n.num_components; ++i) + for (j = 0; j < i; ++j) + if (names[i] == names[j]) + return_error(gs_error_rangecheck); + return (*pcs->params.device_n.alt_space.type->install_cspace) + ((gs_color_space *) & pcs->params.device_n.alt_space, pgs); +} + +/* Adjust the reference count of a DeviceN color space. */ +private void +gx_adjust_cspace_DeviceN(const gs_color_space * pcs, gs_memory_t * mem, + int delta) +{ + (*pcs->params.device_n.alt_space.type->adjust_cspace_count) + ((const gs_color_space *)&pcs->params.device_n.alt_space, mem, delta); +} + +/* GC procedures */ + +#define pcs ((gs_color_space *)vptr) + +private +ENUM_PTRS_BEGIN(cs_DeviceN_enum_ptrs) +{ + return ENUM_USING(*pcs->params.device_n.alt_space.type->stype, + &pcs->params.device_n.alt_space, + sizeof(pcs->params.device_n.alt_space), index - 2); +} +ENUM_PTR(0, gs_color_space, params.device_n.names); +ENUM_PTR(1, gs_color_space, params.device_n.tint_transform_data); +ENUM_PTRS_END +private RELOC_PTRS_BEGIN(cs_DeviceN_reloc_ptrs) +{ + RELOC_PTR(gs_color_space, params.device_n.names); + RELOC_PTR(gs_color_space, params.device_n.tint_transform_data); + RELOC_USING(*pcs->params.device_n.alt_space.type->stype, + &pcs->params.device_n.alt_space, + sizeof(gs_base_color_space)); +} +RELOC_PTRS_END + +#undef pcs diff --git a/gs/src/gscolor3.c b/gs/src/gscolor3.c new file mode 100644 index 000000000..a38028f55 --- /dev/null +++ b/gs/src/gscolor3.c @@ -0,0 +1,67 @@ +/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: gscolor3.c */ +/* "Operators" for LanguageLevel 3 color facilities */ +#include "gx.h" +#include "gserrors.h" +#include "gscspace.h" /* for gscolor2.h */ +#include "gsmatrix.h" /* for gscolor2.h */ +#include "gscolor2.h" +#include "gscolor3.h" +#include "gspath.h" +#include "gzstate.h" +#include "gxshade.h" + +/* setsmoothness */ +int +gs_setsmoothness(gs_state * pgs, floatp smoothness) +{ + pgs->smoothness = + (smoothness < 0 ? 0 : smoothness > 1 ? 1 : smoothness); + return 0; +} + +/* currentsmoothness */ +float +gs_currentsmoothness(const gs_state * pgs) +{ + return pgs->smoothness; +} + +/* shfill */ +int +gs_shfill(gs_state * pgs, const gs_shading_t * psh) +{ + int code = gs_gsave(pgs); + gs_rect rect; + + if (code < 0) + return code; +/****** DOESN'T USE Background, BBox ******/ + if ((code = gs_setcolorspace(pgs, psh->params.ColorSpace)) < 0 || + (code = gs_clippath(pgs)) < 0 || + (code = gs_pathbbox(pgs, &rect)) < 0 || + (code = gs_shading_fill_rectangle(psh, &rect, + gs_currentdevice(pgs), + (gs_imager_state *) pgs)) < 0 + ) + DO_NOTHING; + gs_grestore(pgs); + return code; +} diff --git a/gs/src/gscolor3.h b/gs/src/gscolor3.h new file mode 100644 index 000000000..f17a994d2 --- /dev/null +++ b/gs/src/gscolor3.h @@ -0,0 +1,35 @@ +/* Copyright (C) 1997 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: gscolor3.h */ +/* Client interface to LanguageLevel 3 color facilities */ + +#ifndef gscolor3_INCLUDED +# define gscolor3_INCLUDED + +/* Smooth shading */ +#ifndef gs_shading_t_DEFINED +# define gs_shading_t_DEFINED +typedef struct gs_shading_s gs_shading_t; + +#endif +int gs_setsmoothness(P2(gs_state *, floatp)); +float gs_currentsmoothness(P1(const gs_state *)); +int gs_shfill(P2(gs_state *, const gs_shading_t *)); + +#endif /* gscolor3_INCLUDED */ diff --git a/gs/src/gscompt.h b/gs/src/gscompt.h new file mode 100644 index 000000000..ed041769b --- /dev/null +++ b/gs/src/gscompt.h @@ -0,0 +1,53 @@ +/* Copyright (C) 1997 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: gscompt.h */ +/* Abstract types for compositing objects */ + +#ifndef gscompt_INCLUDED +# define gscompt_INCLUDED + +/* + * Compositing is the next-to-last step in the rendering pipeline. + * It occurs after color correction but before halftoning (if needed). + * + * gs_composite_t is the abstract superclass for compositing functions such + * as RasterOp functions or alpha-based compositing. Concrete subclasses + * must provide a default implementation (presumably based on + * get_bits_rectangle and copy_color) for devices that provide no optimized + * implementation of their own. + * + * A client that wants to produce composited output asks the target device + * to create an appropriate compositing device based on the target device + * and the gs_composite_t (and possibly other elements of the imager state). + * If the target device doesn't have its own implementation for the + * requested function, format, and state, it passes the buck to the + * gs_composite_t, which may make further tests for special cases before + * creating and returning a compositing device that uses the default + * implementation. + */ +typedef struct gs_composite_s gs_composite_t; + +/* + * To enable fast cache lookup and equality testing, compositing functions, + * like halftones, black generation functions, etc., carry a unique ID (time + * stamp). + */ +gs_id gs_composite_id(P1(const gs_composite_t * pcte)); + +#endif /* gscompt_INCLUDED */ diff --git a/gs/src/gscparam.c b/gs/src/gscparam.c new file mode 100644 index 000000000..b7592f894 --- /dev/null +++ b/gs/src/gscparam.c @@ -0,0 +1,474 @@ +/* Copyright (C) 1995, 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: gscparam.c */ +/* Default implementation of parameter lists */ +#include "memory_.h" +#include "string_.h" +#include "gx.h" +#include "gserrors.h" +#include "gsparam.h" +#include "gsstruct.h" + +/* Forward references */ +typedef union c_param_value_s { + GS_PARAM_VALUE_UNION(gs_c_param_list); +} gs_c_param_value; +/*typedef struct gs_c_param_s gs_c_param; *//* in gsparam.h */ + +/* Define the GC type for a parameter list. */ +private_st_c_param_list(); + +/* Lengths corresponding to various gs_param_type_xxx types */ +const byte gs_param_type_sizes[] = { + GS_PARAM_TYPE_SIZES(sizeof(gs_c_param_list)) +}; + +/* Lengths of *actual* data-containing type pointed to or contained by gs_param_type_xxx's */ +const byte gs_param_type_base_sizes[] = { + GS_PARAM_TYPE_BASE_SIZES(0) +}; + +/* + * Define a parameter list element. We use gs_param_type_any to identify + * elements that have been requested but not yet written. The reading + * procedures must recognize such elements as undefined, and ignore them. + */ +struct gs_c_param_s { + gs_c_param *next; + gs_param_name key; + gs_c_param_value value; + gs_param_type type; + void *alternate_typed_data; +}; + +/* Parameter values aren't really simple, */ +/* but since parameter lists are transient, it doesn't matter. */ +gs_private_st_ptrs2(st_c_param, gs_c_param, "gs_c_param", + c_param_enum_ptrs, c_param_reloc_ptrs, next, alternate_typed_data); + +/* ---------------- Utilities ---------------- */ + +private gs_c_param * +c_param_find(const gs_c_param_list * plist, gs_param_name pkey, bool any) +{ + gs_c_param *pparam = plist->head; + + for (; pparam != 0; pparam = pparam->next) + if (!strcmp(pparam->key, pkey)) + return (pparam->type != gs_param_type_any || any ? pparam : 0); + return 0; +} + +/* ---------------- Writing parameters to a list ---------------- */ + +private param_proc_begin_xmit_collection(c_param_begin_write_collection); +private param_proc_end_xmit_collection(c_param_end_write_collection); +private param_proc_xmit_typed(c_param_write_typed); +private param_proc_request(c_param_request); +private param_proc_requested(c_param_requested); +private const gs_param_list_procs c_write_procs = +{ + c_param_write_typed, + c_param_begin_write_collection, + c_param_end_write_collection, + NULL, /* get_next_key */ + c_param_request, + c_param_requested +}; + +/* Initialize a list for writing. */ +void +gs_c_param_list_write(gs_c_param_list * plist, gs_memory_t * mem) +{ + plist->procs = &c_write_procs; + plist->memory = mem; + plist->head = 0; + plist->count = 0; + plist->any_requested = false; + plist->coll_type = gs_param_collection_dict_any; +} + +/* Release a list. */ +void +gs_c_param_list_release(gs_c_param_list * plist) +{ + gs_c_param *pparam; + + while ((pparam = plist->head) != 0) { + gs_c_param *next = pparam->next; + + switch (pparam->type) { + case gs_param_type_dict: + case gs_param_type_dict_int_keys: + case gs_param_type_array: + gs_c_param_list_release(&pparam->value.d); + break; + case gs_param_type_string: + case gs_param_type_name: + case gs_param_type_int_array: + case gs_param_type_float_array: + case gs_param_type_string_array: + case gs_param_type_name_array: + if (!pparam->value.s.persistent) + gs_free_object(plist->memory, (void *)pparam->value.s.data, + "gs_c_param_list_release data"); + break; + default: + break; + } + gs_free_object(plist->memory, pparam->alternate_typed_data, + "gs_c_param_list_release alternate data"); + gs_free_object(plist->memory, pparam, + "gs_c_param_list_release entry"); + plist->head = next; + plist->count--; + } +} + +/* Add an entry to a list. Doesn't set: value, type, plist->head. */ +private gs_c_param * +c_param_add(gs_c_param_list * plist, gs_param_name pkey) +{ + gs_c_param *pparam = + gs_alloc_struct(plist->memory, gs_c_param, &st_c_param, + "c_param_write entry"); + + if (pparam == 0) + return 0; + pparam->next = plist->head; + pparam->key = pkey; + pparam->alternate_typed_data = 0; + return pparam; +} + +/* Write a dynamically typed parameter to a list. */ +private int +c_param_write(gs_c_param_list * plist, gs_param_name pkey, void *pvalue, + gs_param_type type) +{ + unsigned top_level_sizeof = 0; + unsigned second_level_sizeof = 0; + gs_c_param *pparam = c_param_add(plist, pkey); + + if (pparam == 0) + return_error(gs_error_VMerror); + memcpy(&pparam->value, pvalue, gs_param_type_sizes[(int)type]); + pparam->type = type; + + /* Need deeper copies of data if it's not persistent */ + switch (type) { + gs_param_string const *curr_string; + gs_param_string const *end_string; + + case gs_param_type_string_array: + case gs_param_type_name_array: + /* Determine how much mem needed to hold actual string data */ + curr_string = pparam->value.sa.data; + end_string = curr_string + pparam->value.sa.size; + for (; curr_string < end_string; ++curr_string) + if (!curr_string->persistent) + second_level_sizeof += curr_string->size; + /* fall thru */ + + case gs_param_type_string: + case gs_param_type_name: + case gs_param_type_int_array: + case gs_param_type_float_array: + if (!pparam->value.s.persistent) { /* Allocate & copy object pointed to by array or string */ + byte *top_level_memory; + + top_level_sizeof = + pparam->value.s.size * gs_param_type_base_sizes[type]; + top_level_memory = + gs_alloc_bytes_immovable(plist->memory, + top_level_sizeof + second_level_sizeof, + "c_param_write data"); + if (top_level_memory == 0) { + gs_free_object(plist->memory, pparam, "c_param_write entry"); + return_error(gs_error_VMerror); + } + memcpy(top_level_memory, pparam->value.s.data, top_level_sizeof); + pparam->value.s.data = top_level_memory; + + /* String/name arrays need to copy actual str data */ + + if (second_level_sizeof > 0) { + byte *second_level_memory = + top_level_memory + top_level_sizeof; + + curr_string = pparam->value.sa.data; + end_string = curr_string + pparam->value.sa.size; + for (; curr_string < end_string; ++curr_string) + if (!curr_string->persistent) { + memcpy(second_level_memory, + curr_string->data, curr_string->size); + ((gs_param_string *) curr_string)->data + = second_level_memory; + second_level_memory += curr_string->size; + } + } + } + break; + default: + break; + } + + plist->head = pparam; + plist->count++; + return 0; +} + +/* Individual writing routines. */ +private int +c_param_begin_write_collection(gs_param_list * plist, gs_param_name pkey, + gs_param_dict * pvalue, gs_param_collection_type_t coll_type) +{ + gs_c_param_list *const cplist = (gs_c_param_list *)plist; + gs_c_param_list *dlist = + gs_alloc_struct(cplist->memory, gs_c_param_list, &st_c_param_list, + "c_param_begin_write_collection"); + + if (dlist == 0) + return_error(gs_error_VMerror); + gs_c_param_list_write(dlist, cplist->memory); + dlist->coll_type = coll_type; + pvalue->list = (gs_param_list *) dlist; + return 0; +} +private int +c_param_end_write_collection(gs_param_list * plist, gs_param_name pkey, + gs_param_dict * pvalue) +{ + gs_c_param_list *const cplist = (gs_c_param_list *)plist; + gs_c_param_list *dlist = (gs_c_param_list *) pvalue->list; + + return c_param_write(cplist, pkey, pvalue->list, + (dlist->coll_type == gs_param_collection_dict_int_keys ? + gs_param_type_dict_int_keys : + dlist->coll_type == gs_param_collection_array ? + gs_param_type_array : gs_param_type_dict)); +} +private int +c_param_write_typed(gs_param_list * plist, gs_param_name pkey, + gs_param_typed_value * pvalue) +{ + gs_c_param_list *const cplist = (gs_c_param_list *)plist; + gs_param_collection_type_t coll_type; + + switch (pvalue->type) { + case gs_param_type_dict: + coll_type = gs_param_collection_dict_any; + break; + case gs_param_type_dict_int_keys: + coll_type = gs_param_collection_dict_int_keys; + break; + case gs_param_type_array: + coll_type = gs_param_collection_array; + break; + default: + return c_param_write(cplist, pkey, &pvalue->value, pvalue->type); + } + return c_param_begin_write_collection + (plist, pkey, &pvalue->value.d, coll_type); +} + +/* Other procedures */ + +private int +c_param_request(gs_param_list * plist, gs_param_name pkey) +{ + gs_c_param_list *const cplist = (gs_c_param_list *)plist; + gs_c_param *pparam; + + cplist->any_requested = true; + if (c_param_find(cplist, pkey, true)) + return 0; + pparam = c_param_add(cplist, pkey); + if (pparam == 0) + return_error(gs_error_VMerror); + pparam->type = gs_param_type_any; /* mark as undefined */ + cplist->head = pparam; + return 0; +} + +private int +c_param_requested(const gs_param_list * plist, gs_param_name pkey) +{ + const gs_c_param_list *const cplist = (const gs_c_param_list *)plist; + + return (!cplist->any_requested ? -1 : + c_param_find(cplist, pkey, true) != 0); +} + +/* ---------------- Reading from a list to parameters ---------------- */ + +private param_proc_begin_xmit_collection(c_param_begin_read_collection); +private param_proc_end_xmit_collection(c_param_end_read_collection); +private param_proc_xmit_typed(c_param_read_typed); +private param_proc_next_key(c_param_get_next_key); +private param_proc_get_policy(c_param_read_get_policy); +private param_proc_signal_error(c_param_read_signal_error); +private param_proc_commit(c_param_read_commit); +private const gs_param_list_procs c_read_procs = +{ + c_param_read_typed, + c_param_begin_read_collection, + c_param_end_read_collection, + c_param_get_next_key, + NULL, /* request, N/A */ + NULL, /* requested, N/A */ + c_param_read_get_policy, + c_param_read_signal_error, + c_param_read_commit +}; + +/* Switch a list from writing to reading. */ +void +gs_c_param_list_read(gs_c_param_list * plist) +{ + plist->procs = &c_read_procs; +} + +/* Generic routine for reading a parameter from a list. */ + +private int +c_param_read_typed(gs_param_list * plist, gs_param_name pkey, + gs_param_typed_value * pvalue) +{ + gs_c_param_list *const cplist = (gs_c_param_list *)plist; + gs_param_type req_type = pvalue->type; + gs_c_param *pparam = c_param_find(cplist, pkey, false); + int code; + + if (pparam == 0) + return 1; + pvalue->type = pparam->type; + switch (pvalue->type) { + case gs_param_type_dict: + case gs_param_type_dict_int_keys: + case gs_param_type_array: + gs_c_param_list_read(&pparam->value.d); + pvalue->value.d.list = (gs_param_list *) & pparam->value.d; + pvalue->value.d.size = pparam->value.d.count; + return 0; + default: + break; + } + memcpy(&pvalue->value, &pparam->value, + gs_param_type_sizes[(int)pparam->type]); + code = param_coerce_typed(pvalue, req_type, NULL); +/****** SHOULD LET param_coerce_typed DO THIS ******/ + if (code == gs_error_typecheck && + req_type == gs_param_type_float_array && + pvalue->type == gs_param_type_int_array + ) { + /* Convert int array to float dest */ + gs_param_float_array fa; + int element; + + fa.size = pparam->value.ia.size; + fa.persistent = false; + + if (pparam->alternate_typed_data == 0) { + if ((pparam->alternate_typed_data + = (void *)gs_alloc_bytes_immovable(cplist->memory, + fa.size * sizeof(float), + "gs_c_param_read alternate float array")) == 0) + return_error(gs_error_VMerror); + + for (element = 0; element < fa.size; ++element) + ((float *)(pparam->alternate_typed_data))[element] + = (float)pparam->value.ia.data[element]; + } + fa.data = (float *)pparam->alternate_typed_data; + + pvalue->value.fa = fa; + return 0; + } + return code; +} + +/* Individual reading routines. */ +private int +c_param_begin_read_collection(gs_param_list * plist, gs_param_name pkey, + gs_param_dict * pvalue, gs_param_collection_type_t coll_type) +{ + gs_c_param_list *const cplist = (gs_c_param_list *)plist; + gs_c_param *pparam = c_param_find(cplist, pkey, false); + + if (pparam == 0) + return 1; + switch (pparam->type) { + case gs_param_type_dict: + if (coll_type != gs_param_collection_dict_any) + return_error(gs_error_typecheck); + break; + case gs_param_type_dict_int_keys: + if (coll_type == gs_param_collection_array) + return_error(gs_error_typecheck); + break; + case gs_param_type_array: + break; + default: + return_error(gs_error_typecheck); + } + gs_c_param_list_read(&pparam->value.d); + pvalue->list = (gs_param_list *) & pparam->value.d; + pvalue->size = pparam->value.d.count; + return 0; +} +private int +c_param_end_read_collection(gs_param_list * plist, gs_param_name pkey, + gs_param_dict * pvalue) +{ + return 0; +} + +/* Other procedures */ +private int /* ret 0 ok, 1 if EOF, or -ve err */ +c_param_get_next_key(gs_param_list * plist, gs_param_enumerator_t * penum, + gs_param_key_t * key) +{ + gs_c_param_list *const cplist = (gs_c_param_list *)plist; + gs_c_param *pparam = + (penum->pvoid ? ((gs_c_param *) (penum->pvoid))->next : + cplist->head); + + if (pparam == 0) + return 1; + penum->pvoid = pparam; + key->data = (const byte *)pparam->key; /* was const char * */ + key->size = strlen(pparam->key); + return 0; +} +private int +c_param_read_get_policy(gs_param_list * plist, gs_param_name pkey) +{ + return gs_param_policy_ignore; +} +private int +c_param_read_signal_error(gs_param_list * plist, gs_param_name pkey, int code) +{ + return code; +} +private int +c_param_read_commit(gs_param_list * plist) +{ + return 0; +} diff --git a/gs/src/gscpixel.c b/gs/src/gscpixel.c new file mode 100644 index 000000000..a9c38e88b --- /dev/null +++ b/gs/src/gscpixel.c @@ -0,0 +1,83 @@ +/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: gscpixel.c */ +/* DevicePixel color space and operation definition */ +#include "gx.h" +#include "gserrors.h" +#include "gsrefct.h" +#include "gxcspace.h" +#include "gscpixel.h" +#include "gxdevice.h" + +/* Define the DevicePixel color space type. */ +private cs_proc_restrict_color(gx_restrict_DevicePixel); +private cs_proc_remap_concrete_color(gx_remap_concrete_DevicePixel); +private cs_proc_concretize_color(gx_concretize_DevicePixel); +private const gs_color_space_type gs_color_space_type_DevicePixel = { + gs_color_space_index_DevicePixel, true, false, + &st_base_color_space, gx_num_components_1, + gx_no_base_space, + gx_init_paint_1, gx_restrict_DevicePixel, + gx_same_concrete_space, + gx_concretize_DevicePixel, gx_remap_concrete_DevicePixel, + gx_default_remap_color, gx_no_install_cspace, + gx_no_adjust_cspace_count, gx_no_adjust_color_count +}; + +/* Create a DevicePixel color space. */ +void +gs_cs_init_DevicePixel(gs_color_space * pcs, int depth) +{ + pcs->type = &gs_color_space_type_DevicePixel; + pcs->params.pixel.depth = depth; +} + +/* ------ Internal routines ------ */ + +/* Force a DevicePixel color into legal range. */ +private void +gx_restrict_DevicePixel(gs_client_color * pcc, const gs_color_space * pcs) +{ +/****** NOT ENOUGH BITS IN float OR frac ******/ + floatp pixel = pcc->paint.values[0]; + ulong max_value = (1L << pcs->params.pixel.depth) - 1; + + pcc->paint.values[0] = (pixel < 0 ? 0 : min(pixel, max_value)); +} + + +/* Remap a DevicePixel color. */ + +private int +gx_concretize_DevicePixel(const gs_client_color * pc, const gs_color_space * pcs, + frac * pconc, const gs_imager_state * pis) +{ +/****** NOT ENOUGH BITS IN float OR frac ******/ + pconc[0] = (frac) (ulong) pc->paint.values[0]; + return 0; +} + +private int +gx_remap_concrete_DevicePixel(const frac * pconc, + gx_device_color * pdc, const gs_imager_state * pis, gx_device * dev, + gs_color_select_t select) +{ + color_set_pure(pdc, pconc[0] & ((1 << dev->color_info.depth) - 1)); + return 0; +} diff --git a/gs/src/gscpixel.h b/gs/src/gscpixel.h new file mode 100644 index 000000000..544964982 --- /dev/null +++ b/gs/src/gscpixel.h @@ -0,0 +1,28 @@ +/* Copyright (C) 1997 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: gscpixel.h */ +/* Requires gscspace.h */ + +#ifndef gscpixel_INCLUDED +# define gscpixel_INCLUDED + +/* Create a DevicePixel color space. */ +void gs_cs_init_DevicePixel(P2(gs_color_space * pcs, int depth)); + +#endif /* gscpixel_INCLUDED */ diff --git a/gs/src/gscrdp.c b/gs/src/gscrdp.c new file mode 100644 index 000000000..6d874cc74 --- /dev/null +++ b/gs/src/gscrdp.c @@ -0,0 +1,623 @@ +/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: gscrdp.c */ +/* CIE color rendering dictionary creation */ +#include "math_.h" +#include "memory_.h" +#include "gx.h" +#include "gsdevice.h" +#include "gserrors.h" +#include "gsmatrix.h" /* for gscolor2.h */ +#include "gxcspace.h" +#include "gscolor2.h" /* for gs_set/currentcolorrendering */ +#include "gscrdp.h" +#include "gxarith.h" + +/* Define the CRD type that we use here. */ +#define CRD_TYPE 101 + +/* ---------------- Writing ---------------- */ + +/* Internal procedures for writing parameter values. */ +private void +store_vector3(float *p, const gs_vector3 * pvec) +{ + p[0] = pvec->u, p[1] = pvec->v, p[2] = pvec->w; +} +private int +write_floats(gs_param_list * plist, gs_param_name key, + const float *values, int size, gs_memory_t * mem) +{ + float *p = (float *) + gs_alloc_byte_array(mem, size, sizeof(float), "write_floats"); + gs_param_float_array fa; + + if (p == 0) + return_error(gs_error_VMerror); + memcpy(p, values, size * sizeof(float)); + + fa.data = p; + fa.size = size; + fa.persistent = true; + return param_write_float_array(plist, key, &fa); +} +private int +write_vector3(gs_param_list * plist, gs_param_name key, + const gs_vector3 * pvec, gs_memory_t * mem) +{ + float values[3]; + + store_vector3(values, pvec); + return write_floats(plist, key, values, 3, mem); +} +private int +write_matrix3(gs_param_list * plist, gs_param_name key, + const gs_matrix3 * pmat, gs_memory_t * mem) +{ + float values[9]; + + if (!memcmp(pmat, &Matrix3_default, sizeof(*pmat))) + return 0; + store_vector3(values, &pmat->cu); + store_vector3(values + 3, &pmat->cv); + store_vector3(values + 6, &pmat->cw); + return write_floats(plist, key, values, 9, mem); +} +private int +write_range3(gs_param_list * plist, gs_param_name key, + const gs_range3 * prange, gs_memory_t * mem) +{ + float values[6]; + + if (!memcmp(prange, &Range3_default, sizeof(*prange))) + return 0; + values[0] = prange->ranges[0].rmin, values[1] = prange->ranges[0].rmax; + values[2] = prange->ranges[1].rmin, values[3] = prange->ranges[1].rmax; + values[4] = prange->ranges[2].rmin, values[5] = prange->ranges[2].rmax; + return write_floats(plist, key, values, 6, mem); +} +private int +write_proc3(gs_param_list * plist, gs_param_name key, + const gs_cie_render * pcrd, const gs_cie_render_proc3 * procs, + const gs_range3 * domain, gs_memory_t * mem) +{ + float *values; + uint size = gx_cie_cache_size; + gs_param_float_array fa; + int i; + + if (!memcmp(procs, &Encode_default, sizeof(*procs))) + return 0; + values = (float *)gs_alloc_byte_array(mem, size * 3, sizeof(float), + "write_proc3"); + + if (values == 0) + return_error(gs_error_VMerror); + for (i = 0; i < 3; ++i) { + double base = domain->ranges[i].rmin; + double scale = (domain->ranges[i].rmax - base) / (size - 1); + int j; + + for (j = 0; j < size; ++j) + values[i * size + j] = + (*procs->procs[i]) (j * scale + base, pcrd); + } + fa.data = values; + fa.size = size * 3; + fa.persistent = true; + return param_write_float_array(plist, key, &fa); +} + +/* Write a CRD as a device parameter. */ +int +param_write_cie_render1(gs_param_list * plist, gs_param_name key, + const gs_cie_render * pcrd, gs_memory_t * mem) +{ + gs_param_dict dict; + int code, dcode; + + dict.size = 20; + if ((code = param_begin_write_dict(plist, key, &dict, false)) < 0) + return code; + code = param_put_cie_render1(dict.list, pcrd, mem); + dcode = param_end_write_dict(plist, key, &dict); + return (code < 0 ? code : dcode); +} + +/* Write a CRD directly to a parameter list. */ +int +param_put_cie_render1(gs_param_list * plist, const gs_cie_render * pcrd, + gs_memory_t * mem) +{ + int crd_type = CRD_TYPE; + int code; + + if (pcrd->TransformPQR.proc_name) { + gs_param_string pn, pd; + + param_string_from_string(pn, pcrd->TransformPQR.proc_name); + pn.size++; /* include terminating null */ + pd.data = pcrd->TransformPQR.proc_data.data; + pd.size = pcrd->TransformPQR.proc_data.size; + pd.persistent = true; /****** WRONG ******/ + if ((code = param_write_name(plist, "TransformPQRName", &pn)) < 0 || + (code = param_write_string(plist, "TransformPQRData", &pd)) < 0 + ) + return code; + } + else if (pcrd->TransformPQR.proc != TransformPQR_default.proc) { + /* We have no way to represent the procedure, so return an error. */ + return_error(gs_error_rangecheck); + } + if ((code = param_write_int(plist, "ColorRenderingType", &crd_type)) < 0 || + (code = write_vector3(plist, "WhitePoint", &pcrd->points.WhitePoint, mem)) < 0 + ) + return code; + if (memcmp(&pcrd->points.BlackPoint, &BlackPoint_default, + sizeof(pcrd->points.BlackPoint))) { + if ((code = write_vector3(plist, "BlackPoint", &pcrd->points.BlackPoint, mem)) < 0) + return code; + } + if ((code = write_matrix3(plist, "MatrixPQR", &pcrd->MatrixPQR, mem)) < 0 || + (code = write_range3(plist, "RangePQR", &pcrd->RangePQR, mem)) < 0 || + /* TransformPQR is handled separately */ + (code = write_matrix3(plist, "MatrixLMN", &pcrd->MatrixLMN, mem)) < 0 || + (code = write_proc3(plist, "EncodeLMNValues", pcrd, + &pcrd->EncodeLMN, &pcrd->DomainLMN, mem)) < 0 || + (code = write_range3(plist, "RangeLMN", &pcrd->RangeLMN, mem)) < 0 || + (code = write_matrix3(plist, "MatrixABC", &pcrd->MatrixABC, mem)) < 0 || + (code = write_proc3(plist, "EncodeABCValues", pcrd, + &pcrd->EncodeABC, &pcrd->DomainABC, mem)) < 0 || + (code = write_range3(plist, "RangeABC", &pcrd->RangeABC, mem)) < 0 + ) + return code; + if (pcrd->RenderTable.lookup.table) { + int n = pcrd->RenderTable.lookup.n; + int m = pcrd->RenderTable.lookup.m; + int na = pcrd->RenderTable.lookup.dims[0]; + int *size = (int *) + gs_alloc_byte_array(mem, n + 1, sizeof(int), "RenderTableSize"); + + /* + * In principle, we should use gs_alloc_struct_array with a + * type descriptor for gs_param_string. However, it is widely + * assumed that parameter lists are transient, and don't require + * accurate GC information; so we can get away with allocating + * the string table as bytes. + */ + gs_param_string *table = + (gs_param_string *) + gs_alloc_byte_array(mem, na, sizeof(gs_param_string), + "RenderTableTable"); + gs_param_int_array ia; + + if (size == 0 || table == 0) + code = gs_note_error(gs_error_VMerror); + else { + memcpy(size, pcrd->RenderTable.lookup.dims, sizeof(int) * n); + + size[n] = m; + ia.data = size; + ia.size = n + 1; + ia.persistent = true; + code = param_write_int_array(plist, "RenderTableSize", &ia); + } + if (code >= 0) { + gs_param_string_array sa; + int a; + + for (a = 0; a < na; ++a) + table[a].data = pcrd->RenderTable.lookup.table[a].data, + table[a].size = pcrd->RenderTable.lookup.table[a].size, + table[a].persistent = true; + sa.data = table; + sa.size = na; + sa.persistent = true; + code = param_write_string_array(plist, "RenderTableTable", &sa); + if (code >= 0 && !pcrd->caches.RenderTableT_is_identity) { + /****** WRITE RenderTableTValues LIKE write_proc3 ******/ + uint size = gx_cie_cache_size; + float *values = + (float *)gs_alloc_byte_array(mem, size * m, + sizeof(float), + "write_proc3"); + gs_param_float_array fa; + int i; + + if (values == 0) + return_error(gs_error_VMerror); + for (i = 0; i < m; ++i) { + double scale = 255.0 / (size - 1); + int j; + + for (j = 0; j < size; ++j) + values[i * size + j] = + frac2float((*pcrd->RenderTable.T.procs[i]) + (j * scale, pcrd)); + } + fa.data = values; + fa.size = size * m; + fa.persistent = true; + code = param_write_float_array(plist, "RenderTableTValues", + &fa); + } + } + if (code < 0) { + gs_free_object(mem, table, "RenderTableTable"); + gs_free_object(mem, size, "RenderTableSize"); + return code; + } + } + return code; +} + +/* ---------------- Reading ---------------- */ + +/* Internal procedures for reading parameter values. */ +private void +load_vector3(gs_vector3 * pvec, const float *p) +{ + pvec->u = p[0], pvec->v = p[1], pvec->w = p[2]; +} +private int +read_floats(gs_param_list * plist, gs_param_name key, float *values, int count) +{ + gs_param_float_array fa; + int code = param_read_float_array(plist, key, &fa); + + if (code) + return code; + if (fa.size != count) + return_error(gs_error_rangecheck); + memcpy(values, fa.data, sizeof(float) * count); + + return 0; +} +private int +read_vector3(gs_param_list * plist, gs_param_name key, + gs_vector3 * pvec, const gs_vector3 * dflt) +{ + float values[3]; + int code = read_floats(plist, key, values, 3); + + switch (code) { + case 1: /* not defined */ + if (dflt) + *pvec = *dflt; + break; + case 0: + load_vector3(pvec, values); + default: /* error */ + break; + } + return code; +} +private int +read_matrix3(gs_param_list * plist, gs_param_name key, gs_matrix3 * pmat) +{ + float values[9]; + int code = read_floats(plist, key, values, 9); + + switch (code) { + case 1: /* not defined */ + *pmat = Matrix3_default; + break; + case 0: + load_vector3(&pmat->cu, values); + load_vector3(&pmat->cv, values + 3); + load_vector3(&pmat->cw, values + 6); + default: /* error */ + break; + } + return code; +} +private int +read_range3(gs_param_list * plist, gs_param_name key, gs_range3 * prange) +{ + float values[6]; + int code = read_floats(plist, key, values, 6); + + switch (code) { + case 1: /* not defined */ + *prange = Range3_default; + break; + case 0: + prange->ranges[0].rmin = values[0]; + prange->ranges[0].rmax = values[1]; + prange->ranges[1].rmin = values[2]; + prange->ranges[1].rmax = values[3]; + prange->ranges[2].rmin = values[4]; + prange->ranges[2].rmax = values[5]; + default: /* error */ + break; + } + return code; +} +private int +read_proc3(gs_param_list * plist, gs_param_name key, + float values[gx_cie_cache_size * 3]) +{ + return read_floats(plist, key, values, gx_cie_cache_size * 3); +} + +/* Read a CRD from a device parameter. */ +int +gs_cie_render1_param_initialize(gs_cie_render * pcrd, gs_param_list * plist, + gs_param_name key, gx_device * dev) +{ + gs_param_dict dict; + int code = param_begin_read_dict(plist, key, &dict, false); + int dcode; + + if (code < 0) + return code; + code = param_get_cie_render1(pcrd, dict.list, dev); + dcode = param_end_read_dict(plist, key, &dict); + if (code < 0) + return code; + if (dcode < 0) + return dcode; + gs_cie_render_init(pcrd); + gs_cie_render_sample(pcrd); + return gs_cie_render_complete(pcrd); +} + +/* Define the structure for passing Encode values as "client data". */ +typedef struct encode_data_s { + float lmn[gx_cie_cache_size * 3]; /* EncodeLMN */ + float abc[gx_cie_cache_size * 3]; /* EncodeABC */ + float t[gx_cie_cache_size * 4]; /* RenderTable.T */ +} encode_data_t; + +/* Define procedures that retrieve the Encode values read from the list. */ +private float +encode_from_data(floatp v, const float values[gx_cie_cache_size], + const gs_range * range) +{ + return (v <= range->rmin ? values[0] : + v >= range->rmax ? values[gx_cie_cache_size - 1] : + values[(int)((v - range->rmin) / (range->rmax - range->rmin) * + (gx_cie_cache_size - 1) + 0.5)]); +} +/* + * The repetitive boilerplate in the next 10 procedures really sticks in + * my craw, but I've got a mandate not to use macros.... + */ +private float +encode_lmn_0_from_data(floatp v, const gs_cie_render * pcrd) +{ + const encode_data_t *data = pcrd->client_data; + + return encode_from_data(v, &data->lmn[0], + &pcrd->DomainLMN.ranges[0]); +} +private float +encode_lmn_1_from_data(floatp v, const gs_cie_render * pcrd) +{ + const encode_data_t *data = pcrd->client_data; + + return encode_from_data(v, &data->lmn[gx_cie_cache_size], + &pcrd->DomainLMN.ranges[1]); +} +private float +encode_lmn_2_from_data(floatp v, const gs_cie_render * pcrd) +{ + const encode_data_t *data = pcrd->client_data; + + return encode_from_data(v, &data->lmn[gx_cie_cache_size * 2], + &pcrd->DomainLMN.ranges[2]); +} +private float +encode_abc_0_from_data(floatp v, const gs_cie_render * pcrd) +{ + const encode_data_t *data = pcrd->client_data; + + return encode_from_data(v, &data->abc[0], + &pcrd->DomainABC.ranges[0]); +} +private float +encode_abc_1_from_data(floatp v, const gs_cie_render * pcrd) +{ + const encode_data_t *data = pcrd->client_data; + + return encode_from_data(v, &data->abc[gx_cie_cache_size], + &pcrd->DomainABC.ranges[1]); +} +private float +encode_abc_2_from_data(floatp v, const gs_cie_render * pcrd) +{ + const encode_data_t *data = pcrd->client_data; + + return encode_from_data(v, &data->abc[gx_cie_cache_size * 2], + &pcrd->DomainABC.ranges[2]); +} +private frac +render_table_t_0_from_data(byte v, const gs_cie_render * pcrd) +{ + const encode_data_t *data = pcrd->client_data; + + return float2frac(encode_from_data(v / 255.0, + &data->t[0], + &Range3_default.ranges[0])); +} +private frac +render_table_t_1_from_data(byte v, const gs_cie_render * pcrd) +{ + const encode_data_t *data = pcrd->client_data; + + return float2frac(encode_from_data(v / 255.0, + &data->t[gx_cie_cache_size], + &Range3_default.ranges[0])); +} +private frac +render_table_t_2_from_data(byte v, const gs_cie_render * pcrd) +{ + const encode_data_t *data = pcrd->client_data; + + return float2frac(encode_from_data(v / 255.0, + &data->t[gx_cie_cache_size * 2], + &Range3_default.ranges[0])); +} +private frac +render_table_t_3_from_data(byte v, const gs_cie_render * pcrd) +{ + const encode_data_t *data = pcrd->client_data; + + return float2frac(encode_from_data(v / 255.0, + &data->t[gx_cie_cache_size * 3], + &Range3_default.ranges[0])); +} +private const gs_cie_render_proc3 EncodeLMN_from_data = { + {encode_lmn_0_from_data, encode_lmn_1_from_data, encode_lmn_2_from_data} +}; +private const gs_cie_render_proc3 EncodeABC_from_data = { + {encode_abc_0_from_data, encode_abc_1_from_data, encode_abc_2_from_data} +}; +private const gs_cie_render_table_procs RenderTableT_from_data = { + {render_table_t_0_from_data, render_table_t_1_from_data, + render_table_t_2_from_data, render_table_t_3_from_data + } +}; + +/* Read a CRD directly from a parameter list. */ +int +param_get_cie_render1(gs_cie_render * pcrd, gs_param_list * plist, + gx_device * dev) +{ + encode_data_t data; + gs_param_int_array rt_size; + int crd_type; + int code, code_lmn, code_abc, code_rt, code_t; + gs_param_string pname, pdata; + + if ((code = param_read_int(plist, "ColorRenderingType", &crd_type)) < 0 || + crd_type != CRD_TYPE || + (code = read_vector3(plist, "WhitePoint", &pcrd->points.WhitePoint, + NULL)) < 0 || + (code = read_vector3(plist, "BlackPoint", &pcrd->points.BlackPoint, + &BlackPoint_default)) < 0 || + (code = read_matrix3(plist, "MatrixPQR", &pcrd->MatrixPQR)) < 0 || + (code = read_range3(plist, "RangePQR", &pcrd->RangePQR)) < 0 || + /* TransformPQR is handled specially below. */ + (code = read_matrix3(plist, "MatrixLMN", &pcrd->MatrixLMN)) < 0 || + (code_lmn = code = + read_proc3(plist, "EncodeLMNValues", data.lmn)) < 0 || + (code = read_range3(plist, "RangeLMN", &pcrd->RangeLMN)) < 0 || + (code = read_matrix3(plist, "MatrixABC", &pcrd->MatrixABC)) < 0 || + (code_abc = code = + read_proc3(plist, "EncodeABCValues", data.abc)) < 0 || + (code = read_range3(plist, "RangeABC", &pcrd->RangeABC)) < 0 + ) + return code; + /* Handle the sampled functions. */ + switch (code = param_read_string(plist, "TransformPQRName", &pname)) { + default: /* error */ + return code; + case 1: /* missing */ + pcrd->TransformPQR = TransformPQR_default; + break; + case 0: /* specified */ + /* The procedure name must be null-terminated: */ + /* see param_put_cie_render1 above. */ + if (pname.size < 1 || pname.data[pname.size - 1] != 0) + return_error(gs_error_rangecheck); + pcrd->TransformPQR.proc = TransformPQR_lookup_proc_name; + pcrd->TransformPQR.proc_name = (char *)pname.data; + switch (code = param_read_string(plist, "TransformPQRData", &pdata)) { + default: /* error */ + return code; + case 1: /* missing */ + pcrd->TransformPQR.proc_data.data = 0; + pcrd->TransformPQR.proc_data.size = 0; + break; + case 0: + pcrd->TransformPQR.proc_data.data = pdata.data; + pcrd->TransformPQR.proc_data.size = pdata.size; + } + pcrd->TransformPQR.driver_name = gs_devicename(dev); + break; + } + pcrd->client_data = &data; + if (code_lmn > 0) + pcrd->EncodeLMN = Encode_default; + else + pcrd->EncodeLMN = EncodeLMN_from_data; + if (code_abc > 0) + pcrd->EncodeABC = Encode_default; + else + pcrd->EncodeABC = EncodeABC_from_data; + code_rt = code = param_read_int_array(plist, "RenderTableSize", &rt_size); + if (code == 1) { + if (pcrd->RenderTable.lookup.table) { + gs_free_object(pcrd->rc.memory, + (void *)pcrd->RenderTable.lookup.table, /* break const */ + "param_get_cie_render1(RenderTable)"); + pcrd->RenderTable.lookup.table = 0; + } + pcrd->RenderTable.T = RenderTableT_default; + code_t = 1; + } else if (code < 0) + return code; + else if (rt_size.size != 4) + return_error(gs_error_rangecheck); + else { + gs_param_string_array rt_values; + gs_const_string *table; + int n, m, j; + + code = param_read_string_array(plist, "RenderTableTable", &rt_values); + if (code < 0) + return code; + else if (code > 0 || + rt_values.size != rt_size.data[3] * + rt_size.data[1] * rt_size.data[2]) + return_error(gs_error_rangecheck); + pcrd->RenderTable.lookup.n = n = rt_size.size - 1; + pcrd->RenderTable.lookup.m = m = rt_size.data[n]; + if (n > 4 || m > 4) + return_error(gs_error_rangecheck); + memcpy(pcrd->RenderTable.lookup.dims, rt_size.data, n * sizeof(int)); + /****** ALLOCATE table = RenderTable.lookup.table ******/ + for (j = 0; j < pcrd->RenderTable.lookup.dims[0]; ++j) { + table[j].data = rt_values.data[j].data; + table[j].size = rt_values.data[j].size; + } + pcrd->RenderTable.lookup.table = table; + pcrd->RenderTable.T = RenderTableT_from_data; + code_t = code = read_floats(plist, "RenderTableTValues", data.t, + gx_cie_cache_size * m); + if (code > 0) + pcrd->RenderTable.T = RenderTableT_default; + else if (code == 0) + pcrd->RenderTable.T = RenderTableT_from_data; + } + if ((code = gs_cie_render_init(pcrd)) >= 0 && + (code = gs_cie_render_sample(pcrd)) >= 0 + ) + code = gs_cie_render_complete(pcrd); + /* Clean up before exiting. */ + pcrd->client_data = 0; + if (code_lmn == 0) + pcrd->EncodeLMN = EncodeLMN_from_cache; + if (code_abc == 0) + pcrd->EncodeABC = EncodeABC_from_cache; + if (code_t == 0) + pcrd->RenderTable.T = RenderTableT_from_cache; + return code; +} diff --git a/gs/src/gscrdp.h b/gs/src/gscrdp.h new file mode 100644 index 000000000..42ce950ad --- /dev/null +++ b/gs/src/gscrdp.h @@ -0,0 +1,98 @@ +/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: gscrdp.h */ +/* Interface for device-specified CRDs */ + +#ifndef gscrdp_INCLUDED +# define gscrdp_INCLUDED + +#include "gscie.h" +#include "gsparam.h" + +/* + * A driver can provide any number of its own CRDs through (read-only) + * device parameters whose values are slightly modified PostScript-style + * dictionaries. The driver doesn't need to concern itself with how the + * parameters are encoded: it simply constructs a CRD and calls + * param_write_cie_render1. + */ +int param_write_cie_render1(P4(gs_param_list * plist, gs_param_name key, + const gs_cie_render * pcrd, + gs_memory_t * mem)); + +/* + * For internal use, we also provide an API that writes the CRD directly + * into a parameter list, rather than as a named parameter in a larger + * list. + */ +int param_put_cie_render1(P3(gs_param_list * plist, const gs_cie_render * pcrd, + gs_memory_t * mem)); + +/* + * Client code that wants to initialize a CRD from a device parameter + * uses the following complementary procedure. The customary way to + * use this is: + + gs_c_param_list list; + ... + gs_c_param_list_write(&list, mem); + gs_c_param_request(&list, "ParamName"); + code = gs_getdeviceparams(dev, &list); + << error if code < 0 >> + gs_c_param_list_read(&list); + code = gs_cie_render1_param_initialize(pcrd, &list, "ParamName", dev); + gs_c_param_list_release(&list); + << error if code < 0 >> + + * where "ParamName" is the parameter name, e.g., "CRDDefault". + */ +int gs_cie_render1_param_initialize(P4(gs_cie_render * pcrd, + gs_param_list * plist, + gs_param_name key, + gx_device * dev)); + +/* + * Again, we provide an internal procedure that doesn't involve a + * parameter name. + */ +int param_get_cie_render1(P3(gs_cie_render * pcrd, + gs_param_list * plist, + gx_device * dev)); + +/* + * The actual representation of the CRD is a slightly modified PostScript + * ColorRenderingType 1 dictionary. THE FOLLOWING IS SUBJECT TO CHANGE + * WITHOUT NOTICE. Specifically, the following keys are different: + * ColorRenderingType = 101 + * (Instead of TransformPQR = [T1 T2 T3]:) + * TransformPQRName = procedure name (a name) + * TransformPQRData = procedure data (a string) + * (Instead of EncodeLMN/ABC = [E1 E2 E3]:) + * EncodeLMN/ABCValues = [V1,1 V1,2 ... V3,N], where Vi,j is the + * j'th sampled value of the i'th encoding array, mapped linearly + * to the corresponding domain (see gscie.h) + * (Instead of RenderTable = [NA NB NC table m T1 ... Tm]:) + * RenderTableSize = [NA NB NC m] + * RenderTableTable = table (an array of strings) + * RenderTableTValues = [V1,1 V1,2 ... Vm,N] (see above) + * The PostScript setcolorrendering operator selects the correct operator + * according to the ColorRenderingType key. + */ + +#endif /* gscrdp_INCLUDED */ diff --git a/gs/src/gsdpnext.h b/gs/src/gsdpnext.h new file mode 100644 index 000000000..c2c00d8a6 --- /dev/null +++ b/gs/src/gsdpnext.h @@ -0,0 +1,28 @@ +/* Copyright (C) 1997 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: gsdpnext.h */ +/* API for NeXT DPS facilities */ + +#ifndef gsdpnext_INCLUDED +# define gsdpnext_INCLUDED + +#include "gsalpha.h" +#include "gsalphac.h" + +#endif /* gsdpnext_INCLUDED */ diff --git a/gs/src/gsfunc3.c b/gs/src/gsfunc3.c new file mode 100644 index 000000000..b9463cdfd --- /dev/null +++ b/gs/src/gsfunc3.c @@ -0,0 +1,355 @@ +/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: gsfunc3.c */ +/* Implementation of LL3 Functions */ +#include "math_.h" +#include "gx.h" +#include "gserrors.h" +#include "gsfunc3.h" +#include "gxfunc.h" + +/* ---------------- Exponential Interpolation functions ---------------- */ + +typedef struct gs_function_ElIn_s { + gs_function_head_t head; + gs_function_ElIn_params_t params; +} gs_function_ElIn_t; + +private_st_function_ElIn(); + +/* Evaluate an Exponential Interpolation function. */ +private int +fn_ElIn_evaluate(const gs_function_t * pfn_common, const float *in, float *out) +{ + const gs_function_ElIn_t *const pfn = + (const gs_function_ElIn_t *)pfn_common; + double arg = in[0], raised; + int i; + + if (arg < pfn->params.Domain[0]) + arg = pfn->params.Domain[0]; + else if (arg > pfn->params.Domain[1]) + arg = pfn->params.Domain[1]; + raised = pow(arg, pfn->params.N); + for (i = 0; i < pfn->params.n; ++i) { + float v0 = (pfn->params.C0 == 0 ? 0.0 : pfn->params.C0[i]); + float v1 = (pfn->params.C1 == 0 ? 1.0 : pfn->params.C1[i]); + double value = v0 + raised * (v1 - v0); + + if (pfn->params.Range) { + float r0 = pfn->params.Range[2 * i], + r1 = pfn->params.Range[2 * i + 1]; + + if (value < r0) + value = r0; + else if (value > r1) + value = r1; + } + out[i] = value; + } + return 0; +} + +/* Test whether an Exponential function is monotonic. (They always are.) */ +private int +fn_ElIn_is_monotonic(const gs_function_t * pfn_common, + const float *lower, const float *upper, bool must_know) +{ + const gs_function_ElIn_t *const pfn = + (const gs_function_ElIn_t *)pfn_common; + + if (lower[0] > pfn->params.Domain[1] || + upper[0] < pfn->params.Domain[0] + ) + return_error(gs_error_rangecheck); + return 1; +} + +/* Free the parameters of an Exponential Interpolation function. */ +void +gs_function_ElIn_free_params(gs_function_ElIn_params_t * params, + gs_memory_t * mem) +{ + gs_free_object(mem, (void *)params->C1, "C1"); /* break const */ + gs_free_object(mem, (void *)params->C0, "C0"); /* break const */ + fn_common_free_params((gs_function_params_t *) params, mem); +} + +/* Allocate and initialize an Exponential Interpolation function. */ +int +gs_function_ElIn_init(gs_function_t ** ppfn, + const gs_function_ElIn_params_t * params, + gs_memory_t * mem) +{ + static const gs_function_head_t function_ElIn_head = + { + function_type_ExponentialInterpolation, + (fn_evaluate_proc_t) fn_ElIn_evaluate, + (fn_is_monotonic_proc_t) fn_ElIn_is_monotonic, + (fn_free_params_proc_t) gs_function_ElIn_free_params, + fn_common_free + }; + int code; + + *ppfn = 0; /* in case of error */ + code = fn_check_mnDR((const gs_function_params_t *)params, 1, params->n); + if (code < 0) + return code; + if ((params->C0 == 0 || params->C1 == 0) && params->n != 1) + return_error(gs_error_rangecheck); + if (params->N != floor(params->N)) { + /* Non-integral exponent, all inputs must be non-negative. */ + if (params->Domain[0] < 0) + return_error(gs_error_rangecheck); + } + if (params->N < 0) { + /* Negative exponent, input must not be zero. */ + if (params->Domain[0] <= 0 && params->Domain[1] >= 0) + return_error(gs_error_rangecheck); + } { + gs_function_ElIn_t *pfn = + gs_alloc_struct(mem, gs_function_ElIn_t, &st_function_ElIn, + "gs_function_ElIn_init"); + + if (pfn == 0) + return_error(gs_error_VMerror); + pfn->params = *params; + pfn->params.m = 1; + pfn->head = function_ElIn_head; + *ppfn = (gs_function_t *) pfn; + } + return 0; +} + +/* ---------------- 1-Input Stitching functions ---------------- */ + +typedef struct gs_function_1ItSg_s { + gs_function_head_t head; + gs_function_1ItSg_params_t params; +} gs_function_1ItSg_t; + +private_st_function_1ItSg(); + +/* Evaluate a 1-Input Stitching function. */ +private int +fn_1ItSg_evaluate(const gs_function_t * pfn_common, const float *in, float *out) +{ + const gs_function_1ItSg_t *const pfn = + (const gs_function_1ItSg_t *)pfn_common; + float arg = in[0], b0, b1, e0, encoded; + int k = pfn->params.k; + int i; + + if (arg < pfn->params.Domain[0]) { + arg = pfn->params.Domain[0]; + i = 0; + } else if (arg > pfn->params.Domain[1]) { + arg = pfn->params.Domain[1]; + i = k - 1; + } else { + for (i = 0; i < k - 1; ++i) + if (arg <= pfn->params.Bounds[i]) + break; + } + b0 = (i == 0 ? pfn->params.Domain[0] : pfn->params.Bounds[i - 1]); + b1 = (i == k - 1 ? pfn->params.Domain[1] : pfn->params.Bounds[i]); + e0 = pfn->params.Encode[2 * i]; + encoded = + (arg - b0) * (pfn->params.Encode[2 * i + 1] - e0) / (b1 - b0) + e0; + return gs_function_evaluate(pfn->params.Functions[i], &encoded, out); +} + +/* Test whether a 1-Input Stitching function is monotonic. */ +private int +fn_1ItSg_is_monotonic(const gs_function_t * pfn_common, + const float *lower, const float *upper, bool must_know) +{ + const gs_function_1ItSg_t *const pfn = + (const gs_function_1ItSg_t *)pfn_common; + + if (lower[0] > pfn->params.Domain[1] || + upper[0] < pfn->params.Domain[0] + ) + return_error(gs_error_rangecheck); +/****** NYI ******/ + return gs_error_undefined; +} + +/* Free the parameters of a 1-Input Stitching function. */ +void +gs_function_1ItSg_free_params(gs_function_1ItSg_params_t * params, + gs_memory_t * mem) +{ + gs_free_object(mem, (void *)params->Encode, "Encode"); /* break const */ + gs_free_object(mem, (void *)params->Bounds, "Bounds"); /* break const */ + fn_free_functions((gs_function_t **) params->Functions, /* break const */ + params->k, mem); + fn_common_free_params((gs_function_params_t *) params, mem); +} + +/* Allocate and initialize a 1-Input Stitching function. */ +int +gs_function_1ItSg_init(gs_function_t ** ppfn, + const gs_function_1ItSg_params_t * params, gs_memory_t * mem) +{ + static const gs_function_head_t function_1ItSg_head = + { + function_type_1InputStitching, + (fn_evaluate_proc_t) fn_1ItSg_evaluate, + (fn_is_monotonic_proc_t) fn_1ItSg_is_monotonic, + (fn_free_params_proc_t) gs_function_1ItSg_free_params, + fn_common_free + }; + int n = (params->Range == 0 ? 0 : params->n); + float prev = params->Domain[0]; + int i; + + *ppfn = 0; /* in case of error */ + for (i = 0; i < params->k; ++i) { + const gs_function_t *psubfn = params->Functions[i]; + + if (psubfn->params.m != 1) + return_error(gs_error_rangecheck); + if (n == 0) + n = psubfn->params.n; + else if (psubfn->params.n != n) + return_error(gs_error_rangecheck); + /* There are only k - 1 Bounds, not k. */ + if (i < params->k - 1) { + if (params->Bounds[i] <= prev) + return_error(gs_error_rangecheck); + prev = params->Bounds[i]; + } + } + if (params->Domain[1] < prev) + return_error(gs_error_rangecheck); + fn_check_mnDR((const gs_function_params_t *)params, 1, n); + { + gs_function_1ItSg_t *pfn = + gs_alloc_struct(mem, gs_function_1ItSg_t, &st_function_1ItSg, + "gs_function_1ItSg_init"); + + if (pfn == 0) + return_error(gs_error_VMerror); + pfn->params = *params; + pfn->params.m = 1; + pfn->params.n = n; + pfn->head = function_1ItSg_head; + *ppfn = (gs_function_t *) pfn; + } + return 0; +} + +/* ---------------- Arrayed Output functions ---------------- */ + +typedef struct gs_function_AdOt_s { + gs_function_head_t head; + gs_function_AdOt_params_t params; +} gs_function_AdOt_t; + +private_st_function_AdOt(); + +/* Evaluate an Arrayed Output function. */ +private int +fn_AdOt_evaluate(const gs_function_t * pfn_common, const float *in, float *out) +{ + const gs_function_AdOt_t *const pfn = + (const gs_function_AdOt_t *)pfn_common; + int i; + + for (i = 0; i < pfn->params.n; ++i) { + int code = + gs_function_evaluate(pfn->params.Functions[i], in, out + i); + + if (code < 0) + return code; + } + return 0; +} + +/* Test whether an Arrayed Output function is monotonic. */ +private int +fn_AdOt_is_monotonic(const gs_function_t * pfn_common, + const float *lower, const float *upper, bool must_know) +{ + const gs_function_AdOt_t *const pfn = + (const gs_function_AdOt_t *)pfn_common; + int i; + + for (i = 0; i < pfn->params.n; ++i) { + int code = + gs_function_is_monotonic(pfn->params.Functions[i], lower, upper, + must_know); + + if (code <= 0) + return code; + } + return 1; +} + +/* Free the parameters of an Arrayed Output function. */ +void +gs_function_AdOt_free_params(gs_function_AdOt_params_t * params, + gs_memory_t * mem) +{ + fn_free_functions((gs_function_t **) params->Functions, /* break const */ + params->n, mem); + fn_common_free_params((gs_function_params_t *) params, mem); +} + +/* Allocate and initialize an Arrayed Output function. */ +int +gs_function_AdOt_init(gs_function_t ** ppfn, + const gs_function_AdOt_params_t * params, gs_memory_t * mem) +{ + static const gs_function_head_t function_AdOt_head = + { + function_type_ArrayedOutput, + (fn_evaluate_proc_t) fn_AdOt_evaluate, + (fn_is_monotonic_proc_t) fn_AdOt_is_monotonic, + (fn_free_params_proc_t) gs_function_AdOt_free_params, + fn_common_free + }; + int m = params->m, n = params->n; + int i; + + *ppfn = 0; /* in case of error */ + if (m <= 0 || n <= 0) + return_error(gs_error_rangecheck); + for (i = 0; i < n; ++i) { + const gs_function_t *psubfn = params->Functions[i]; + + if (psubfn->params.m != m || psubfn->params.n != 1) + return_error(gs_error_rangecheck); + } + { + gs_function_AdOt_t *pfn = + gs_alloc_struct(mem, gs_function_AdOt_t, &st_function_AdOt, + "gs_function_AdOt_init"); + + if (pfn == 0) + return_error(gs_error_VMerror); + pfn->params = *params; + pfn->params.Domain = 0; + pfn->params.Range = 0; + pfn->head = function_AdOt_head; + *ppfn = (gs_function_t *) pfn; + } + return 0; +} diff --git a/gs/src/gsfunc3.h b/gs/src/gsfunc3.h new file mode 100644 index 000000000..5eca1cd59 --- /dev/null +++ b/gs/src/gsfunc3.h @@ -0,0 +1,108 @@ +/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: gsfunc3.h */ +/* Definitions for LL3 Functions */ + +#ifndef gsfunc3_INCLUDED +# define gsfunc3_INCLUDED + +#include "gsfunc.h" +#include "gsdsrc.h" + +/* ---------------- Types and structures ---------------- */ + +/* + * Define the Function types. + * See gsfunc.h for why gs_function_type_t can't be an enum type. + */ +enum { + function_type_ExponentialInterpolation = 2, + function_type_1InputStitching = 3, + /* For internal use only */ + function_type_ArrayedOutput = -1 +}; + +/* Define Exponential Interpolation functions. */ +typedef struct gs_function_ElIn_params_s { + gs_function_params_common; + const float *C0; /* n, optional */ + const float *C1; /* n, optional */ + float N; +} gs_function_ElIn_params_t; + +#define private_st_function_ElIn() /* in gsfunc.c */\ + gs_private_st_suffix_add2(st_function_ElIn, gs_function_ElIn_t,\ + "gs_function_ElIn_t", function_ElIn_enum_ptrs, function_ElIn_reloc_ptrs,\ + st_function, params.C0, params.C1) + +/* Define 1-Input Stitching functions. */ +typedef struct gs_function_1ItSg_params_s { + gs_function_params_common; + int k; + const gs_function_t *const *Functions; /* k */ + const float *Bounds; /* k - 1 */ + const float *Encode; /* 2 x k */ +} gs_function_1ItSg_params_t; + +#define private_st_function_1ItSg() /* in gsfunc.c */\ + gs_private_st_suffix_add3(st_function_1ItSg, gs_function_1ItSg_t,\ + "gs_function_1ItSg_t", function_1ItSg_enum_ptrs, function_1ItSg_reloc_ptrs,\ + st_function, params.Functions, params.Bounds, params.Encode) + +/* + * Define Arrayed Output functions. These consist of n m x 1 functions + * whose outputs are assembled into the output of the arrayed function. + * We use them to handle certain PostScript constructs that can accept + * either a single n-output function or n 1-output functions. + * + * Note that for this type, and only this type, both Domain and Range + * are ignored (0). + */ +typedef struct gs_function_AdOt_params_s { + gs_function_params_common; + const gs_function_t *const *Functions; /* n */ +} gs_function_AdOt_params_t; + +#define private_st_function_AdOt() /* in gsfunc.c */\ + gs_private_st_suffix_add1(st_function_AdOt, gs_function_AdOt_t,\ + "gs_function_AdOt_t", function_AdOt_enum_ptrs, function_AdOt_reloc_ptrs,\ + st_function, params.Functions) + +/* ---------------- Procedures ---------------- */ + +/* Allocate and initialize functions of specific types. */ +int gs_function_ElIn_init(P3(gs_function_t ** ppfn, + const gs_function_ElIn_params_t * params, + gs_memory_t * mem)); +int gs_function_1ItSg_init(P3(gs_function_t ** ppfn, + const gs_function_1ItSg_params_t * params, + gs_memory_t * mem)); +int gs_function_AdOt_init(P3(gs_function_t ** ppfn, + const gs_function_AdOt_params_t * params, + gs_memory_t * mem)); + +/* Free parameters of specific types. */ +void gs_function_ElIn_free_params(P2(gs_function_ElIn_params_t * params, + gs_memory_t * mem)); +void gs_function_1ItSg_free_params(P2(gs_function_1ItSg_params_t * params, + gs_memory_t * mem)); +void gs_function_AdOt_free_params(P2(gs_function_AdOt_params_t * params, + gs_memory_t * mem)); + +#endif /* gsfunc3_INCLUDED */ diff --git a/gs/src/gsiparm2.h b/gs/src/gsiparm2.h new file mode 100644 index 000000000..859db6e7c --- /dev/null +++ b/gs/src/gsiparm2.h @@ -0,0 +1,64 @@ +/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: gsiparm2.h */ +/* ImageType 2 image parameter definition */ + +#ifndef gsiparm2_INCLUDED +# define gsiparm2_INCLUDED + +#include "gsiparam.h" + +/* Opaque type for a path */ +#ifndef gx_path_DEFINED +# define gx_path_DEFINED +typedef struct gx_path_s gx_path; + +#endif + +/* + * See Section 7.1 of the Adobe PostScript Version 3010 Supplement + * for a definition of ImageType 2 images. + */ + +typedef struct gs_image2_s { + gs_image_common; + gs_state *DataSource; + float XOrigin, YOrigin; + float Width, Height; + /* + * If UnpaintedPath is not 0, any unpainted path will be appended to it. + */ + gx_path *UnpaintedPath; + bool PixelCopy; +} gs_image2_t; + +/* ImageType 2 images compute the source size differently. */ +#define image2_type_data\ + gx_begin_image2, gx_image2_source_size, 2 +#define image2_enum_procs_data\ + 0/*num_planes*/, gx_no_image_plane_data, gx_ignore_end_image + +/* + * Initialize an ImageType 2 image. Defaults: + * UnpaintedPath = 0 + * PixelCopy = false + */ +void gs_image2_t_init(P1(gs_image2_t * pim)); + +#endif /* gsiparm2_INCLUDED */ diff --git a/gs/src/gsmalloc.c b/gs/src/gsmalloc.c new file mode 100644 index 000000000..cf6048b8b --- /dev/null +++ b/gs/src/gsmalloc.c @@ -0,0 +1,389 @@ +/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: gsmalloc.c */ +/* C heap allocator */ +#include "malloc_.h" +#include "gdebug.h" +#include "gstypes.h" +#include "gsmemory.h" +#include "gsmdebug.h" +#include "gsstruct.h" /* for st_bytes */ +#include "gsmalloc.h" + +/* ------ Heap allocator ------ */ + +/* + * An implementation of Ghostscript's memory manager interface + * that works directly with the C heap. We keep track of all allocated + * blocks so we can free them at cleanup time. + */ +/* Raw memory procedures */ +private gs_memory_proc_alloc_bytes(gs_heap_alloc_bytes); +private gs_memory_proc_resize_object(gs_heap_resize_object); +private gs_memory_proc_free_object(gs_heap_free_object); +private gs_memory_proc_status(gs_heap_status); +private gs_memory_proc_free_all(gs_heap_free_all); + +/* Object memory procedures */ +private gs_memory_proc_alloc_struct(gs_heap_alloc_struct); +private gs_memory_proc_alloc_byte_array(gs_heap_alloc_byte_array); +private gs_memory_proc_alloc_struct_array(gs_heap_alloc_struct_array); +private gs_memory_proc_object_size(gs_heap_object_size); +private gs_memory_proc_object_type(gs_heap_object_type); +private gs_memory_proc_alloc_string(gs_heap_alloc_string); +private gs_memory_proc_resize_string(gs_heap_resize_string); +private gs_memory_proc_free_string(gs_heap_free_string); +private gs_memory_proc_register_root(gs_heap_register_root); +private gs_memory_proc_unregister_root(gs_heap_unregister_root); +private gs_memory_proc_enable_free(gs_heap_enable_free); +private const gs_memory_procs_t gs_malloc_memory_procs = +{ + /* Raw memory procedures */ + gs_heap_alloc_bytes, + gs_heap_resize_object, + gs_heap_free_object, + gs_heap_status, + gs_heap_free_all, + /* Object memory procedures */ + gs_heap_alloc_bytes, + gs_heap_alloc_struct, + gs_heap_alloc_struct, + gs_heap_alloc_byte_array, + gs_heap_alloc_byte_array, + gs_heap_alloc_struct_array, + gs_heap_alloc_struct_array, + gs_heap_object_size, + gs_heap_object_type, + gs_heap_alloc_string, + gs_heap_alloc_string, + gs_heap_resize_string, + gs_heap_free_string, + gs_heap_register_root, + gs_heap_unregister_root, + gs_heap_enable_free +}; + +/* We must make sure that malloc_blocks leave the block aligned. */ +/*typedef struct gs_malloc_block_s gs_malloc_block_t; */ +#define malloc_block_data\ + gs_malloc_block_t *next;\ + gs_malloc_block_t *prev;\ + uint size;\ + gs_memory_type_ptr_t type;\ + client_name_t cname +struct malloc_block_data_s { + malloc_block_data; +}; +struct gs_malloc_block_s { + malloc_block_data; +/* ANSI C does not allow zero-size arrays, so we need the following */ +/* unnecessary and wasteful workaround: */ +#define _npad (-size_of(struct malloc_block_data_s) & 7) + byte _pad[(_npad == 0 ? 8 : _npad)]; /* pad to double */ +#undef _npad +}; + +/* Define the default allocator. */ +gs_malloc_memory_t *gs_malloc_memory_default; + +/* Initialize a malloc allocator. */ +private long heap_available(P0()); +gs_malloc_memory_t * +gs_malloc_memory_init(void) +{ + gs_malloc_memory_t *mem = malloc(sizeof(gs_malloc_memory_t)); + + mem->procs = gs_malloc_memory_procs; + mem->allocated = 0; + mem->limit = max_long; + mem->used = 0; + mem->max_used = 0; + return mem; +} +/* + * Estimate the amount of available memory by probing with mallocs. + * We may under-estimate by a lot, but that's better than winding up with + * a seriously inflated address space. This is quite a hack! + */ +#define max_malloc_probes 20 +#define malloc_probe_size 64000 +private long +heap_available(void) +{ + long avail = 0; + void *probes[max_malloc_probes]; + uint n; + + for (n = 0; n < max_malloc_probes; n++) { + if ((probes[n] = malloc(malloc_probe_size)) == 0) + break; + if_debug2('a', "[a]heap_available probe[%d]=0x%lx\n", + n, (ulong) probes[n]); + avail += malloc_probe_size; + } + while (n) + free(probes[--n]); + return avail; +} + +/* Allocate various kinds of blocks. */ +private byte * +gs_heap_alloc_bytes(gs_memory_t * mem, uint size, client_name_t cname) +{ + gs_malloc_memory_t *mmem = (gs_malloc_memory_t *) mem; + byte *ptr = 0; + +#ifdef DEBUG + const char *msg; + static const char *const ok_msg = "OK"; + +# define set_msg(str) (msg = (str)) +#else +# define set_msg(str) DO_NOTHING +#endif + + if (size > mmem->limit - sizeof(gs_malloc_block_t)) { + /* Definitely too large to allocate; also avoids overflow. */ + set_msg("exceeded limit"); + } else { + uint added = size + sizeof(gs_malloc_block_t); + + if (mmem->limit - added < mmem->used) + set_msg("exceeded limit"); + else if ((ptr = (byte *) malloc(added)) == 0) + set_msg("failed"); + else { + gs_malloc_block_t *bp = (gs_malloc_block_t *) ptr; + + if (mmem->allocated) + mmem->allocated->prev = bp; + bp->next = mmem->allocated; + bp->prev = 0; + bp->size = size; + bp->type = &st_bytes; + bp->cname = cname; + mmem->allocated = bp; + set_msg(ok_msg); + ptr = (byte *) (bp + 1); + gs_alloc_fill(ptr, gs_alloc_fill_alloc, size); + mmem->used += size + sizeof(gs_malloc_block_t); + if (mmem->used > mmem->max_used) + mmem->max_used = mmem->used; + } + } +#ifdef DEBUG + if (gs_debug_c('a') || msg != ok_msg) + dlprintf4("[a+]gs_malloc(%s)(%u) = 0x%lx: %s\n", + client_name_string(cname), size, (ulong) ptr, msg); +#endif + return ptr; +#undef set_msg +} +private void * +gs_heap_alloc_struct(gs_memory_t * mem, gs_memory_type_ptr_t pstype, + client_name_t cname) +{ + void *ptr = + gs_heap_alloc_bytes(mem, gs_struct_type_size(pstype), cname); + + if (ptr == 0) + return 0; + ((gs_malloc_block_t *) ptr)[-1].type = pstype; + return ptr; +} +private byte * +gs_heap_alloc_byte_array(gs_memory_t * mem, uint num_elements, uint elt_size, + client_name_t cname) +{ + ulong lsize = (ulong) num_elements * elt_size; + + if (lsize != (uint) lsize) + return 0; + return gs_heap_alloc_bytes(mem, (uint) lsize, cname); +} +private void * +gs_heap_alloc_struct_array(gs_memory_t * mem, uint num_elements, + gs_memory_type_ptr_t pstype, client_name_t cname) +{ + void *ptr = + gs_heap_alloc_byte_array(mem, num_elements, + gs_struct_type_size(pstype), cname); + + if (ptr == 0) + return 0; + ((gs_malloc_block_t *) ptr)[-1].type = pstype; + return ptr; +} +private void * +gs_heap_resize_object(gs_memory_t * mem, void *obj, uint new_num_elements, + client_name_t cname) +{ + gs_malloc_memory_t *mmem = (gs_malloc_memory_t *) mem; + gs_malloc_block_t *ptr = (gs_malloc_block_t *) obj - 1; + gs_memory_type_ptr_t pstype = ptr->type; + uint old_size = gs_object_size(mem, obj) + sizeof(gs_malloc_block_t); + uint new_size = + gs_struct_type_size(pstype) * new_num_elements + + sizeof(gs_malloc_block_t); + gs_malloc_block_t *new_ptr = + (gs_malloc_block_t *) gs_realloc(ptr, old_size, new_size); + + if (new_ptr == 0) + return 0; + if (new_ptr->prev) + new_ptr->prev->next = new_ptr; + else + mmem->allocated = new_ptr; + if (new_ptr->next) + new_ptr->next->prev = new_ptr; + new_ptr->size = new_size - sizeof(gs_malloc_block_t); + mmem->used -= old_size; + mmem->used += new_size; + if (new_size > old_size) + gs_alloc_fill((byte *) new_ptr + old_size, + gs_alloc_fill_alloc, new_size - old_size); + return new_ptr + 1; +} +private uint +gs_heap_object_size(gs_memory_t * mem, const void *ptr) +{ + return ((const gs_malloc_block_t *)ptr)[-1].size; +} +private gs_memory_type_ptr_t +gs_heap_object_type(gs_memory_t * mem, const void *ptr) +{ + return ((const gs_malloc_block_t *)ptr)[-1].type; +} +private void +gs_heap_free_object(gs_memory_t * mem, void *ptr, client_name_t cname) +{ + gs_malloc_memory_t *mmem = (gs_malloc_memory_t *) mem; + gs_malloc_block_t *bp = mmem->allocated; + + if (gs_debug_c('a')) + dlprintf3("[a-]gs_free(%s) 0x%lx(%u)\n", + client_name_string(cname), (ulong) ptr, + (ptr == 0 ? 0 : ((gs_malloc_block_t *) ptr)[-1].size)); + if (ptr == 0) + return; + if (ptr == bp + 1) { + mmem->allocated = bp->next; + mmem->used -= bp->size + sizeof(gs_malloc_block_t); + + if (mmem->allocated) + mmem->allocated->prev = 0; + gs_alloc_fill(bp, gs_alloc_fill_free, + bp->size + sizeof(gs_malloc_block_t)); + free(bp); + } else { + gs_malloc_block_t *np; + + for (; (np = bp->next) != 0; bp = np) { + if (ptr == np + 1) { + bp->next = np->next; + if (np->next) + np->next->prev = bp; + mmem->used -= np->size + sizeof(gs_malloc_block_t); + gs_alloc_fill(np, gs_alloc_fill_free, + np->size + sizeof(gs_malloc_block_t)); + free(np); + return; + } + } + lprintf2("%s: free 0x%lx not found!\n", + client_name_string(cname), (ulong) ptr); + free((char *)((gs_malloc_block_t *) ptr - 1)); + } +} +private byte * +gs_heap_alloc_string(gs_memory_t * mem, uint nbytes, client_name_t cname) +{ + return gs_heap_alloc_bytes(mem, nbytes, cname); +} +private byte * +gs_heap_resize_string(gs_memory_t * mem, byte * data, uint old_num, uint new_num, + client_name_t cname) +{ + if (gs_heap_object_type(mem, data) != &st_bytes) + lprintf2("%s: resizing non-string 0x%lx!\n", + client_name_string(cname), (ulong) data); + return gs_heap_resize_object(mem, data, new_num, cname); +} +private void +gs_heap_free_string(gs_memory_t * mem, byte * data, uint nbytes, + client_name_t cname) +{ +/****** SHOULD CHECK SIZE IF DEBUGGING ******/ + gs_heap_free_object(mem, data, cname); +} +private void +gs_heap_register_root(gs_memory_t * mem, gs_gc_root_t * rp, gs_ptr_type_t ptype, + void **up, client_name_t cname) +{ +} +private void +gs_heap_unregister_root(gs_memory_t * mem, gs_gc_root_t * rp, + client_name_t cname) +{ +} +private void +gs_heap_status(gs_memory_t * mem, gs_memory_status_t * pstat) +{ + gs_malloc_memory_t *mmem = (gs_malloc_memory_t *) mem; + + pstat->allocated = mmem->used + heap_available(); + pstat->used = mmem->used; +} +private void +gs_heap_enable_free(gs_memory_t * mem, bool enable) +{ + if (enable) + mem->procs.free_object = gs_heap_free_object, + mem->procs.free_string = gs_heap_free_string; + else + mem->procs.free_object = gs_ignore_free_object, + mem->procs.free_string = gs_ignore_free_string; +} + +/* Release all memory acquired by this allocator. */ +private void +gs_heap_free_all(gs_memory_t * mem) +{ + gs_malloc_memory_t *const mmem = (gs_malloc_memory_t *) mem; + gs_malloc_block_t *bp = mmem->allocated; + gs_malloc_block_t *np; + + for (; bp != 0; bp = np) { + np = bp->next; + if (gs_debug_c('a')) + dlprintf3("[a]gs_malloc_release(%s) 0x%lx(%u)\n", + client_name_string(bp->cname), (ulong) (bp + 1), + bp->size); + gs_alloc_fill(bp + 1, gs_alloc_fill_free, bp->size); + free(bp); + } +} + +/* Release all malloc'ed blocks, and free the allocator. */ +void +gs_malloc_memory_release(gs_malloc_memory_t * mem) +{ + gs_free_all((gs_memory_t *) mem); + free(mem); +} diff --git a/gs/src/gsmalloc.h b/gs/src/gsmalloc.h new file mode 100644 index 000000000..ca1ae51a3 --- /dev/null +++ b/gs/src/gsmalloc.h @@ -0,0 +1,71 @@ +/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: gsmalloc.h */ +/* Client interface to default (C heap) allocator */ +/* Requires gsmemory.h */ + +#ifndef gsmalloc_INCLUDED +# define gsmalloc_INCLUDED + +/* Define a memory manager that allocates directly from the C heap. */ +typedef struct gs_malloc_block_s gs_malloc_block_t; +typedef struct gs_malloc_memory_s { + gs_memory_common; + gs_malloc_block_t *allocated; + long limit; + long used; + long max_used; +} gs_malloc_memory_t; + +/* Allocate and initialize a malloc memory manager. */ +gs_malloc_memory_t *gs_malloc_memory_init(P0()); + +/* Release all the allocated blocks, and free the memory manager. */ +void gs_malloc_memory_release(P1(gs_malloc_memory_t *)); + +/* + * Define a default allocator that allocates from the C heap. + * (We would really like to get rid of this.) + */ + +extern gs_malloc_memory_t *gs_malloc_memory_default; + +#define gs_memory_default (*(gs_memory_t *)gs_malloc_memory_default) + +/* + * The following procedures are historical artifacts that we hope to + * get rid of someday. + */ +#define gs_malloc_init()\ + (gs_malloc_memory_default = gs_malloc_memory_init()) +#define gs_malloc_release()\ + (gs_malloc_memory_release(gs_malloc_memory_default),\ + gs_malloc_memory_default = 0) +#define gs_malloc(nelts, esize, cname)\ + (void *)gs_alloc_byte_array(&gs_memory_default, nelts, esize, cname) +#define gs_free(data, nelts, esize, cname)\ + gs_free_object(&gs_memory_default, data, cname) + +/* Define an accessor for the limit on the total allocated heap space. */ +#define gs_malloc_limit (gs_malloc_memory_default->limit) + +/* Define an accessor for the maximum amount ever allocated from the heap. */ +#define gs_malloc_max (gs_malloc_memory_default->max_used) + +#endif /* gsmalloc_INCLUDED */ diff --git a/gs/src/gsnorop.c b/gs/src/gsnorop.c new file mode 100644 index 000000000..163a65267 --- /dev/null +++ b/gs/src/gsnorop.c @@ -0,0 +1,113 @@ +/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: gsnorop.c */ +/* Stubs for unimplemented RasterOp */ +#include "gx.h" +#include "gserrors.h" +#include "gsrop.h" +#include "gxdevcli.h" +#include "gdevmrop.h" + +/* Stub accessors to logical operation in graphics state. */ + +gs_logical_operation_t +gs_current_logical_op(const gs_state * pgs) +{ + return lop_default; +} + +int +gs_set_logical_op(gs_state * pgs, gs_logical_operation_t lop) +{ + return (lop == lop_default ? 0 : gs_note_error(gs_error_rangecheck)); +} + +/* Stub RasterOp implementations for memory devices. */ + +int +mem_mono_strip_copy_rop(gx_device * dev, + const byte * sdata, int sourcex, uint sraster, gx_bitmap_id id, + const gx_color_index * scolors, + const gx_strip_bitmap * textures, const gx_color_index * tcolors, + int x, int y, int width, int height, + int phase_x, int phase_y, gs_logical_operation_t lop) +{ + return_error(gs_error_rangecheck); +} + +int +mem_gray_strip_copy_rop(gx_device * dev, + const byte * sdata, int sourcex, uint sraster, gx_bitmap_id id, + const gx_color_index * scolors, + const gx_strip_bitmap * textures, const gx_color_index * tcolors, + int x, int y, int width, int height, + int phase_x, int phase_y, gs_logical_operation_t lop) +{ + return_error(gs_error_rangecheck); +} + +int +mem_gray8_rgb24_strip_copy_rop(gx_device * dev, + const byte * sdata, int sourcex, uint sraster, gx_bitmap_id id, + const gx_color_index * scolors, + const gx_strip_bitmap * textures, const gx_color_index * tcolors, + int x, int y, int width, int height, + int phase_x, int phase_y, gs_logical_operation_t lop) +{ + return_error(gs_error_rangecheck); +} + +/* Stub default implementations of device procedures. */ + +int +gx_default_copy_rop(gx_device * dev, + const byte * sdata, int sourcex, uint sraster, gx_bitmap_id id, + const gx_color_index * scolors, + const gx_tile_bitmap * texture, const gx_color_index * tcolors, + int x, int y, int width, int height, + int phase_x, int phase_y, gs_logical_operation_t lop) +{ + return_error(gs_error_unknownerror); /* not implemented */ +} + +int +gx_default_strip_copy_rop(gx_device * dev, + const byte * sdata, int sourcex, uint sraster, gx_bitmap_id id, + const gx_color_index * scolors, + const gx_strip_bitmap * textures, const gx_color_index * tcolors, + int x, int y, int width, int height, + int phase_x, int phase_y, gs_logical_operation_t lop) +{ + return_error(gs_error_unknownerror); /* not implemented */ +} + +/* Stub RasterOp source devices. */ + +int +gx_alloc_rop_texture_device(gx_device_rop_texture ** prsdev, gs_memory_t * mem, + client_name_t cname) +{ + return_error(gs_error_rangecheck); +} + +void +gx_make_rop_texture_device(gx_device_rop_texture * dev, gx_device * target, + gs_logical_operation_t log_op, const gx_device_color * texture) +{ /* Never called. */ +} diff --git a/gs/src/gsptype2.h b/gs/src/gsptype2.h new file mode 100644 index 000000000..95e7f9489 --- /dev/null +++ b/gs/src/gsptype2.h @@ -0,0 +1,46 @@ +/* Copyright (C) 1997 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: gsptype2.h */ +/* Client interface to PatternType 2 Patterns */ + +#ifndef gsptype2_INCLUDED +# define gsptype2_INCLUDED + +#include "gspcolor.h" + +/* ---------------- Types and structures ---------------- */ + +#ifndef gs_shading_t_DEFINED +# define gs_shading_t_DEFINED +typedef struct gs_shading_s gs_shading_t; + +#endif + +/* PatternType 2 template */ +typedef struct gs_pattern2_template_s { + gs_pattern_template_common; + const gs_shading_t *Shading; +} gs_pattern2_template_t; + +/* ---------------- Procedures ---------------- */ + +/* Initialize a PatternType 2 pattern. */ +void gs_pattern2_init(P1(gs_pattern2_template_t *)); + +#endif /* gsptype2_INCLUDED */ diff --git a/gs/src/gsrect.h b/gs/src/gsrect.h new file mode 100644 index 000000000..9b4044622 --- /dev/null +++ b/gs/src/gsrect.h @@ -0,0 +1,55 @@ +/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: gsrect.h */ +/* Rectangle utilities */ + +#ifndef gsrect_INCLUDED +# define gsrect_INCLUDED + +/* Check whether one rectangle is included entirely within another. */ +#define rect_within(inner, outer)\ + (inner.q.y <= outer.q.y && inner.q.x <= outer.q.x &&\ + inner.p.y >= outer.p.y && inner.p.x >= outer.p.x) + +/* + * Intersect two rectangles, replacing the first. The result may be + * anomalous (q < p) if the intersection is empty. + */ +#define rect_intersect(to, from)\ + BEGIN\ + if ( from.p.x > to.p.x ) to.p.x = from.p.x;\ + if ( from.q.x < to.q.x ) to.q.x = from.q.x;\ + if ( from.p.y > to.p.y ) to.p.y = from.p.y;\ + if ( from.q.y < to.q.y ) to.q.y = from.q.y;\ + END + +/* + * Calculate the difference of two rectangles, a list of up to 4 rectangles. + * Return the number of rectangles in the list, and set the first rectangle + * to the intersection. The resulting first rectangle is guaranteed not to + * be anomalous (q < p) iff it was not anomalous originally. + * + * Note that unlike the macros above, we need different versions of this + * depending on the data type of the individual values: we'll only implement + * the variations that we need. + */ +int int_rect_difference(P3(gs_int_rect * outer, const gs_int_rect * inner, + gs_int_rect * diffs /*[4] */ )); + +#endif /* gsrect_INCLUDED */ diff --git a/gs/src/gsropc.c b/gs/src/gsropc.c new file mode 100644 index 000000000..4c99238e1 --- /dev/null +++ b/gs/src/gsropc.c @@ -0,0 +1,310 @@ +/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: gsropc.c */ +/* RasterOp-compositing implementation */ +#include "gx.h" +#include "gserrors.h" +#include "gsutil.h" /* for gs_next_ids */ +#include "gxdcolor.h" +#include "gxdevice.h" +#include "gxdevmem.h" +#include "gxropc.h" + +/* ------ Object definition and creation ------ */ + +/* Define RasterOp-compositing objects. */ +private composite_create_default_compositor_proc(c_rop_create_default_compositor); +private composite_equal_proc(c_rop_equal); +private composite_write_proc(c_rop_write); +private composite_read_proc(c_rop_read); +private const gs_composite_type_t gs_composite_rop_type = +{ + { + c_rop_create_default_compositor, + c_rop_equal, + c_rop_write, + c_rop_read + } +}; + +private_st_composite_rop(); + +/* Create a RasterOp-compositing object. */ +int +gs_create_composite_rop(gs_composite_t ** ppcte, + const gs_composite_rop_params_t * params, gs_memory_t * mem) +{ + gs_composite_rop_t *pcte; + + rc_alloc_struct_0(pcte, gs_composite_rop_t, &st_composite_rop, + mem, return_error(gs_error_VMerror), + "gs_create_composite_rop"); + pcte->type = &gs_composite_rop_type; + pcte->id = gs_next_ids(1); + pcte->params = *params; + *ppcte = (gs_composite_t *) pcte; + return 0; +} + +/* ------ Object implementation ------ */ + +#define prcte ((const gs_composite_rop_t *)pcte) + +private bool +c_rop_equal(const gs_composite_t * pcte, const gs_composite_t * pcte2) +{ + return (pcte2->type == pcte->type && +#define prcte2 ((const gs_composite_rop_t *)pcte2) + prcte2->params.log_op == prcte->params.log_op && + (prcte->params.texture == 0 ? prcte2->params.texture == 0 : + prcte2->params.texture != 0 && + gx_device_color_equal(prcte->params.texture, + prcte2->params.texture))); +#undef prcte2 +} + +private int +c_rop_write(const gs_composite_t * pcte, byte * data, uint * psize) +{ +/****** NYI ******/ +} + +private int +c_rop_read(gs_composite_t ** ppcte, const byte * data, uint size, + gs_memory_t * mem) +{ +/****** NYI ******/ +} + +/* ---------------- RasterOp-compositing device ---------------- */ + +/* Define the default RasterOp-compositing device. */ +typedef struct gx_device_composite_rop_s { + gx_device_forward_common; + gs_composite_rop_params_t params; +} gx_device_composite_rop; + +gs_private_st_suffix_add1_final(st_device_composite_rop, + gx_device_composite_rop, "gx_device_composite_rop", + device_c_rop_enum_ptrs, device_c_rop_reloc_ptrs, gx_device_finalize, + st_device_forward, params.texture); +/* The device descriptor. */ +private dev_proc_close_device(dcr_close); +private dev_proc_fill_rectangle(dcr_fill_rectangle); +private dev_proc_copy_mono(dcr_copy_mono); +private dev_proc_copy_color(dcr_copy_color); +private dev_proc_copy_alpha(dcr_copy_alpha); +private const gx_device_composite_rop gs_composite_rop_device = +{std_device_std_body_open(gx_device_composite_rop, 0, + "RasterOp compositor", 0, 0, 1, 1), + {gx_default_open_device, + gx_forward_get_initial_matrix, + gx_forward_sync_output, + gx_forward_output_page, + dcr_close, + gx_forward_map_rgb_color, + gx_forward_map_color_rgb, + dcr_fill_rectangle, + gx_default_tile_rectangle, + dcr_copy_mono, + dcr_copy_color, + gx_default_draw_line, + gx_default_get_bits, + gx_forward_get_params, + gx_forward_put_params, + gx_forward_map_cmyk_color, + gx_forward_get_xfont_procs, + gx_forward_get_xfont_device, + gx_forward_map_rgb_alpha_color, + gx_forward_get_page_device, + gx_forward_get_alpha_bits, + dcr_copy_alpha, + gx_forward_get_band, + gx_default_copy_rop, + gx_default_fill_path, + gx_default_stroke_path, + gx_default_fill_mask, + gx_default_fill_trapezoid, + gx_default_fill_parallelogram, + gx_default_fill_triangle, + gx_default_draw_thin_line, + gx_default_begin_image, + gx_default_image_data, + gx_default_end_image, + gx_default_strip_tile_rectangle, + gx_default_strip_copy_rop, + gx_forward_get_clipping_box, + gx_default_begin_typed_image, + gx_forward_get_bits_rectangle, + gx_forward_map_color_rgb_alpha, + gx_no_create_compositor + } +}; + +/* Create a RasterOp compositor. */ +int +c_rop_create_default_compositor(const gs_composite_t * pcte, + gx_device ** pcdev, gx_device * dev, const gs_imager_state * pis, + gs_memory_t * mem) +{ + gs_logical_operation_t log_op = prcte->params.log_op; + const gx_device_color *texture = prcte->params.texture; + gx_device_composite_rop *cdev; + +#if 0 /*************** */ + if (<<operation is identity >>) { + /* Just use the original device. */ + *pcdev = dev; + return 0; + } +#endif /*************** */ + cdev = + gs_alloc_struct_immovable(mem, gx_device_composite_rop, + &st_device_composite_rop, + "create default RasterOp compositor"); + *pcdev = (gx_device *) cdev; + if (cdev == 0) + return_error(gs_error_VMerror); + *(gx_device *) cdev = *dev; + cdev->dname = gs_composite_rop_device.dname; + cdev->memory = mem; + cdev->stype = &st_device_composite_rop; + cdev->color_info = dev->color_info; + assign_dev_procs(cdev, &gs_composite_rop_device); + /* + * Check for memory devices, and select the correct RasterOp + * implementation based on depth and device color space. + ****** NYI ****** + */ + cdev->target = dev; + cdev->params = prcte->params; + return 0; +} + +/* Close the device and free its storage. */ +private int +dcr_close(gx_device * dev) +{ /* + * Finalization will call close again: avoid a recursion loop. + */ + set_dev_proc(dev, close_device, gx_default_close_device); + gs_free_object(dev->memory, dev, "dcr_close"); + return 0; +} + +/* ------ Imaging ------ */ + +/* Import the existing RasterOp implementations. */ +extern dev_proc_strip_copy_rop(mem_mono_strip_copy_rop); +extern dev_proc_strip_copy_rop(mem_gray_strip_copy_rop); +extern dev_proc_strip_copy_rop(mem_gray8_rgb24_strip_copy_rop); +extern dev_proc_strip_copy_rop(gx_default_strip_copy_rop); + +private int +dcr_fill_rectangle(gx_device * dev, int x, int y, int w, int h, + gx_color_index color) +{ + gx_device_composite_rop *rdev = (gx_device_composite_rop *) dev; + + /* + * This is where all the work gets done right now. + * Sooner or later, we'll have to do the right thing here.... + */ + gs_logical_operation_t log_op = rdev->params.log_op; + const gx_device_color *texture = rdev->params.texture; + gx_color_index colors[2]; + gx_color_index tcolors[2]; + + dev_proc_strip_copy_rop((*copy)) = gx_default_strip_copy_rop; + + colors[0] = colors[1] = color; + if (gs_device_is_memory(dev)) { +/****** SHOULD CHECK FOR STANDARD COLOR REPRESENTATION ******/ + switch (dev->color_info.depth) { + case 1: + copy = mem_mono_strip_copy_rop; + break; + case 2: + case 4: + copy = mem_gray_strip_copy_rop; + break; + case 8: + case 24: + copy = mem_gray8_rgb24_strip_copy_rop; + break; + case 16: + case 32: +/****** NOT IMPLEMENTED ******/ + } + } + if (texture == 0) { + return (*copy) + (dev, (const byte *)0, 0, 0, gx_no_bitmap_id, + (const gx_color_index *)0, (const gx_strip_bitmap *)0, colors, + x, y, w, h, 0, 0, log_op); + } + /* Apply the texture, whatever it may be. */ + if (gx_dc_is_pure(texture)) { + tcolors[0] = tcolors[1] = texture->colors.pure; + return (*copy) + (dev, (const byte *)0, 0, 0, gx_no_bitmap_id, colors, + (const gx_strip_bitmap *)0, tcolors, + x, y, w, h, 0, 0, log_op); + } else if (gx_dc_is_binary_halftone(texture)) { + tcolors[0] = texture->colors.binary.color[0]; + tcolors[1] = texture->colors.binary.color[1]; + return (*copy) + (dev, (const byte *)0, 0, 0, gx_no_bitmap_id, colors, + &texture->colors.binary.b_tile->tiles, tcolors, + x, y, w, h, texture->phase.x, texture->phase.y, log_op); + } else if (gx_dc_is_colored_halftone(texture)) { +/****** NO CAN DO ******/ + } else +/****** WHAT ABOUT PATTERNS? ******/ + return_error(gs_error_rangecheck); +} + +private int +dcr_copy_mono(gx_device * dev, const byte * data, + int dx, int raster, gx_bitmap_id id, int x, int y, int w, int h, + gx_color_index zero, gx_color_index one) +{ +/****** TEMPORARY ******/ + return gx_default_copy_mono(dev, data, dx, raster, id, x, y, w, h, + zero, one); +} + +private int +dcr_copy_color(gx_device * dev, const byte * data, + int dx, int raster, gx_bitmap_id id, + int x, int y, int w, int h) +{ +/****** TEMPORARY ******/ + return gx_default_copy_color(dev, data, dx, raster, id, x, y, w, h); +} + +private int +dcr_copy_alpha(gx_device * dev, const byte * data, int data_x, + int raster, gx_bitmap_id id, int x, int y, int width, int height, + gx_color_index color, int depth) +{ +/****** TEMPORARY ******/ + return gx_default_copy_alpha(dev, data, data_x, raster, id, x, y, + width, height, color, depth); +} diff --git a/gs/src/gsropc.h b/gs/src/gsropc.h new file mode 100644 index 000000000..bc4cf51d8 --- /dev/null +++ b/gs/src/gsropc.h @@ -0,0 +1,54 @@ +/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: gsropc.h */ +/* RasterOp-compositing interface */ + +#ifndef gsropc_INCLUDED +# define gsropc_INCLUDED + +#include "gscompt.h" +#include "gsropt.h" + +/* + * Define parameters for RasterOp-compositing. + * There are two kinds of RasterOp compositing operations. + * If texture == 0, the input data are the texture, and the source is + * implicitly all 0 (black). If texture != 0, it defines the texture, + * and the input data are the source. Note that in the latter case, + * the client (the caller of gs_create_composite_rop) promises that + * *texture will not change. + */ + +#ifndef gx_device_color_DEFINED +# define gx_device_color_DEFINED +typedef struct gx_device_color_s gx_device_color; + +#endif + +typedef struct gs_composite_rop_params_s { + gs_logical_operation_t log_op; + const gx_device_color *texture; +} gs_composite_rop_params_t; + +/* Create a RasterOp-compositing object. */ +int gs_create_composite_rop(P3(gs_composite_t ** ppcte, + const gs_composite_rop_params_t * params, + gs_memory_t * mem)); + +#endif /* gsropc_INCLUDED */ diff --git a/gs/src/gsshade.c b/gs/src/gsshade.c new file mode 100644 index 000000000..b789caa83 --- /dev/null +++ b/gs/src/gsshade.c @@ -0,0 +1,304 @@ +/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: gsshade.c */ +/* Constructors for shadings */ +#include "gx.h" +#include "gscspace.h" +#include "gserrors.h" +#include "gsstruct.h" /* for extern_st */ +#include "gxshade.h" + +/* ---------------- Generic services ---------------- */ + +/* GC descriptors */ +private_st_shading(); +private_st_shading_mesh(); + +/* Check ColorSpace, BBox, and Function (if present). */ +/* Free variables: params. */ +#define check_CBFD(function, domain, m)\ + BEGIN\ + int code = check_CBFD_proc((const gs_shading_params_t *)(params),\ + function, domain, m);\ + if ( code < 0 ) return code;\ + END +#define check_CBF(m) check_CBFD(params->Function, params->Domain, m) +private int +check_CBFD_proc(const gs_shading_params_t * params, + const gs_function_t * function, const float *domain, int m) +{ + int ncomp = gs_color_space_num_components(params->ColorSpace); + + if (ncomp < 0 || + (params->have_BBox && + (params->BBox.p.x > params->BBox.q.x || + params->BBox.p.y > params->BBox.q.y)) + ) + return_error(gs_error_rangecheck); + if (function != 0) { + if (function->params.m != m || function->params.n != ncomp) + return_error(gs_error_rangecheck); + /* + * The Adobe documentation says that the function's domain must + * be a superset of the domain defined in the shading dictionary. + * However, Adobe implementations apparently don't necessarily + * check this ahead of time; therefore, we do the same. + */ +#if 0 /*************** */ + { + int i; + + for (i = 0; i < m; ++i) + if (function->params.Domain[2 * i] > domain[2 * i] || + function->params.Domain[2 * i + 1] < domain[2 * i + 1] + ) + return_error(gs_error_rangecheck); + } +#endif /*************** */ + } + return 0; +} + +/* Check parameters for a mesh shading. */ +/* Free variables: params. */ +#define check_mesh()\ + BEGIN\ + int code = check_mesh_proc((const gs_shading_mesh_params_t *)(params),\ + (params)->Function, (params)->Decode);\ + if ( code < 0 ) return code;\ + END +private int +check_mesh_proc(const gs_shading_mesh_params_t * params, + const gs_function_t * function, const float *decode) +{ + if (!data_source_is_array(params->DataSource)) { + check_CBFD(function, decode, 1); + switch (params->BitsPerCoordinate) { + case 1: + case 2: + case 4: + case 8: + case 12: + case 16: + case 24: + case 32: + break; + default: + return_error(gs_error_rangecheck); + } + switch (params->BitsPerComponent) { + case 1: + case 2: + case 4: + case 8: + case 12: + case 16: + break; + default: + return_error(gs_error_rangecheck); + } + } + return 0; +} + +/* Allocate and initialize a shading. */ +/* Free variables: mem, params, ppsh. */ +#define alloc_shading(shtype, sttype, stype, sfrproc, cname)\ + BEGIN\ + shtype *psh = gs_alloc_struct(mem, shtype, sttype, cname);\ + if ( psh == 0 )\ + return_error(gs_error_VMerror);\ + psh->head.type = stype;\ + psh->head.fill_rectangle = sfrproc;\ + psh->params = *params;\ + *ppsh = (gs_shading_t *)psh;\ + END +#define alloc_return_shading(shtype, sttype, stype, sfrproc, cname)\ + BEGIN\ + alloc_shading(shtype, sttype, stype, sfrproc, cname);\ + return 0;\ + END + +/* ---------------- Function-based shading ---------------- */ + +private_st_shading_Fb(); + +/* Allocate and initialize a Function-based shading. */ +int +gs_shading_Fb_init(gs_shading_t ** ppsh, + const gs_shading_Fb_params_t * params, gs_memory_t * mem) +{ + gs_matrix imat; + + check_CBF(2); + if (gs_matrix_invert(¶ms->Matrix, &imat) < 0) + return_error(gs_error_rangecheck); + alloc_return_shading(gs_shading_Fb_t, &st_shading_Fb, + shading_type_Function_based, + gs_shading_Fb_fill_rectangle, + "gs_shading_Fb_init"); +} + +/* ---------------- Axial shading ---------------- */ + +private_st_shading_A(); + +/* Allocate and initialize an Axial shading. */ +int +gs_shading_A_init(gs_shading_t ** ppsh, + const gs_shading_A_params_t * params, gs_memory_t * mem) +{ + check_CBF(1); + alloc_return_shading(gs_shading_A_t, &st_shading_A, + shading_type_Axial, + gs_shading_A_fill_rectangle, + "gs_shading_A_init"); +} + +/* ---------------- Radial shading ---------------- */ + +private_st_shading_R(); + +/* Allocate and initialize a Radial shading. */ +int +gs_shading_R_init(gs_shading_t ** ppsh, + const gs_shading_R_params_t * params, gs_memory_t * mem) +{ + check_CBF(1); + if ((params->Domain != 0 && params->Domain[0] == params->Domain[1]) || + params->Coords[2] < 0 || params->Coords[5] < 0 + ) + return_error(gs_error_rangecheck); + alloc_return_shading(gs_shading_R_t, &st_shading_R, + shading_type_Radial, + gs_shading_R_fill_rectangle, + "gs_shading_R_init"); +} + +/* ---------------- Free-form Gouraud triangle mesh shading ---------------- */ + +private_st_shading_FfGt(); + +/* Allocate and return a shading with BitsPerFlag. */ +#define alloc_return_BPF_shading(shtype, sttype, stype, sfrproc, bpf, cname)\ + BEGIN\ + alloc_shading(shtype, sttype, stype, sfrproc, cname);\ + ((shtype *)(*ppsh))->params.BitsPerFlag = bpf;\ + return 0;\ + END + +/* Allocate and initialize a Free-form Gouraud triangle mesh shading. */ +int +gs_shading_FfGt_init(gs_shading_t ** ppsh, + const gs_shading_FfGt_params_t * params, gs_memory_t * mem) +{ + int bpf; + + if (params->Decode != 0 && params->Decode[0] == params->Decode[1]) + return_error(gs_error_rangecheck); + check_mesh(); + if (!data_source_is_array(params->DataSource)) + switch (bpf = params->BitsPerFlag) { + case 2: + case 4: + case 8: + break; + default: + return_error(gs_error_rangecheck); + } else + bpf = 2; + alloc_return_BPF_shading(gs_shading_FfGt_t, &st_shading_FfGt, + shading_type_Free_form_Gouraud_triangle, + gs_shading_FfGt_fill_rectangle, bpf, + "gs_shading_FfGt_init"); +} + +/* -------------- Lattice-form Gouraud triangle mesh shading -------------- */ + +private_st_shading_LfGt(); + +/* Allocate and initialize a Lattice-form Gouraud triangle mesh shading. */ +int +gs_shading_LfGt_init(gs_shading_t ** ppsh, + const gs_shading_LfGt_params_t * params, gs_memory_t * mem) +{ + check_mesh(); + if (params->VerticesPerRow < 2) + return_error(gs_error_rangecheck); + alloc_return_shading(gs_shading_LfGt_t, &st_shading_LfGt, + shading_type_Lattice_form_Gouraud_triangle, + gs_shading_LfGt_fill_rectangle, + "gs_shading_LfGt_init"); +} + +/* ---------------- Coons patch mesh shading ---------------- */ + +private_st_shading_Cp(); + +/* Allocate and initialize a Coons patch mesh shading. */ +int +gs_shading_Cp_init(gs_shading_t ** ppsh, + const gs_shading_Cp_params_t * params, gs_memory_t * mem) +{ + int bpf; + + check_mesh(); + if (!data_source_is_array(params->DataSource)) + switch (bpf = params->BitsPerFlag) { + case 2: + case 4: + case 8: + break; + default: + return_error(gs_error_rangecheck); + } else + bpf = 2; + alloc_return_BPF_shading(gs_shading_Cp_t, &st_shading_Cp, + shading_type_Coons_patch, + gs_shading_Cp_fill_rectangle, bpf, + "gs_shading_Cp_init"); +} + +/* ---------------- Tensor product patch mesh shading ---------------- */ + +private_st_shading_Tpp(); + +/* Allocate and initialize a Tensor product patch mesh shading. */ +int +gs_shading_Tpp_init(gs_shading_t ** ppsh, + const gs_shading_Tpp_params_t * params, gs_memory_t * mem) +{ + int bpf; + + check_mesh(); + if (!data_source_is_array(params->DataSource)) + switch (bpf = params->BitsPerFlag) { + case 2: + case 4: + case 8: + break; + default: + return_error(gs_error_rangecheck); + } else + bpf = 2; + alloc_return_BPF_shading(gs_shading_Tpp_t, &st_shading_Tpp, + shading_type_Tensor_product_patch, + gs_shading_Tpp_fill_rectangle, bpf, + "gs_shading_Tpp_init"); +} diff --git a/gs/src/gsshade.h b/gs/src/gsshade.h new file mode 100644 index 000000000..02f67b60d --- /dev/null +++ b/gs/src/gsshade.h @@ -0,0 +1,222 @@ +/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: gsshade.h */ +/* Definitions for shading */ + +#ifndef gsshade_INCLUDED +# define gsshade_INCLUDED + +#include "gsccolor.h" +#include "gscspace.h" +#include "gsdsrc.h" +#include "gsfunc.h" +#include "gsmatrix.h" + +/* ---------------- Types and structures ---------------- */ + +/* Define the shading types. */ +typedef enum { + shading_type_Function_based = 1, + shading_type_Axial = 2, + shading_type_Radial = 3, + shading_type_Free_form_Gouraud_triangle = 4, + shading_type_Lattice_form_Gouraud_triangle = 5, + shading_type_Coons_patch = 6, + shading_type_Tensor_product_patch = 7 +} gs_shading_type_t; + +/* + * Define information common to all shading types. + * We separate the private part from the parameters so that + * clients can create statically initialized parameter structures. + */ +#define gs_shading_params_common\ + gs_color_space *ColorSpace;\ + gs_client_color *Background;\ + bool have_BBox;\ + gs_rect BBox;\ + bool AntiAlias + +/* Define a generic shading, for use as the target type of pointers. */ +typedef struct gs_shading_params_s { + gs_shading_params_common; +} gs_shading_params_t; + +#ifndef gs_shading_t_DEFINED +# define gs_shading_t_DEFINED +typedef struct gs_shading_s gs_shading_t; + +#endif +#ifndef gx_device_DEFINED +# define gx_device_DEFINED +typedef struct gx_device_s gx_device; + +#endif +#define shading_fill_rectangle_proc(proc)\ + int proc(P4(const gs_shading_t *psh, const gs_rect *rect, gx_device *dev,\ + gs_imager_state *pis)) +typedef shading_fill_rectangle_proc((*shading_fill_rectangle_proc_t)); +typedef struct gs_shading_head_s { + gs_shading_type_t type; + shading_fill_rectangle_proc_t fill_rectangle; +} gs_shading_head_t; +struct gs_shading_s { + gs_shading_head_t head; + gs_shading_params_t params; +}; + +#define ShadingType(psh) ((psh)->head.type) +#define private_st_shading() /* in gsshade.c */\ + gs_private_st_ptrs2(st_shading, gs_shading_t, "gs_shading_t",\ + shading_enum_ptrs, shading_reloc_ptrs,\ + params.ColorSpace, params.Background) + +/* Define Function-based shading. */ +typedef struct gs_shading_Fb_params_s { + gs_shading_params_common; + float Domain[4]; + gs_matrix Matrix; + gs_function_t *Function; +} gs_shading_Fb_params_t; + +#define private_st_shading_Fb() /* in gsshade.c */\ + gs_private_st_suffix_add1(st_shading_Fb, gs_shading_Fb_t,\ + "gs_shading_Fb_t", shading_Fb_enum_ptrs, shading_Fb_reloc_ptrs,\ + st_shading, params.Function) + +/* Define Axial shading. */ +typedef struct gs_shading_A_params_s { + gs_shading_params_common; + float Coords[4]; + float Domain[2]; + gs_function_t *Function; + bool Extend[2]; +} gs_shading_A_params_t; + +#define private_st_shading_A() /* in gsshade.c */\ + gs_private_st_suffix_add1(st_shading_A, gs_shading_A_t,\ + "gs_shading_A_t", shading_A_enum_ptrs, shading_A_reloc_ptrs,\ + st_shading, params.Function) + +/* Define Radial shading. */ +typedef struct gs_shading_R_params_s { + gs_shading_params_common; + float Coords[6]; + float Domain[2]; + gs_function_t *Function; + bool Extend[2]; +} gs_shading_R_params_t; + +#define private_st_shading_R() /* in gsshade.c */\ + gs_private_st_suffix_add1(st_shading_R, gs_shading_R_t,\ + "gs_shading_R_t", shading_R_enum_ptrs, shading_R_reloc_ptrs,\ + st_shading, params.Function) + +/* Define common parameters for mesh shading. */ +#define gs_shading_mesh_params_common\ + gs_shading_params_common;\ + gs_data_source_t DataSource;\ + int BitsPerCoordinate;\ + int BitsPerComponent;\ + float *Decode;\ + gs_function_t *Function +/* The following are for internal use only. */ +typedef struct gs_shading_mesh_params_s { + gs_shading_mesh_params_common; +} gs_shading_mesh_params_t; +typedef struct gs_shading_mesh_s { + gs_shading_head_t head; + gs_shading_mesh_params_t params; +} gs_shading_mesh_t; + +#define private_st_shading_mesh() /* in gsshade.c */\ + gs_private_st_suffix_add2(st_shading_mesh, gs_shading_mesh_t,\ + "gs_shading_mesh_t", shading_mesh_enum_ptrs, shading_mesh_reloc_ptrs,\ + st_shading, params.Decode, params.Function) + +/* Define Free-form Gouraud triangle mesh shading. */ +typedef struct gs_shading_FfGt_params_s { + gs_shading_mesh_params_common; + int BitsPerFlag; +} gs_shading_FfGt_params_t; + +#define private_st_shading_FfGt() /* in gsshade.c */\ + gs_private_st_suffix_add0_local(st_shading_FfGt, gs_shading_FfGt_t,\ + "gs_shading_FfGt_t", shading_mesh_enum_ptrs, shading_mesh_reloc_ptrs,\ + st_shading_mesh) + +/* Define Lattice-form Gouraud triangle mesh shading. */ +typedef struct gs_shading_LfGt_params_s { + gs_shading_mesh_params_common; + int VerticesPerRow; +} gs_shading_LfGt_params_t; + +#define private_st_shading_LfGt() /* in gsshade.c */\ + gs_private_st_suffix_add0_local(st_shading_LfGt, gs_shading_LfGt_t,\ + "gs_shading_LfGt_t", shading_mesh_enum_ptrs, shading_mesh_reloc_ptrs,\ + st_shading_mesh) + +/* Define Coons patch mesh shading. */ +typedef struct gs_shading_Cp_params_s { + gs_shading_mesh_params_common; + int BitsPerFlag; +} gs_shading_Cp_params_t; + +#define private_st_shading_Cp() /* in gsshade.c */\ + gs_private_st_suffix_add0_local(st_shading_Cp, gs_shading_Cp_t,\ + "gs_shading_Cp_t", shading_mesh_enum_ptrs, shading_mesh_reloc_ptrs,\ + st_shading_mesh) + +/* Define Tensor product patch mesh shading. */ +typedef struct gs_shading_Tpp_params_s { + gs_shading_mesh_params_common; + int BitsPerFlag; +} gs_shading_Tpp_params_t; + +#define private_st_shading_Tpp() /* in gsshade.c */\ + gs_private_st_suffix_add0_local(st_shading_Tpp, gs_shading_Tpp_t,\ + "gs_shading_Tpp_t", shading_mesh_enum_ptrs, shading_mesh_reloc_ptrs,\ + st_shading_mesh) + +/* ---------------- Procedures ---------------- */ + +/* Create (initialize) shadings of specific types. */ +int gs_shading_Fb_init(P3(gs_shading_t ** ppsh, + const gs_shading_Fb_params_t * params, + gs_memory_t * mem)); +int gs_shading_A_init(P3(gs_shading_t ** ppsh, + const gs_shading_A_params_t * params, + gs_memory_t * mem)); +int gs_shading_R_init(P3(gs_shading_t ** ppsh, + const gs_shading_R_params_t * params, + gs_memory_t * mem)); +int gs_shading_FfGt_init(P3(gs_shading_t ** ppsh, + const gs_shading_FfGt_params_t * params, + gs_memory_t * mem)); +int gs_shading_LfGt_init(P3(gs_shading_t ** ppsh, + const gs_shading_LfGt_params_t * params, + gs_memory_t * mem)); +int gs_shading_Cp_init(P3(gs_shading_t ** ppsh, + const gs_shading_Cp_params_t * params, + gs_memory_t * mem)); +int gs_shading_Tpp_init(P3(gs_shading_t ** ppsh, + const gs_shading_Tpp_params_t * params, + gs_memory_t * mem)); + +#endif /* gsshade_INCLUDED */ diff --git a/gs/src/gstext.c b/gs/src/gstext.c new file mode 100644 index 000000000..352ee355c --- /dev/null +++ b/gs/src/gstext.c @@ -0,0 +1,336 @@ +/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: gstext.c */ +/* Driver text interface support */ +#include "std.h" +#include "gstypes.h" +#include "gdebug.h" +#include "gserror.h" +#include "gserrors.h" +#include "gsmemory.h" +#include "gsstruct.h" +#include "gstypes.h" +#include "gxdevcli.h" +#include "gxpath.h" +#include "gxtext.h" +#include "gzstate.h" + +/* GC descriptors */ +public_st_gs_text_params(); +public_st_gs_text_enum(); + +#define tptr ((gs_text_params_t *)vptr) + +private +ENUM_PTRS_BEGIN(text_params_enum_ptrs) return 0; + +case 0: +if (tptr->operation & TEXT_FROM_STRING) +{ +gs_const_string str; + +str.data = tptr->data.bytes; +str.size = tptr->size; +return ENUM_CONST_STRING(&str); +} +if (tptr->operation & TEXT_FROM_BYTES) + return ENUM_OBJ(tptr->data.bytes); +if (tptr->operation & TEXT_FROM_CHARS) + return ENUM_OBJ(tptr->data.chars); +if (tptr->operation & TEXT_FROM_GLYPHS) + return ENUM_OBJ(tptr->data.glyphs); +return ENUM_OBJ(NULL); +case 1: +return ENUM_OBJ(tptr->operation & TEXT_REPLACE_X_WIDTHS ? + tptr->x_widths : NULL); +case 2: +return ENUM_OBJ(tptr->operation & TEXT_REPLACE_Y_WIDTHS ? + tptr->y_widths : NULL); +ENUM_PTRS_END + +private RELOC_PTRS_BEGIN(text_params_reloc_ptrs) +{ + if (tptr->operation & TEXT_FROM_STRING) { + gs_const_string str; + + str.data = tptr->data.bytes; + str.size = tptr->size; + RELOC_CONST_STRING_VAR(str); + tptr->data.bytes = str.data; + } else if (tptr->operation & TEXT_FROM_BYTES) + RELOC_OBJ_VAR(tptr->data.bytes); + else if (tptr->operation & TEXT_FROM_CHARS) + RELOC_OBJ_VAR(tptr->data.chars); + else if (tptr->operation & TEXT_FROM_GLYPHS) + RELOC_OBJ_VAR(tptr->data.glyphs); + if (tptr->operation & TEXT_REPLACE_X_WIDTHS) + RELOC_OBJ_VAR(tptr->x_widths); + if (tptr->operation & TEXT_REPLACE_Y_WIDTHS) + RELOC_OBJ_VAR(tptr->y_widths); +} +RELOC_PTRS_END + +#undef tptr + +#define eptr ((gs_text_enum_t *)vptr) + +private ENUM_PTRS_BEGIN(text_enum_enum_ptrs) ENUM_USING(st_gs_text_params, &eptr->text, sizeof(eptr->text), index - 1); +case 0: +return ENUM_OBJ(gx_device_enum_ptr(eptr->dev)); +ENUM_PTRS_END + +private RELOC_PTRS_BEGIN(text_enum_reloc_ptrs) +{ + RELOC_USING(st_gs_text_params, &eptr->text, sizeof(eptr->text)); + gx_device_reloc_ptr(eptr->dev, gcst); +} +RELOC_PTRS_END + +#undef eptr + +/* Begin processing text. */ +int +gx_device_text_begin(gx_device * dev, gs_imager_state * pis, + const gs_text_params_t * text, const gs_font * font, + gx_path * path, /* unless DO_NONE & !RETURN_WIDTH */ + const gx_device_color * pdcolor, /* DO_DRAW */ + const gx_clip_path * pcpath, /* DO_DRAW */ + gs_memory_t * mem, gs_text_enum_t ** ppte) +{ + if (TEXT_OPERATION_IS_INVALID(text->operation)) + return_error(gs_error_rangecheck); + { + gx_path *tpath = + ((text->operation & TEXT_DO_NONE) && + !(text->operation & TEXT_RETURN_WIDTH) ? 0 : path); + int code = + (*dev_proc(dev, text_begin)) + (dev, pis, text, font, tpath, + (text->operation & TEXT_DO_DRAW ? pdcolor : 0), + (text->operation & TEXT_DO_DRAW ? pcpath : 0), + mem, ppte); + gs_text_enum_t *pte = *ppte; + + if (code < 0) + return code; + pte->text = *text; + pte->dev = dev; + pte->index = 0; + return code; + } +} + +/* Begin processing text based on a graphics state. */ +int +gs_text_begin(gs_state * pgs, const gs_text_params_t * text, + gs_memory_t * mem, gs_text_enum_t ** ppte) +{ + gx_clip_path *pcpath = 0; + + if (text->operation & TEXT_DO_DRAW) { + int code = gx_effective_clip_path(pgs, &pcpath); + + if (code < 0) + return code; + gx_set_dev_color(pgs); + } + return gx_device_text_begin(pgs->device, (gs_imager_state *) pgs, + text, pgs->font, pgs->path, pgs->dev_color, + pcpath, mem, ppte); +} + +/* Begin PostScript-equivalent text operations. */ +int +gs_show_begin(gs_state * pgs, const byte * str, uint size, + gs_memory_t * mem, gs_text_enum_t ** ppte) +{ + gs_text_params_t text; + + text.operation = TEXT_FROM_STRING | TEXT_DO_DRAW | TEXT_RETURN_WIDTH; + text.data.bytes = str, text.size = size; + return gs_text_begin(pgs, &text, mem, ppte); +} +int +gs_ashow_begin(gs_state * pgs, floatp ax, floatp ay, const byte * str, uint size, + gs_memory_t * mem, gs_text_enum_t ** ppte) +{ + gs_text_params_t text; + + text.operation = TEXT_FROM_STRING | TEXT_ADD_TO_ALL_WIDTHS | + TEXT_DO_DRAW | TEXT_RETURN_WIDTH; + text.data.bytes = str, text.size = size; + text.delta_all.x = ax; + text.delta_all.y = ay; + return gs_text_begin(pgs, &text, mem, ppte); +} +int +gs_widthshow_begin(gs_state * pgs, floatp cx, floatp cy, gs_char chr, + const byte * str, uint size, + gs_memory_t * mem, gs_text_enum_t ** ppte) +{ + gs_text_params_t text; + + text.operation = TEXT_FROM_STRING | TEXT_ADD_TO_SPACE_WIDTH | + TEXT_DO_DRAW | TEXT_RETURN_WIDTH; + text.data.bytes = str, text.size = size; + text.delta_space.x = cx; + text.delta_space.y = cy; + text.space.s_char = chr; + return gs_text_begin(pgs, &text, mem, ppte); +} +int +gs_awidthshow_begin(gs_state * pgs, floatp cx, floatp cy, gs_char chr, + floatp ax, floatp ay, const byte * str, uint size, + gs_memory_t * mem, gs_text_enum_t ** ppte) +{ + gs_text_params_t text; + + text.operation = TEXT_FROM_STRING | + TEXT_ADD_TO_ALL_WIDTHS | TEXT_ADD_TO_SPACE_WIDTH | + TEXT_DO_DRAW | TEXT_RETURN_WIDTH; + text.data.bytes = str, text.size = size; + text.delta_space.x = cx; + text.delta_space.y = cy; + text.space.s_char = chr; + text.delta_all.x = ax; + text.delta_all.y = ay; + return gs_text_begin(pgs, &text, mem, ppte); +} +int +gs_kshow_begin(gs_state * pgs, const byte * str, uint size, + gs_memory_t * mem, gs_text_enum_t ** ppte) +{ + gs_text_params_t text; + + text.operation = TEXT_FROM_STRING | TEXT_DO_DRAW | TEXT_INTERVENE | + TEXT_RETURN_WIDTH; + text.data.bytes = str, text.size = size; + return gs_text_begin(pgs, &text, mem, ppte); +} +int +gs_xyshow_begin(gs_state * pgs, const byte * str, uint size, + const float *x_widths, const float *y_widths, + gs_memory_t * mem, gs_text_enum_t ** ppte) +{ + gs_text_params_t text; + + text.operation = TEXT_FROM_STRING | + TEXT_REPLACE_X_WIDTHS | TEXT_REPLACE_Y_WIDTHS | + TEXT_DO_DRAW | TEXT_INTERVENE | TEXT_RETURN_WIDTH; + text.data.bytes = str, text.size = size; + text.x_widths = x_widths; + text.y_widths = y_widths; + return gs_text_begin(pgs, &text, mem, ppte); +} +int +gs_glyphshow_begin(gs_state * pgs, gs_glyph glyph, + gs_memory_t * mem, gs_text_enum_t ** ppte) +{ + gs_text_params_t text; + +/****** SET glyphs ******/ + text.size = 1; + text.operation = TEXT_FROM_GLYPHS | TEXT_DO_DRAW | TEXT_RETURN_WIDTH; + return gs_text_begin(pgs, &text, mem, ppte); +} +int +gs_cshow_begin(gs_state * pgs, const byte * str, uint size, + gs_memory_t * mem, gs_text_enum_t ** ppte) +{ + gs_text_params_t text; + + text.operation = TEXT_FROM_STRING | TEXT_DO_NONE; + text.data.bytes = str, text.size = size; + return gs_text_begin(pgs, &text, mem, ppte); +} +int +gs_stringwidth_begin(gs_state * pgs, const byte * str, uint size, + gs_memory_t * mem, gs_text_enum_t ** ppte) +{ + gs_text_params_t text; + + text.operation = TEXT_FROM_STRING | TEXT_DO_NONE | TEXT_RETURN_WIDTH; + text.data.bytes = str, text.size = size; + return gs_text_begin(pgs, &text, mem, ppte); +} +int +gs_charpath_begin(gs_state * pgs, const byte * str, uint size, bool stroke_path, + gs_memory_t * mem, gs_text_enum_t ** ppte) +{ + gs_text_params_t text; + + text.operation = TEXT_FROM_STRING | TEXT_RETURN_WIDTH | + (stroke_path ? TEXT_DO_TRUE_CHARPATH : TEXT_DO_FALSE_CHARPATH); + text.data.bytes = str, text.size = size; + return gs_text_begin(pgs, &text, mem, ppte); +} +int +gs_charboxpath_begin(gs_state * pgs, const byte * str, uint size, + bool stroke_path, gs_memory_t * mem, gs_text_enum_t ** ppte) +{ + gs_text_params_t text; + + text.operation = TEXT_FROM_STRING | TEXT_RETURN_WIDTH | + (stroke_path ? TEXT_DO_TRUE_CHARBOXPATH : TEXT_DO_FALSE_CHARBOXPATH); + text.data.bytes = str, text.size = size; + return gs_text_begin(pgs, &text, mem, ppte); +} +int +gs_glyphpath_begin(gs_state * pgs, gs_glyph glyph, bool stroke_path, + gs_memory_t * mem, gs_text_enum_t ** ppte) +{ + gs_text_params_t text; + + text.operation = TEXT_FROM_GLYPHS | TEXT_RETURN_WIDTH | + (stroke_path ? TEXT_DO_TRUE_CHARPATH : TEXT_DO_FALSE_CHARPATH); +/****** SET glyphs ******/ + text.size = 1; + return gs_text_begin(pgs, &text, mem, ppte); +} + +/* Process text after 'begin'. */ +int +gs_text_process(gs_text_enum_t * pte) +{ + return pte->procs->process(pte); +} + +/* Set text metrics and optionally enable caching. */ +int +gs_text_setcharwidth(gs_text_enum_t * pte, const double wxy[2]) +{ + return pte->procs->set_cache(pte, wxy, TEXT_SET_CHAR_WIDTH); +} +int +gs_text_setcachedevice(gs_text_enum_t * pte, const double wbox[6]) +{ + return pte->procs->set_cache(pte, wbox, TEXT_SET_CACHE_DEVICE); +} +int +gs_text_setcachedevice2(gs_text_enum_t * pte, const double wbox2[10]) +{ + return pte->procs->set_cache(pte, wbox2, TEXT_SET_CACHE_DEVICE2); +} + +/* Release the text processing structures. */ +void +gs_text_release(gs_text_enum_t * pte, client_name_t cname) +{ + rc_decrement_only(pte, cname); +} diff --git a/gs/src/gstext.h b/gs/src/gstext.h new file mode 100644 index 000000000..9182b11b6 --- /dev/null +++ b/gs/src/gstext.h @@ -0,0 +1,223 @@ +/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: gstext.h */ +/* Driver interface for text */ + +#ifndef gstext_INCLUDED +# define gstext_INCLUDED + +#include "gsccode.h" +#include "gsrefct.h" + +/* EVERYTHING IN THIS FILE IS SUBJECT TO CHANGE WITHOUT NOTICE. */ + +/* + * Note that like get_params and get_hardware_params, but unlike all other + * driver procedures, text display must return information to the generic + * code: + * *show except [x][y]show: the string escapement a.k.a. "width"). + * charpath, .glyphpath: the entire character description. + * .charboxpath: the character bounding box. + */ + +/* + * Define the set of possible text operations. While we define this as + * a bit mask for convenience in testing, only certain combinations are + * meaningful. Specifically, the following are errors: + * - No FROM or DO. + * The following are undefined: + * - More than one FROM or DO. + * - Both ADD_TO and REPLACE. + */ +#define TEXT_HAS_MORE_THAN_ONE_(op, any_)\ + ( ((op) & any_) & (((op) & any_) - 1) ) +#define TEXT_OPERATION_IS_INVALID(op)\ + (!((op) & TEXT_FROM_ANY_) ||\ + !((op) & TEXT_DO_ANY_) ||\ + TEXT_HAS_MORE_THAN_ONE_(op, TEXT_FROM_ANY_) ||\ + TEXT_HAS_MORE_THAN_ONE_(op, TEXT_DO_ANY_) ||\ + (((op) & TEXT_ADD_ANY_) && ((op) & TEXT_REPLACE_ANY_))\ + ) + + /* Define the representation of the text itself. */ +#define TEXT_FROM_STRING 0x00001 +#define TEXT_FROM_BYTES 0x00002 +#define TEXT_FROM_CHARS 0x00004 +#define TEXT_FROM_GLYPHS 0x00008 +#define TEXT_FROM_ANY_ /* internal use only, see above */\ + (TEXT_FROM_STRING | TEXT_FROM_BYTES | TEXT_FROM_CHARS | TEXT_FROM_GLYPHS) + /* Define how to compute escapements. */ +#define TEXT_ADD_TO_ALL_WIDTHS 0x00010 +#define TEXT_ADD_TO_SPACE_WIDTH 0x00020 +#define TEXT_ADD_ANY_ /* internal use only, see above */\ + (TEXT_ADD_TO_ALL_WIDTHS | TEXT_ADD_TO_SPACE_WIDTH) +#define TEXT_REPLACE_X_WIDTHS 0x00040 +#define TEXT_REPLACE_Y_WIDTHS 0x00080 +#define TEXT_REPLACE_ANY_ /* internal use only, see above */\ + (TEXT_REPLACE_X_WIDTHS | TEXT_REPLACE_Y_WIDTHS) + /* Define what result should be produced. */ +#define TEXT_DO_NONE 0x00100 /* stringwidth or cshow only */ +#define TEXT_DO_DRAW 0x00200 +#define TEXT_DO_FALSE_CHARPATH 0x00400 +#define TEXT_DO_TRUE_CHARPATH 0x00800 +#define TEXT_DO_FALSE_CHARBOXPATH 0x01000 +#define TEXT_DO_TRUE_CHARBOXPATH 0x02000 +#define TEXT_DO_ANY_CHARPATH\ + (TEXT_DO_FALSE_CHARPATH | TEXT_DO_TRUE_CHARPATH |\ + TEXT_DO_FALSE_CHARBOXPATH | TEXT_DO_TRUE_CHARBOXPATH) +#define TEXT_DO_ANY_ /* internal use only, see above */\ + (TEXT_DO_NONE | TEXT_DO_DRAW | TEXT_DO_ANY_CHARPATH) + /* Define whether the client intervenes between characters. */ +#define TEXT_INTERVENE 0x10000 + /* Define whether to return the width. */ +#define TEXT_RETURN_WIDTH 0x20000 + +/* + * Define the structure of parameters passed in for text display. + * Note that the implementation does not modify any of these; the client + * must not modify them after initialization. + */ +typedef struct gs_text_params_s { + /* The client must set the following in all cases. */ + uint operation; /* TEXT_xxx mask */ + union sd_ { + const byte *bytes; /* FROM_STRING, FROM_BYTES */ + const gs_char *chars; /* FROM_CHARS */ + const gs_glyph *glyphs; /* FROM_GLYPHS */ + } data; + uint size; /* number of data elements */ + /* The following are used only in the indicated cases. */ + gs_point delta_all; /* ADD_TO_ALL_WIDTHS */ + gs_point delta_space; /* ADD_TO_SPACE_WIDTH */ + union s_ { + gs_char s_char; /* ADD_TO_SPACE_WIDTH & !FROM_GLYPHS */ + gs_glyph s_glyph; /* ADD_TO_SPACE_WIDTH & FROM_GLYPHS */ + } space; + /* If x_widths == y_widths, widths are taken in pairs. */ + /* Either one may be NULL, meaning widths = 0. */ + const float *x_widths; /* REPLACE_X_WIDTHS */ + const float *y_widths; /* REPLACE_Y_WIDTHS */ +} gs_text_params_t; + +#define st_gs_text_params_max_ptrs 3 +/*extern_st(st_gs_text_params); */ +#define public_st_gs_text_params() /* in gstext.c */\ + gs_public_st_composite(st_gs_text_params, gs_text_params_t,\ + "gs_text_params", text_params_enum_ptrs, text_params_reloc_ptrs) + +/* Define the abstract type for the object procedures. */ +typedef struct gs_text_enum_procs_s gs_text_enum_procs_t; + +/* + * Define the common part of the structure that tracks the state of text + * display. All implementations of text_begin must allocate one of these + * using rc_alloc_struct_1; implementations may subclass and extend it. + * Note that it includes a copy of the text parameters. + */ +#ifndef gx_device_DEFINED +# define gx_device_DEFINED +typedef struct gx_device_s gx_device; + +#endif +#define gs_text_enum_common\ + /* The following are set at initialization, and const thereafter. */\ + gs_text_params_t text;\ + const gs_text_enum_procs_t *procs;\ + gx_device *dev;\ + /* The following change dynamically. */\ + rc_header rc;\ + uint index /* index within string */ +typedef struct gs_text_enum_s { + gs_text_enum_common; +} gs_text_enum_t; + +#define st_gs_text_enum_max_ptrs st_gs_text_params_max_ptrs +/*extern_st(st_gs_text_enum); */ +#define public_st_gs_text_enum() /* in gstext.c */\ + gs_public_st_composite(st_gs_text_enum, gs_text_enum_t, "gs_text_enum_t",\ + text_enum_enum_ptrs, text_enum_reloc_ptrs) + +/* Begin processing text. */ +/* Note that these take a graphics state argument. */ +#ifndef gs_state_DEFINED +# define gs_state_DEFINED +typedef struct gs_state_s gs_state; + +#endif +int gs_text_begin(P4(gs_state * pgs, const gs_text_params_t * text, + gs_memory_t * mem, gs_text_enum_t ** ppenum)); + +/* Begin the PostScript-equivalent text operators. */ +int + gs_show_begin(P5(gs_state *, const byte *, uint, + gs_memory_t *, gs_text_enum_t **)), gs_ashow_begin(P7(gs_state *, floatp, floatp, const byte *, uint, + gs_memory_t *, gs_text_enum_t **)), + gs_widthshow_begin(P8(gs_state *, floatp, floatp, gs_char, + const byte *, uint, + gs_memory_t *, gs_text_enum_t **)), gs_awidthshow_begin(P10(gs_state *, floatp, floatp, gs_char, + floatp, floatp, const byte *, uint, + gs_memory_t *, gs_text_enum_t **)), + gs_kshow_begin(P5(gs_state *, const byte *, uint, + gs_memory_t *, gs_text_enum_t **)), gs_xyshow_begin(P7(gs_state *, const byte *, uint, + const float *, const float *, + gs_memory_t *, gs_text_enum_t **)), + gs_glyphshow_begin(P4(gs_state *, gs_glyph, + gs_memory_t *, gs_text_enum_t **)), gs_cshow_begin(P5(gs_state *, const byte *, uint, + gs_memory_t *, gs_text_enum_t **)), + gs_stringwidth_begin(P5(gs_state *, const byte *, uint, + gs_memory_t *, gs_text_enum_t **)), gs_charpath_begin(P6(gs_state *, const byte *, uint, bool, + gs_memory_t *, gs_text_enum_t **)), + gs_glyphpath_begin(P5(gs_state *, gs_glyph, bool, + gs_memory_t *, gs_text_enum_t **)), gs_charboxpath_begin(P6(gs_state *, const byte *, uint, bool, + gs_memory_t *, gs_text_enum_t **)); + +/* + * Define the possible return values from gs_text_process. The client + * should call text_process until it returns 0 (successful completion) or a + * negative (error) value. + */ + + /* + * The client must render a character: obtain the code from + * gs_show_current_char, do whatever is necessary, and then + * call gs_text_process again. + */ +#define TEXT_PROCESS_RENDER 1 + + /* + * The client has asked to intervene between characters. + * Obtain the previous and next codes from gs_show_previous_char + * and gs_kshow_next_char, do whatever is necessary, and then + * call gs_text_process again. + */ +#define TEXT_PROCESS_INTERVENE 2 + +/* Process text after 'begin'. */ +int gs_text_process(P1(gs_text_enum_t * penum)); + +/* Set text metrics and optionally enable caching. */ +int + gs_text_setcharwidth(P2(gs_text_enum_t * penum, const double wxy[2])), + gs_text_setcachedevice(P2(gs_text_enum_t * penum, const double wbox[6])), + gs_text_setcachedevice2(P2(gs_text_enum_t * penum, const double wbox2[10])); + +/* Release the text processing structures. */ +void gs_text_release(P2(gs_text_enum_t * penum, client_name_t cname)); + +#endif /* gstext_INCLUDED */ diff --git a/gs/src/gstrap.c b/gs/src/gstrap.c new file mode 100644 index 000000000..555f88667 --- /dev/null +++ b/gs/src/gstrap.c @@ -0,0 +1,184 @@ +/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: gstrap.c */ +/* Setting trapping parameters and zones */ +#include "string_.h" +#include "gx.h" +#include "gserrors.h" +#include "gstrap.h" + +/* Parameter utilities, copied from gdevpsdf.c. */ +/* These should be merged.... */ + +/* Compare a C string and a gs_param_string. */ +private bool +trap_key_eq(const gs_param_string * pcs, const char *str) +{ + return (strlen(str) == pcs->size && + !strncmp(str, (const char *)pcs->data, pcs->size)); +} + +/* Put an enumerated value. */ +private int +trap_put_enum_param(gs_param_list * plist, gs_param_name param_name, + int *pvalue, const char *const pnames[], int ecode) +{ + gs_param_string ens; + int code = param_read_name(plist, param_name, &ens); + + switch (code) { + case 1: + return ecode; + case 0: + { + int i; + + for (i = 0; pnames[i] != 0; ++i) + if (trap_key_eq(&ens, pnames[i])) { + *pvalue = i; + return 0; + } + } + code = gs_error_rangecheck; + default: + ecode = code; + param_signal_error(plist, param_name, code); + } + return code; +} + +/* Put a Boolean, integer, or float parameter. */ +private int +trap_put_bool_param(gs_param_list * plist, gs_param_name param_name, + bool * pval, int ecode) +{ + int code; + + switch (code = param_read_bool(plist, param_name, pval)) { + default: + ecode = code; + param_signal_error(plist, param_name, ecode); + case 0: + case 1: + break; + } + return ecode; +} +private int +trap_put_int_param(gs_param_list * plist, gs_param_name param_name, + int *pval, int ecode) +{ + int code; + + switch (code = param_read_int(plist, param_name, pval)) { + default: + ecode = code; + param_signal_error(plist, param_name, ecode); + case 0: + case 1: + break; + } + return ecode; +} +private bool +check_unit(float *pval) +{ + return (*pval >= 0 && *pval <= 1); +} +private bool +check_positive(float *pval) +{ + return (*pval > 0); +} +private int +trap_put_float_param(gs_param_list * plist, gs_param_name param_name, + float *pval, bool(*check) (P1(float *pval)), int ecode) +{ + int code; + + switch (code = param_read_float(plist, param_name, pval)) { + case 0: + if ((*check) (pval)) + return 0; + code = gs_error_rangecheck; + default: + ecode = code; + param_signal_error(plist, param_name, ecode); + break; + case 1: + break; + } + return ecode; +} + +/* settrapparams */ +int +gs_settrapparams(gs_trap_params_t * pparams, gs_param_list * plist) +{ + gs_trap_params_t params; + int ecode = 0; + static const char *const trap_placement_names[] = + { + gs_trap_placement_names, 0 + }; + + params = *pparams; + ecode = trap_put_float_param(plist, "BlackColorLimit", + ¶ms.BlackColorLimit, + check_unit, ecode); + ecode = trap_put_float_param(plist, "BlackDensityLimit", + ¶ms.BlackDensityLimit, + check_positive, ecode); + ecode = trap_put_float_param(plist, "BlackWidth", + ¶ms.BlackWidth, + check_positive, ecode); + ecode = trap_put_bool_param(plist, "Enabled", + ¶ms.Enabled, ecode); + ecode = trap_put_bool_param(plist, "ImageInternalTrapping", + ¶ms.ImageInternalTrapping, ecode); + ecode = trap_put_int_param(plist, "ImageResolution", + ¶ms.ImageResolution, ecode); + if (params.ImageResolution <= 0) + param_signal_error(plist, "ImageResolution", + ecode = gs_error_rangecheck); + ecode = trap_put_bool_param(plist, "ImageToObjectTrapping", + ¶ms.ImageToObjectTrapping, ecode); + { + int placement = params.ImageTrapPlacement; + + ecode = trap_put_enum_param(plist, "ImageTrapPlacement", + &placement, trap_placement_names, ecode); + params.ImageTrapPlacement = placement; + } + ecode = trap_put_float_param(plist, "SlidingTrapLimit", + ¶ms.SlidingTrapLimit, + check_unit, ecode); + ecode = trap_put_float_param(plist, "StepLimit", + ¶ms.StepLimit, check_unit, ecode); + ecode = trap_put_float_param(plist, "TrapColorScaling", + ¶ms.TrapColorScaling, + check_unit, ecode); + ecode = trap_put_float_param(plist, "TrapWidth", + ¶ms.TrapWidth, + check_positive, ecode); + if (ecode < 0) + return ecode; + *pparams = params; + return 0; +} diff --git a/gs/src/gstrap.h b/gs/src/gstrap.h new file mode 100644 index 000000000..968a51ae4 --- /dev/null +++ b/gs/src/gstrap.h @@ -0,0 +1,75 @@ +/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: gstrap.h */ +/* Definitions for trapping parameters and zones */ + +#ifndef gstrap_INCLUDED +# define gstrap_INCLUDED + +#include "gsparam.h" + +/* ---------------- Types and structures ---------------- */ + +/* Opaque type for a path */ +#ifndef gx_path_DEFINED +# define gx_path_DEFINED +typedef struct gx_path_s gx_path; + +#endif + +/* Define the placement of image traps. */ +typedef enum { + tp_Center, + tp_Choke, + tp_Spread, + tp_Normal +} gs_trap_placement_t; + +#define gs_trap_placement_names\ + "Center", "Choke", "Spread", "Normal" + +/* Define a trapping parameter set. */ +typedef struct gs_trap_params_s { + float BlackColorLimit; /* 0-1 */ + float BlackDensityLimit; /* > 0 */ + float BlackWidth; /* > 0 */ + /* ColorantZoneDetails; */ + bool Enabled; + /* HalftoneName; */ + bool ImageInternalTrapping; + int ImageResolution; + bool ImageToObjectTrapping; + gs_trap_placement_t ImageTrapPlacement; + float SlidingTrapLimit; /* 0-1 */ + float StepLimit; /* 0-1 */ + float TrapColorScaling; /* 0-1 */ + float TrapWidth; /* > 0 */ +} gs_trap_params_t; + +/* Define a trapping zone. ****** SUBJECT TO CHANGE ****** */ +typedef struct gs_trap_zone_s { + gs_trap_params_t params; + gx_path *zone; +} gs_trap_zone_t; + +/* ---------------- Procedures ---------------- */ + +int gs_settrapparams(P2(gs_trap_params_t * params, gs_param_list * list)); + +#endif /* gstrap_INCLUDED */ diff --git a/gs/src/gxalpha.h b/gs/src/gxalpha.h new file mode 100644 index 000000000..5cf4659ec --- /dev/null +++ b/gs/src/gxalpha.h @@ -0,0 +1,68 @@ +/* Copyright (C) 1997 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: gxalpha.h */ +/* Internal machinery for alpha channel support */ + +#ifndef gxalpha_INCLUDED +# define gxalpha_INCLUDED + +/* + * As discussed in the classic Porter & Duff paper on compositing, + * supporting alpha channel properly involves premultiplying color values + * that are associated with non-unity alpha values. After considerable + * thrashing around trying to read between the lines of the spotty NeXT + * documentation, we've concluded that the correct approach is to + * premultiply towards whatever the color value 0 represents in the device's + * native color space: black for DeviceGray and DeviceRGB (displays and some + * file formats), white for DeviceCMYK (color printers), with a special hack + * for monochrome printers TBD. This makes things very easy internally, at + * the expense of some inconsistency at the boundaries. + * + * For the record, the only places apparently affected by this decision + * are the following: + * - alphaimage, if it doesn't assume premultiplication (see below) + * - readimage + * - The cmap_rgb_alpha_ procedures in gxcmap.c + * - [color]image, if they are supposed to use currentalpha (see below) + * - The compositing code in gsalphac.c + * + * The NeXT documentation also is very unclear as to how readimage, + * alphaimage, and [color]image are supposed to work. Our current + * interpretation is the following: + * + * - readimage reads pixels exactly as the device stores them + * (converted into DeviceGray or DeviceRGB space if the device + * uses a palette). Pixels with non-unity alpha come out + * premultiplied, however the device stores them. + * + * - alphaimage assumes the pixels are premultiplied as appropriate + * for the relevant color space. This makes alphaimage and + * readimage complementary, i.e., the output of readimage is + * suitable as the input of alphaimage. + * + * - [color]image disregard currentalpha, and treat all input as + * opaque (alpha = 1). */ +/* + * Just in case we ever change our minds about the direction of + * premultiplication, uncommenting the following preprocessor definition is + * supposed to produce premultiplication towards white. + */ +/*#define PREMULTIPLY_TOWARDS_WHITE */ + +#endif /* gxalpha_INCLUDED */ diff --git a/gs/src/gxbitfmt.h b/gs/src/gxbitfmt.h new file mode 100644 index 000000000..c0c80399b --- /dev/null +++ b/gs/src/gxbitfmt.h @@ -0,0 +1,189 @@ +/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: gxbitfmt.h */ +/* Definitions for bitmap storage formats */ + +#ifndef gxbitfmt_INCLUDED +# define gxbitfmt_INCLUDED + +/* + * Several operations, such as the get_bits_rectangle driver procedure, can + * take and/or produce data in a flexible variety of formats; the ability to + * describe how bitmap data is stored is useful in other contexts as well. + * We define bitmap storage formats using a bit mask: this allows a + * procedure to ask for, or offer to provide, data in more than one format. + */ + +typedef ulong gx_bitmap_format_t; + + /* + * Define the supported color space alternatives. + */ + +#define GB_COLORS_NATIVE (1L<<0) /* native representation (DevicePixel) */ +#define GB_COLORS_GRAY (1L<<1) /* DeviceGray */ +#define GB_COLORS_RGB (1L<<2) /* DeviceRGB */ +#define GB_COLORS_CMYK (1L<<3) /* DeviceCMYK */ + +#define GB_COLORS_STANDARD_ALL\ + (GB_COLORS_GRAY | GB_COLORS_RGB | GB_COLORS_CMYK) +#define GB_COLORS_ALL\ + (GB_COLORS_NATIVE | GB_COLORS_STANDARD_ALL) +#define gb_colors_for_device(dev)\ + ((dev)->color_info.num_components == 4 ? GB_COLORS_CMYK :\ + (dev)->color_info.num_components == 3 ? GB_COLORS_RGB : GB_COLORS_GRAY) +#define GB_COLORS_NAMES\ + "colors_native", "colors_Gray", "colors_RGB", "colors_CMYK" + + /* + * Define whether alpha information is included. For GB_COLORS_NATIVE, + * all values other than GB_ALPHA_NONE are equivalent. + */ + +#define GB_ALPHA_NONE (1L<<4) /* no alpha */ +#define GB_ALPHA_FIRST (1L<<5) /* include alpha as first component */ +#define GB_ALPHA_LAST (1L<<6) /* include alpha as last component */ + /*unused*/ /*(1L<<7)*/ + +#define GB_ALPHA_ALL\ + (GB_ALPHA_NONE | GB_ALPHA_FIRST | GB_ALPHA_LAST) +#define GB_ALPHA_NAMES\ + "alpha_none", "alpha_first", "alpha_last", "?alpha_unused?" + + /* + * Define the supported depths per component for GB_COLORS_STANDARD. + */ + +#define GB_DEPTH_1 (1L<<8) +#define GB_DEPTH_2 (1L<<9) +#define GB_DEPTH_4 (1L<<10) +#define GB_DEPTH_8 (1L<<11) +#define GB_DEPTH_12 (1L<<12) +#define GB_DEPTH_16 (1L<<13) + /*unused1*/ /*(1L<<14)*/ + /*unused2*/ /*(1L<<15)*/ + +#define GB_DEPTH_ALL\ + (GB_DEPTH_1 | GB_DEPTH_2 | GB_DEPTH_4 | GB_DEPTH_8 |\ + GB_DEPTH_12 | GB_DEPTH_16) +#define GB_DEPTH_NAMES\ + "depth_1", "depth_2", "depth_4", "depth_8",\ + "depth_12", "depth_16", "?depth_unused1?", "?depth_unused2?" + +/* Find the maximum depth of an options mask. */ +#define GB_OPTIONS_MAX_DEPTH(opt)\ +"\ +\000\001\002\002\004\004\004\004\010\010\010\010\010\010\010\010\ +\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\014\ +\020\020\020\020\020\020\020\020\020\020\020\020\020\020\020\020\ +\020\020\020\020\020\020\020\020\020\020\020\020\020\020\020\020\ +"[((opt) >> 8) & 0x3f] +/* Find the depth of an options mask with exactly 1 bit set. */ +#define GB_OPTIONS_DEPTH(opt)\ + ((((opt) >> 8) & 0xf) |\ + "\000\000\014\020"[((opt) >> 12) & 3]) + + /* + * Define the supported packing formats. Currently, GB_PACKING_PLANAR is + * only partially supported, and GB_PACKING_BIT_PLANAR is hardly supported + * at all. + */ + +#define GB_PACKING_CHUNKY (1L<<16) +#define GB_PACKING_PLANAR (1L<<17) /* 1 plane per component */ +#define GB_PACKING_BIT_PLANAR (1L<<18) /* 1 plane per bit */ + /*unused*/ /*(1L<<19)*/ + +#define GB_PACKING_ALL\ + (GB_PACKING_CHUNKY | GB_PACKING_PLANAR | GB_PACKING_BIT_PLANAR) +#define GB_PACKING_NAMES\ + "packing_chunky", "packing_planar", "packing_bit_planar", "?packing_unused?" + + /* + * Define the possible methods of returning data. + */ + +#define GB_RETURN_COPY (1L<<20) /* copy to client's buffer */ +#define GB_RETURN_POINTER (1L<<21) /* return pointers to data */ + +#define GB_RETURN_ALL\ + (GB_RETURN_COPY | GB_RETURN_POINTER) +#define GB_RETURN_NAMES\ + "return_copy", "return_pointer" + + /* + * Define the allowable alignments. This is only relevant for + * GB_RETURN_POINTER: for GB_RETURN_COPY, any alignment is acceptable. + */ + +#define GB_ALIGN_STANDARD (1L<<22) /* require standard bitmap alignment */ +#define GB_ALIGN_ANY (1L<<23) /* any alignment is acceptable */ + +#define GB_ALIGN_ALL\ + (GB_ALIGN_ANY | GB_ALIGN_STANDARD) +#define GB_ALIGN_NAMES\ + "align_any", "align_standard" + + /* + * Define the allowable X offsets. GB_OFFSET_ANY is only relevant for + * GB_RETURN_POINTER: for GB_RETURN_COPY, clients must specify the + * offset so they know how much space to allocate. + */ + +#define GB_OFFSET_0 (1L<<24) /* no offsetting */ +#define GB_OFFSET_SPECIFIED (1L<<25) /* client-specified offset */ +#define GB_OFFSET_ANY (1L<<26) /* any offset is acceptable */ + /* (for GB_RETURN_POINTER only) */ + /*unused*/ /*(1L<<27)*/ + +#define GB_OFFSET_ALL\ + (GB_OFFSET_0 | GB_OFFSET_SPECIFIED | GB_OFFSET_ANY) +#define GB_OFFSET_NAMES\ + "offset_0", "offset_specified", "offset_any", "?offset_unused?" + + /* + * Define the allowable rasters. GB_RASTER_ANY is only relevant for + * GB_RETURN_POINTER, for the same reason as GB_OFFSET_ANY. + * Note also that if GB_ALIGN_STANDARD and GB_RASTER_SPECIFIED are + * both chosen and more than one scan line is being transferred, + * the raster value must also be aligned (i.e., 0 mod align_bitmap_mod). + * Implementors are not required to check this. + */ + + /* + * Standard raster is bitmap_raster(dev->width) for return_ptr, + * bitmap_raster(x_offset + width) for return_copy, + * padding per alignment. + */ +#define GB_RASTER_STANDARD (1L<<28) +#define GB_RASTER_SPECIFIED (1L<<29) /* any client-specified raster */ +#define GB_RASTER_ANY (1L<<30) /* any raster is acceptable (for */ + /* GB_RETURN_POINTER only) */ + +#define GB_RASTER_ALL\ + (GB_RASTER_STANDARD | GB_RASTER_SPECIFIED | GB_RASTER_ANY) +#define GB_RASTER_NAMES\ + "raster_standard", "raster_specified", "raster_any" + +/* Define names for debugging printout. */ +#define GX_BITMAP_FORMAT_NAMES\ + GB_COLORS_NAMES, GB_ALPHA_NAMES, GB_DEPTH_NAMES, GB_PACKING_NAMES,\ + GB_RETURN_NAMES, GB_ALIGN_NAMES, GB_OFFSET_NAMES, GB_RASTER_NAMES + +#endif /* gxbitfmt_INCLUDED */ diff --git a/gs/src/gxbitops.h b/gs/src/gxbitops.h new file mode 100644 index 000000000..9a8e25460 --- /dev/null +++ b/gs/src/gxbitops.h @@ -0,0 +1,136 @@ +/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: gxbitops.h */ +/* Internal definitions for bitmap operations */ + +#ifndef gxbitops_INCLUDED +# define gxbitops_INCLUDED + +#include "gsbitops.h" + +/* + * Macros for processing bitmaps in the largest possible chunks. + * Bits within a byte are always stored big-endian; + * bytes are likewise stored in left-to-right order, i.e., big-endian. + * Note that this is the format used for the source of copy_mono. + * It used to be the case that bytes were stored in the natural + * platform order, and the client had force them into big-endian order + * by calling gdev_mem_ensure_byte_order, but this no longer necessary. + * + * Note that we use type uint for register variables holding a chunk: + * for this reason, the chunk size cannot be larger than uint. + */ +/* Generic macros for chunk accessing. */ +#define cbytes(ct) size_of(ct) /* sizeof may be unsigned */ +# define chunk_bytes cbytes(chunk) +/* The clog2_bytes macro assumes that ints are 2, 4, or 8 bytes in size. */ +#define clog2_bytes(ct) (size_of(ct) == 8 ? 3 : size_of(ct)>>1) +# define chunk_log2_bytes clog2_bytes(chunk) +#define cbits(ct) (size_of(ct)*8) /* sizeof may be unsigned */ +# define chunk_bits cbits(chunk) +#define clog2_bits(ct) (clog2_bytes(ct)+3) +# define chunk_log2_bits clog2_bits(chunk) +#define cbit_mask(ct) (cbits(ct)-1) +# define chunk_bit_mask cbit_mask(chunk) +#define calign_bytes(ct)\ + (sizeof(ct) == 1 ? 1:\ + sizeof(ct) == sizeof(short) ? arch_align_short_mod :\ + sizeof(ct) == sizeof(int) ? arch_align_int_mod : arch_align_long_mod) +# define chunk_align_bytes calign_bytes(chunk) +#define calign_bit_mask(ct) (calign_bytes(ct)*8-1) +# define chunk_align_bit_mask calign_bit_mask(chunk) +/* + * The obvious definition for cmask is: + * #define cmask(ct) ((ct)~(ct)0) + * but this doesn't work on the VAX/VMS compiler, which fails to truncate + * the value to 16 bits when ct is ushort. + * Instead, we have to generate the mask with no extra 1-bits. + * We can't do this in the obvious way: + * #define cmask(ct) ((1 << (size_of(ct) * 8)) - 1) + * because some compilers won't allow a shift of the full type size. + * Instead, we have to do something really awkward: + */ +#define cmask(ct) ((ct) (((((ct)1 << (size_of(ct)*8-2)) - 1) << 2) + 3)) +# define chunk_all_bits cmask(chunk) +/* + * The obvious definition for chi_bits is: + * #define chi_bits(ct,n) (cmask(ct)-(cmask(ct)>>(n))) + * but this doesn't work on the DEC/MIPS compilers. + * Instead, we have to restrict chi_bits to only working for values of n + * between 0 and cbits(ct)-1, and use + */ +#define chi_bits(ct,n) (ct)(~(ct)1 << (cbits(ct)-1 - (n))) +# define chunk_hi_bits(n) chi_bits(chunk,n) + +/* Define whether this is a machine where chunks are long, */ +/* but the machine can't shift a long by its full width. */ +#define arch_cant_shift_full_chunk\ + (arch_is_big_endian && !arch_ints_are_short && !arch_can_shift_full_long) + +/* Pointer arithmetic macros. */ +#define inc_ptr(ptr,delta)\ + (ptr = (void *)((byte *)ptr + (delta))) + +/* Define macros for setting up left- and right-end masks. */ +/* These are used for monobit operations, and for filling */ +/* with 2- and 4-bit-per-pixel patterns. */ + +/* + * Define the chunk size for monobit copying operations. + */ +#if arch_is_big_endian +# define mono_copy_chunk uint +# define set_mono_right_mask(var, w)\ + (var = ((w) == chunk_bits ? chunk_all_bits : chunk_hi_bits(w))) +/* + * We have to split the following statement because of a bug in the Xenix C + * compiler (it produces a signed rather than an unsigned shift if we don't + * split). + */ +# define set_mono_thin_mask(var, w, bit)\ + set_mono_right_mask(var, w), var >>= (bit) +/* + * We have to split the following statement in two because of a bug + * in the DEC VAX/VMS C compiler. + */ +# define set_mono_left_mask(var, bit)\ + (var = chunk_all_bits, var >>= (bit)) +#else +# define mono_copy_chunk bits16 +extern const bits16 mono_copy_masks[17]; + +# if mono_fill_chunk_bytes == 2 +# define mono_fill_masks mono_copy_masks +# else +extern const bits32 mono_fill_masks[33]; + +# endif +/* + * We define mono_masks as either mono_fill_masks or + * mono_copy_masks before using the following macros. + */ +# define set_mono_left_mask(var, bit)\ + (var = mono_masks[bit]) +# define set_mono_thin_mask(var, w, bit)\ + (var = ~mono_masks[(w) + (bit)] & mono_masks[bit]) +# define set_mono_right_mask(var, ebit)\ + (var = ~mono_masks[ebit]) +#endif + +#endif /* gxbitops_INCLUDED */ diff --git a/gs/src/gxclrast.c b/gs/src/gxclrast.c new file mode 100644 index 000000000..ec0b1746d --- /dev/null +++ b/gs/src/gxclrast.c @@ -0,0 +1,2232 @@ +/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: gxclrast.c */ +/* Command list interpreter/rasterizer */ +#include "memory_.h" +#include "gx.h" +#include "gp.h" /* for gp_fmode_rb */ +#include "gpcheck.h" +#include "gserrors.h" +#include "gsbitops.h" +#include "gsparams.h" +#include "gsstate.h" /* (should only be imager state) */ +#include "gxdcolor.h" +#include "gxdevice.h" +#include "gscoord.h" /* requires gsmatrix.h */ +#include "gsdevice.h" /* for gs_deviceinitialmatrix */ +#include "gxdevmem.h" /* must precede gxcldev.h */ +#include "gxcldev.h" +#include "gxclpath.h" +#include "gxcmap.h" +#include "gxcspace.h" /* for gs_color_space_type */ +#include "gxgetbit.h" +#include "gxpaint.h" /* for gx_fill/stroke_params */ +#include "gxhttile.h" +#include "gdevht.h" +#include "gzpath.h" +#include "gzcpath.h" +#include "gzacpath.h" +#include "stream.h" +#include "strimpl.h" + +/* We need color space types for constructing temporary color spaces. */ +extern const gs_color_space_type gs_color_space_type_Indexed; + +/* Print a bitmap for tracing */ +#ifdef DEBUG +private void +cmd_print_bits(const byte * data, int width, int height, int raster) +{ + int i, j; + + dlprintf3("[L]width=%d, height=%d, raster=%d\n", + width, height, raster); + for (i = 0; i < height; i++) { + const byte *row = data + i * raster; + + dlprintf("[L]"); + for (j = 0; j < raster; j++) + dprintf1(" %02x", row[j]); + dputc('\n'); + } +} +#else +# define cmd_print_bits(data, width, height, raster) DO_NOTHING +#endif + +/* Get a variable-length integer operand. */ +#define cmd_getw(var, p)\ + BEGIN\ + if ( *p < 0x80 ) var = *p++;\ + else { const byte *_cbp; var = cmd_get_w(p, &_cbp); p = _cbp; }\ + END +private long +cmd_get_w(const byte * p, const byte ** rp) +{ + long val = *p++ & 0x7f; + int shift = 7; + + for (; val += (long)(*p & 0x7f) << shift, *p++ > 0x7f; shift += 7); + *rp = p; + return val; +} + +/* + * Define the structure for keeping track of the command reading buffer. + * + * The ptr member is not used, since normally we want it kept in a + * register. + */ +typedef struct command_buf_s { + byte *data; /* actual buffer, guaranteed aligned */ + uint size; + /*const byte *ptr;*/ /* next byte to be read (see above) */ + const byte *limit; /* refill warning point */ + const byte *end; /* byte just beyond valid data */ + stream *s; /* for refilling buffer */ + int end_status; +} command_buf_t; + +/* Set the end of a command buffer. */ +private void +set_cb_end(command_buf_t *pcb, const byte *end) +{ + pcb->end = end; + pcb->limit = pcb->data + (pcb->size - cmd_largest_size + 1); + if ( pcb->limit > pcb->end ) + pcb->limit = pcb->end; +} + +/* Read more data into a command buffer. */ +private const byte * +top_up_cbuf(command_buf_t *pcb, const byte *cbp) +{ + uint nread; + byte *cb_top = pcb->data + (pcb->end - cbp); + + memmove(pcb->data, cbp, pcb->end - cbp); + nread = pcb->end - cb_top; + pcb->end_status = sgets(pcb->s, cb_top, nread, &nread); + if ( nread == 0 ) { + /* No data for this band at all. */ + *cb_top = cmd_opv_end_run; + nread = 1; + } + set_cb_end(pcb, cb_top + nread); + process_interrupts(); + return pcb->data; +} + +/* Read data from the command buffer and stream. */ +private const byte * +cmd_read_data(command_buf_t *pcb, byte *ptr, uint rsize, const byte *cbp) +{ + if (pcb->end - cbp >= rsize) { + memcpy(ptr, cbp, rsize); + return cbp + rsize; + } else { + uint cleft = pcb->end - cbp; + uint rleft = rsize - cleft; + + memcpy(ptr, cbp, cleft); + sgets(pcb->s, ptr + cleft, rleft, &rleft); + return pcb->end; + } +} + +/* + * Render one band to a specified target device. Note that if + * action == setup, target may be 0. + */ +private const byte *cmd_read_rect(P3(int, gx_cmd_rect *, const byte *)); +private const byte *cmd_read_matrix(P2(gs_matrix *, const byte *)); +private void clist_unpack_short_bits(P5(byte *, const byte *, int, int, uint)); +private int cmd_select_map(P7(cmd_map_index, bool, gs_imager_state *, + gx_ht_order *, frac **, uint *, gs_memory_t *)); +private int cmd_resize_halftone(P3(gx_device_halftone *, uint, gs_memory_t *)); +private int cmd_install_ht_order(P3(gx_ht_order *, const gx_ht_order *, + gs_memory_t *)); +private int clist_decode_segment(P7(gx_path *, int, fixed[6], + gs_fixed_point *, int, int, + segment_notes)); +int +clist_playback_band(clist_playback_action playback_action, + gx_device_clist_reader *cdev, stream *s, + gx_device *target, int x0, int y0, gs_memory_t * mem) +{ + /* cbuf must be maximally aligned, but still be a byte *. */ + typedef union { void *p; double d; long l; } aligner_t; + aligner_t cbuf_storage[cbuf_size / sizeof(aligner_t) + 1]; + command_buf_t cbuf; + /* data_bits is for short copy_* bits and copy_* compressed, */ + /* must be aligned */ +#define data_bits_size cbuf_size + byte *data_bits; + register const byte *cbp; + int dev_depth = cdev->color_info.depth; + int dev_depth_bytes = (dev_depth + 7) >> 3; + gx_device *tdev; + gx_clist_state state; + gx_color_index *set_colors; + tile_slot *state_slot; + gx_strip_bitmap state_tile; /* parameters for reading tiles */ + tile_slot tile_bits; /* parameters of current tile */ + gs_int_point tile_phase; + gx_path path; + bool in_path; + gs_fixed_point ppos; + gx_clip_path clip_path; + bool use_clip; + gx_clip_path *pcpath; + gx_device_cpath_accum clip_accum; + gs_fixed_rect target_box; + struct _cas { + bool lop_enabled; + gs_fixed_point fill_adjust; + } clip_save; + gs_imager_state imager_state; + gx_device_color dev_color; + float dash_pattern[cmd_max_dash]; + gx_fill_params fill_params; + gx_stroke_params stroke_params; + gx_device_halftone dev_ht; + gs_halftone_type halftone_type; + gx_ht_order *porder; + uint ht_data_index; + gs_image_t image; + int image_num_planes; + gs_int_rect image_rect; + gs_color_space color_space; /* only used for indexed spaces */ + const gs_color_space *pcs; + gx_image_enum_common_t *image_info; + segment_notes notes; + int data_x; + int code = 0; + +#define cmd_get_value(var, cbp)\ + memcpy(&var, cbp, sizeof(var));\ + cbp += sizeof(var) +#define cmd_read(ptr, rsize, cbp)\ + cbp = cmd_read_data(&cbuf, ptr, rsize, cbp) +#define cmd_read_short_bits(ptr, bw, ht, ras, cbp)\ + cbp = cmd_read_data(&cbuf, ptr, (bw) * (ht), cbp);\ + clist_unpack_short_bits(ptr, ptr, bw, ht, ras) + + cbuf.data = (byte *)cbuf_storage; + cbuf.size = cbuf_size; + cbuf.s = s; + cbuf.end_status = 0; + set_cb_end(&cbuf, cbuf.data + cbuf.size); + cbp = cbuf.end; +in: /* Initialize for a new page. */ + tdev = target; + set_colors = state.colors; + use_clip = false; + pcpath = NULL; + notes = sn_none; + data_x = 0; + { + static const gx_clist_state cls_initial = { cls_initial_values }; + + state = cls_initial; + } + state_tile.id = gx_no_bitmap_id; + state_tile.shift = state_tile.rep_shift = 0; + tile_phase.x = tile_phase.y = 0; + gx_path_init_local(&path, mem); + in_path = false; + /* + * Initialize the clipping region to the full page. + * (Since we also initialize use_clip to false, this is arbitrary.) + */ + { + gs_fixed_rect cbox; + + gx_cpath_init_local(&clip_path, mem); + cbox.p.x = 0; + cbox.p.y = 0; + cbox.q.x = cdev->width; + cbox.q.y = cdev->height; + gx_cpath_from_rectangle(&clip_path, &cbox); + } + if (target != 0) + (*dev_proc(target, get_clipping_box))(target, &target_box); + imager_state = clist_imager_state_initial; + imager_state.line_params.dash.pattern = dash_pattern; + code = gs_imager_state_initialize(&imager_state, mem); + if (code < 0) + goto out; + imager_state.halftone = 0; /* never referenced */ + memset(&dev_ht, 0, sizeof(dev_ht)); + dev_ht.order.levels = 0; /* clear pointers explicitly, just in case */ + dev_ht.order.bits = 0; + dev_ht.order.transfer = 0; + dev_ht.components = 0; + imager_state.dev_ht = &dev_ht; + imager_state.ht_cache = 0; + if (tdev != 0) + gx_set_cmap_procs(&imager_state, tdev); + gx_imager_setscreenphase(&imager_state, -x0, -y0, gs_color_select_all); + halftone_type = ht_type_none; + fill_params.fill_zero_width = false; + pcs = gs_cspace_DeviceGray(&imager_state); + data_bits = gs_alloc_bytes(mem, data_bits_size, + "clist_playback_band(data_bits)"); + if (data_bits == 0) { + code = gs_note_error(gs_error_VMerror); + goto out; + } + while (code >= 0) { + int op; + int compress, depth, raster; + byte *source; + gx_color_index colors[2]; + gx_color_index *pcolor; + gs_logical_operation_t log_op; + tile_slot bits; /* parameters for reading bits */ + + /* Make sure the buffer contains a full command. */ + if (cbp >= cbuf.limit) { + if (cbuf.end_status < 0) { /* End of file or error. */ + if (cbp == cbuf.end) { + code = (cbuf.end_status == EOFC ? 0 : + gs_note_error(gs_error_ioerror)); + break; + } + } else { + cbp = top_up_cbuf(&cbuf, cbp); + } + } + op = *cbp++; +#ifdef DEBUG + if (gs_debug_c('L')) { + const char *const *sub = cmd_sub_op_names[op >> 4]; + + if (sub) + dlprintf1("[L]%s:", sub[op & 0xf]); + else + dlprintf2("[L]%s %d:", cmd_op_names[op >> 4], op & 0xf); + } +#endif + switch (op >> 4) { + case cmd_op_misc >> 4: + switch (op) { + case cmd_opv_end_run: + if_debug0('L', "\n"); + continue; + case cmd_opv_set_tile_size: + { + uint rep_width, rep_height; + byte bd = *cbp++; + + tile_bits.cb_depth = (bd & 31) + 1; + cmd_getw(rep_width, cbp); + cmd_getw(rep_height, cbp); + if (bd & 0x20) { + cmd_getw(tile_bits.x_reps, cbp); + tile_bits.width = + rep_width * tile_bits.x_reps; + } else { + tile_bits.x_reps = 1, + tile_bits.width = rep_width; + } + if (bd & 0x40) { + cmd_getw(tile_bits.y_reps, cbp); + tile_bits.height = + rep_height * tile_bits.y_reps; + } else { + tile_bits.y_reps = 1, + tile_bits.height = rep_height; + } + if (bd & 0x80) + cmd_getw(tile_bits.rep_shift, cbp); + else + tile_bits.rep_shift = 0; + if_debug6('L', " depth=%d size=(%d,%d), rep_size=(%d,%d), rep_shift=%d\n", + tile_bits.cb_depth, tile_bits.width, + tile_bits.height, rep_width, + rep_height, tile_bits.rep_shift); + tile_bits.shift = + (tile_bits.rep_shift == 0 ? 0 : + (tile_bits.rep_shift * + (tile_bits.height / rep_height)) + % rep_width); + tile_bits.cb_raster = + bitmap_raster(tile_bits.width * + tile_bits.cb_depth); + } + continue; + case cmd_opv_set_tile_phase: + cmd_getw(state.tile_phase.x, cbp); + cmd_getw(state.tile_phase.y, cbp); + if_debug2('L', " (%d,%d)\n", + state.tile_phase.x, + state.tile_phase.y); + goto set_phase; + case cmd_opv_set_tile_bits: + bits = tile_bits; + compress = 0; + stb:{ + uint rep_width = bits.width / bits.x_reps; + uint rep_height = bits.height / bits.y_reps; + uint index; + ulong offset; + uint width_bits = rep_width * bits.cb_depth; + uint width_bytes; + uint bytes = + clist_bitmap_bytes(width_bits, rep_height, + compress | + (rep_width < bits.width ? + decompress_spread : 0) | + decompress_elsewhere, + &width_bytes, + (uint *) & raster); + byte *data; + + cmd_getw(index, cbp); + cmd_getw(offset, cbp); + if_debug2('L', " index=%d offset=%lu\n", + state.tile_index, offset); + state.tile_index = index; + cdev->tile_table[state.tile_index].offset = + offset; + state_slot = + (tile_slot *) (cdev->chunk.data + offset); + *state_slot = bits; + state_tile.data = data = + (byte *) (state_slot + 1); +#ifdef DEBUG + state_slot->index = state.tile_index; +#endif + if (compress) { /* Decompress the image data. */ + /* We'd like to share this code */ + /* with the similar code in copy_*, */ + /* but right now we don't see how. */ + stream_cursor_read r; + stream_cursor_write w; + + /* We don't know the data length a */ + /* priori, so to be conservative, */ + /* we read the uncompressed size. */ + uint cleft = cbuf.end - cbp; + + if (cleft < bytes) { + uint nread = cbuf_size - cleft; + + memmove(cbuf.data, cbp, cleft); + cbuf.end_status = sgets(s, cbuf.data + cleft, nread, &nread); + set_cb_end(&cbuf, cbuf.data + cleft + nread); + cbp = cbuf.data; + } + r.ptr = cbp - 1; + r.limit = cbuf.end - 1; + w.ptr = data - 1; + w.limit = w.ptr + bytes; + switch (compress) { + case cmd_compress_rle: + { + stream_RLD_state sstate; + + clist_rld_init(&sstate); + (*s_RLD_template.process) + ((stream_state *) & sstate, &r, &w, true); + } + break; + case cmd_compress_cfe: + { + stream_CFD_state sstate; + + clist_cfd_init(&sstate, + width_bytes << 3 /*width_bits */ , + rep_height, mem); + (*s_CFD_template.process) + ((stream_state *) & sstate, &r, &w, true); + (*s_CFD_template.release) + ((stream_state *) & sstate); + } + break; + default: + goto bad_op; + } + cbp = r.ptr + 1; + } else if (rep_height > 1 && + width_bytes != bits.cb_raster + ) { + cmd_read_short_bits(data, width_bytes, + rep_height, bits.cb_raster, cbp); + } else { + cmd_read(data, bytes, cbp); + } + if (bits.width > rep_width) + bits_replicate_horizontally(data, + rep_width * bits.cb_depth, rep_height, + bits.cb_raster, + bits.width * bits.cb_depth, + bits.cb_raster); + if (bits.height > rep_height) + bits_replicate_vertically(data, + rep_height, bits.cb_raster, + bits.height); +#ifdef DEBUG + if (gs_debug_c('L')) + cmd_print_bits(data, bits.width, + bits.height, + bits.cb_raster); +#endif + } + goto stp; + case cmd_opv_set_bits: + compress = *cbp & 3; + bits.cb_depth = *cbp++ >> 2; + cmd_getw(bits.width, cbp); + cmd_getw(bits.height, cbp); + if_debug4('L', " compress=%d depth=%d size=(%d,%d)", + compress, bits.cb_depth, + bits.width, bits.height); + bits.cb_raster = + bitmap_raster(bits.width * bits.cb_depth); + bits.x_reps = bits.y_reps = 1; + bits.shift = bits.rep_shift = 0; + goto stb; + case cmd_opv_set_tile_color: + set_colors = state.tile_colors; + if_debug0('L', "\n"); + continue; + case cmd_opv_set_misc: + { + uint cb = *cbp++; + + switch (cb >> 6) { + case cmd_set_misc_lop >> 6: + cmd_getw(state.lop, cbp); + state.lop = (state.lop << 6) + (cb & 0x3f); + if_debug1('L', " lop=0x%x\n", state.lop); + if (state.lop_enabled) + imager_state.log_op = state.lop; + break; + case cmd_set_misc_data_x >> 6: + if (cb & 0x20) + cmd_getw(data_x, cbp); + else + data_x = 0; + data_x = (data_x << 5) + (cb & 0x1f); + if_debug1('L', " data_x=%d\n", data_x); + break; + case cmd_set_misc_map >> 6: + { + frac *mdata; + uint count; + + code = cmd_select_map(cb & 0x1f, + cb & 0x20, + &imager_state, + porder, &mdata, + &count, mem); + + if (code < 0) + goto out; + if (mdata) { + cmd_read((byte *) mdata, count, cbp); +#ifdef DEBUG + if (gs_debug_c('L')) { + uint i; + + for (i = 0; i < count / sizeof(*mdata); ++i) + dprintf1(" 0x%04x", mdata[i]); + dputc('\n'); + } + } else { + if_debug0('L', " none\n"); +#endif + } + } + /* Recompute the effective transfer, */ + /* in case this was a transfer map. */ + gx_imager_set_effective_xfer( + &imager_state); + break; + case cmd_set_misc_halftone >> 6: + halftone_type = cb & 0x3f; + { + uint num_comp; + + cmd_getw(num_comp, cbp); + if_debug2('L', " halftone type=%d num_comp=%u\n", + halftone_type, num_comp); + code = cmd_resize_halftone(&dev_ht, + num_comp, mem); + if (code < 0) + goto out; + } + break; + default: + goto bad_op; + } + } + continue; + case cmd_opv_enable_lop: + state.lop_enabled = true; + imager_state.log_op = state.lop; + if_debug0('L', "\n"); + continue; + case cmd_opv_disable_lop: + state.lop_enabled = false; + imager_state.log_op = lop_default; + if_debug0('L', "\n"); + continue; + case cmd_opv_set_ht_order: + { + int index; + gx_ht_order order; + + cmd_getw(index, cbp); + if (index == 0) + porder = &dev_ht.order; + else { + gx_ht_order_component *pcomp = + &dev_ht.components[index - 1]; + + cmd_getw(pcomp->cname, cbp); + if_debug1('L', " cname=%lu", + (ulong) pcomp->cname); + porder = &pcomp->corder; + } + order = *porder; + cmd_getw(order.width, cbp); + cmd_getw(order.height, cbp); + cmd_getw(order.raster, cbp); + cmd_getw(order.shift, cbp); + cmd_getw(order.num_levels, cbp); + cmd_getw(order.num_bits, cbp); + if_debug7('L', " index=%d size=(%d,%d) raster=%d shift=%d num_levels=%d num_bits=%d\n", + index, order.width, order.height, + order.raster, order.shift, + order.num_levels, order.num_bits); + code = + cmd_install_ht_order(porder, &order, mem); + if (code < 0) + goto out; + } + ht_data_index = 0; + continue; + case cmd_opv_set_ht_data: + { + int n = *cbp++; + + if (ht_data_index < porder->num_levels) { /* Setting levels */ + byte *lptr = (byte *) + (porder->levels + ht_data_index); + + cmd_read(lptr, n * sizeof(*porder->levels), + cbp); +#ifdef DEBUG + if (gs_debug_c('L')) { + int i; + + dprintf1(" levels[%u]", ht_data_index); + for (i = 0; i < n; ++i) + dprintf1(" %u", + porder->levels[ht_data_index + i]); + dputc('\n'); + } +#endif + } else { /* Setting bits */ + byte *bptr = (byte *) + (porder->bits + + (ht_data_index - porder->num_levels)); + + cmd_read(bptr, n * sizeof(*porder->bits), + cbp); +#ifdef DEBUG + if (gs_debug_c('L')) { + int i; + + dprintf1(" bits[%u]", ht_data_index - porder->num_levels); + for (i = 0; i < n; ++i) { + const gx_ht_bit *pb = + &porder->bits[ht_data_index - porder->num_levels + i]; + + dprintf2(" (%u,0x%lx)", + pb->offset, + (ulong) pb->mask); + } + dputc('\n'); + } +#endif + } + ht_data_index += n; + } + /* If this is the end of the data, */ + /* install the (device) halftone. */ + if (porder == + (dev_ht.components != 0 ? + &dev_ht.components[0].corder : + &dev_ht.order) && + ht_data_index == porder->num_levels + + porder->num_bits + ) { /* Make sure we have a halftone cache. */ + uint i; + + if (imager_state.ht_cache == 0) { + gx_ht_cache *pcache = + gx_ht_alloc_cache(mem, + porder->num_levels + 2, + gx_ht_cache_default_bits()); + + if (pcache == 0) { + code = gs_note_error(gs_error_VMerror); + goto out; + } + imager_state.ht_cache = pcache; + } + for (i = 1; i < dev_ht.num_comp; ++i) { + gx_ht_order *pco = + &dev_ht.components[i].corder; + + if (!pco->cache) { + gx_ht_cache *pcache = + gx_ht_alloc_cache(mem, 1, + pco->raster * (pco->num_bits / + pco->width)); + + if (pcache == 0) { + code = gs_note_error(gs_error_VMerror); + goto out; + } + pco->cache = pcache; + gx_ht_init_cache(pcache, pco); + } + } + if (dev_ht.num_comp) { + dev_ht.components[0].corder.cache = + imager_state.ht_cache; + dev_ht.order = + dev_ht.components[0].corder; + } + gx_imager_dev_ht_install(&imager_state, + &dev_ht, halftone_type, + (const gx_device *)cdev); + } + continue; + case cmd_opv_end_page: + if_debug0('L', "\n"); + /* + * Do end-of-page cleanup, then reinitialize if + * there are more pages to come. + */ + goto out; + case cmd_opv_delta2_color0: + pcolor = &set_colors[0]; + goto delta2_c; + case cmd_opv_delta2_color1: + pcolor = &set_colors[1]; + delta2_c:set_colors = state.colors; + { + gx_color_index b = ((uint) * cbp << 8) + cbp[1]; + + cbp += 2; + if (dev_depth > 24) + *pcolor += + ((b & 0xf000) << 12) + ((b & 0x0f00) << 8) + + ((b & 0x00f0) << 4) + (b & 0x000f) - + cmd_delta2_32_bias; + else + *pcolor += + ((b & 0xf800) << 5) + ((b & 0x07e0) << 3) + + (b & 0x001f) - cmd_delta2_24_bias; + } + if_debug1('L', " 0x%lx\n", *pcolor); + continue; + case cmd_opv_set_copy_color: + state.color_is_alpha = 0; + if_debug0('L', "\n"); + continue; + case cmd_opv_set_copy_alpha: + state.color_is_alpha = 1; + if_debug0('L', "\n"); + continue; + default: + goto bad_op; + } + /*NOTREACHED */ + case cmd_op_set_color0 >> 4: + pcolor = &set_colors[0]; + goto set_color; + case cmd_op_set_color1 >> 4: + pcolor = &set_colors[1]; + set_color:set_colors = state.colors; + switch (op & 0xf) { + case 0: + break; + case 15: /* special handling because this may */ + /* require more bits than depth */ + *pcolor = gx_no_color_index; + goto setc; + default: + switch (dev_depth_bytes) { + case 4: + { + gx_color_index b = + ((gx_color_index) (op & 0xf) << 8) + *cbp++; + + *pcolor += + ((b & 07000) << 15) + ((b & 0700) << 10) + + ((b & 070) << 5) + (b & 7) - + cmd_delta1_32_bias; + goto setc; + } + case 3: + { + gx_color_index b = *cbp++; + + *pcolor += + ((gx_color_index) (op & 0xf) << 16) + + ((b & 0xf0) << 4) + (b & 0x0f) - + cmd_delta1_24_bias; + goto setc; + } + case 2: + break; + case 1: + *pcolor += (gx_color_index) (op & 0xf) - 8; + goto setc; + } + } + { + gx_color_index color = 0; + + switch (dev_depth_bytes) { + case 4: + color |= (gx_color_index) * cbp++ << 24; + case 3: + color |= (gx_color_index) * cbp++ << 16; + case 2: + color |= (gx_color_index) * cbp++ << 8; + case 1: + color |= (gx_color_index) * cbp++; + } + *pcolor = color; + } + setc:if_debug1('L', " 0x%lx\n", *pcolor); + continue; + case cmd_op_fill_rect >> 4: + case cmd_op_tile_rect >> 4: + cbp = cmd_read_rect(op, &state.rect, cbp); + break; + case cmd_op_fill_rect_short >> 4: + case cmd_op_tile_rect_short >> 4: + state.rect.x += *cbp + cmd_min_short; + state.rect.width += cbp[1] + cmd_min_short; + if (op & 0xf) { + state.rect.height += (op & 0xf) + cmd_min_dxy_tiny; + cbp += 2; + } else { + state.rect.y += cbp[2] + cmd_min_short; + state.rect.height += cbp[3] + cmd_min_short; + cbp += 4; + } + break; + case cmd_op_fill_rect_tiny >> 4: + case cmd_op_tile_rect_tiny >> 4: + if (op & 8) + state.rect.x += state.rect.width; + else { + int txy = *cbp++; + + state.rect.x += (txy >> 4) + cmd_min_dxy_tiny; + state.rect.y += (txy & 0xf) + cmd_min_dxy_tiny; + } + state.rect.width += (op & 7) + cmd_min_dw_tiny; + break; + case cmd_op_copy_mono >> 4: + depth = 1; + goto copy; + case cmd_op_copy_color_alpha >> 4: + if (state.color_is_alpha) { + if (!(op & 8)) + depth = *cbp++; + } else + depth = dev_depth; + copy:cmd_getw(state.rect.x, cbp); + cmd_getw(state.rect.y, cbp); + if (op & 8) { /* Use the current "tile". */ +#ifdef DEBUG + if (state_slot->index != state.tile_index) { + lprintf2("state_slot->index = %d, state.tile_index = %d!\n", + state_slot->index, + state.tile_index); + code = gs_note_error(gs_error_ioerror); + goto out; + } +#endif + depth = state_slot->cb_depth; + state.rect.width = state_slot->width; + state.rect.height = state_slot->height; + raster = state_slot->cb_raster; + source = (byte *) (state_slot + 1); + } else { /* Read width, height, bits. */ + /* depth was set already. */ + uint width_bits, width_bytes; + uint bytes; + + cmd_getw(state.rect.width, cbp); + cmd_getw(state.rect.height, cbp); + width_bits = state.rect.width * depth; + bytes = + clist_bitmap_bytes(width_bits, + state.rect.height, + op & 3, &width_bytes, + (uint *) & raster); + /* copy_mono and copy_color/alpha */ + /* ensure that the bits will fit in a single buffer, */ + /* even after decompression if compressed. */ +#ifdef DEBUG + if (bytes > cbuf_size) { + lprintf6("bitmap size exceeds buffer! width=%d raster=%d height=%d\n file pos %ld buf pos %d/%d\n", + state.rect.width, raster, + state.rect.height, + stell(s), (int)(cbp - cbuf.data), + (int)(cbuf.end - cbuf.data)); + code = gs_note_error(gs_error_ioerror); + goto out; + } +#endif + if (op & 3) { /* Decompress the image data. */ + stream_cursor_read r; + stream_cursor_write w; + + /* We don't know the data length a priori, */ + /* so to be conservative, we read */ + /* the uncompressed size. */ + uint cleft = cbuf.end - cbp; + + if (cleft < bytes) { + uint nread = cbuf_size - cleft; + + memmove(cbuf.data, cbp, cleft); + cbuf.end_status = sgets(s, cbuf.data + cleft, nread, &nread); + set_cb_end(&cbuf, cbuf.data + cleft + nread); + cbp = cbuf.data; + } + r.ptr = cbp - 1; + r.limit = cbuf.end - 1; + w.ptr = data_bits - 1; + w.limit = w.ptr + data_bits_size; + switch (op & 3) { + case cmd_compress_rle: + { + stream_RLD_state sstate; + + clist_rld_init(&sstate); + /* The process procedure can't fail. */ + (*s_RLD_template.process) + ((stream_state *) & sstate, &r, &w, true); + } + break; + case cmd_compress_cfe: + { + stream_CFD_state sstate; + + clist_cfd_init(&sstate, + width_bytes << 3 /*state.rect.width */ , + state.rect.height, mem); + /* The process procedure can't fail. */ + (*s_CFD_template.process) + ((stream_state *) & sstate, &r, &w, true); + (*s_CFD_template.release) + ((stream_state *) & sstate); + } + break; + default: + goto bad_op; + } + cbp = r.ptr + 1; + source = data_bits; + } else if (state.rect.height > 1 && + width_bytes != raster + ) { + source = data_bits; + cmd_read_short_bits(source, width_bytes, + state.rect.height, + raster, cbp); + } else { + cmd_read(cbuf.data, bytes, cbp); + source = cbuf.data; + } +#ifdef DEBUG + if (gs_debug_c('L')) { + dprintf2(" depth=%d, data_x=%d\n", + depth, data_x); + cmd_print_bits(source, state.rect.width, + state.rect.height, raster); + } +#endif + } + break; + case cmd_op_delta_tile_index >> 4: + state.tile_index += (int)(op & 0xf) - 8; + goto sti; + case cmd_op_set_tile_index >> 4: + state.tile_index = + ((op & 0xf) << 8) + *cbp++; + sti:state_slot = + (tile_slot *) (cdev->chunk.data + + cdev->tile_table[state.tile_index].offset); + if_debug2('L', " index=%u offset=%lu\n", + state.tile_index, + cdev->tile_table[state.tile_index].offset); + state_tile.data = (byte *) (state_slot + 1); + stp:state_tile.size.x = state_slot->width; + state_tile.size.y = state_slot->height; + state_tile.raster = state_slot->cb_raster; + state_tile.rep_width = state_tile.size.x / + state_slot->x_reps; + state_tile.rep_height = state_tile.size.y / + state_slot->y_reps; + state_tile.rep_shift = state_slot->rep_shift; + state_tile.shift = state_slot->shift; + set_phase:tile_phase.x = + (state.tile_phase.x + x0) % state_tile.size.x; + /* + * The true tile height for shifted tiles is not + * size.y: see gxbitmap.h for the computation. + */ + { + int full_height; + + if (state_tile.shift == 0) + full_height = state_tile.size.y; + else + full_height = state_tile.rep_height * + (state_tile.rep_width / + igcd(state_tile.rep_shift, + state_tile.rep_width)); + tile_phase.y = + (state.tile_phase.y + y0) % full_height; + } + gx_imager_setscreenphase(&imager_state, + -(state.tile_phase.x + x0), + -(state.tile_phase.y + y0), + gs_color_select_all); + continue; + case cmd_op_misc2 >> 4: + switch (op) { + case cmd_opv_set_flatness: + cmd_get_value(imager_state.flatness, cbp); + if_debug1('L', " %g\n", imager_state.flatness); + continue; + case cmd_opv_set_fill_adjust: + cmd_get_value(imager_state.fill_adjust.x, cbp); + cmd_get_value(imager_state.fill_adjust.y, cbp); + if_debug2('L', " (%g,%g)\n", + fixed2float(imager_state.fill_adjust.x), + fixed2float(imager_state.fill_adjust.y)); + continue; + case cmd_opv_set_ctm: + { + gs_matrix mat; + + cbp = cmd_read_matrix(&mat, cbp); + mat.tx -= x0; + mat.ty -= y0; + gs_imager_setmatrix(&imager_state, &mat); + if_debug6('L', " [%g %g %g %g %g %g]\n", + mat.xx, mat.xy, mat.yx, mat.yy, + mat.tx, mat.ty); + } + continue; + case cmd_opv_set_line_width: + { + float width; + + cmd_get_value(width, cbp); + if_debug1('L', " %g\n", width); + gx_set_line_width(&imager_state.line_params, width); + } + continue; + case cmd_opv_set_misc2: + { + uint cb = *cbp; + + switch (cb >> 6) { + case cmd_set_misc2_cap_join >> 6: + imager_state.line_params.cap = + (gs_line_cap) ((cb >> 3) & 7); + imager_state.line_params.join = + (gs_line_join) (cb & 7); + if_debug2('L', " cap=%d join=%d\n", + imager_state.line_params.cap, + imager_state.line_params.join); + break; + case cmd_set_misc2_ac_op_sa >> 6: + imager_state.accurate_curves = + (cb & 4) != 0; + imager_state.overprint = (cb & 2) != 0; + imager_state.stroke_adjust = cb & 1; + if_debug3('L', " AC=%d OP=%d SA=%d\n", + imager_state.accurate_curves, + imager_state.overprint, + imager_state.stroke_adjust); + break; + case cmd_set_misc2_notes >> 6: + notes = (segment_notes) (cb & 0x3f); + if_debug1('L', " notes=%d\n", notes); + break; + case cmd_set_misc2_alpha >> 6: + memcpy(&imager_state.alpha, cbp + 1, + sizeof(imager_state.alpha)); + cbp += sizeof(imager_state.alpha); + break; + default: + goto bad_op; + } + } + cbp++; + continue; + case cmd_opv_set_miter_limit: + { + float limit; + + cmd_get_value(limit, cbp); + if_debug1('L', " %g\n", limit); + gx_set_miter_limit(&imager_state.line_params, limit); + } + continue; + case cmd_opv_set_dash: + { + int nb = *cbp++; + int n = nb & 0x3f; + float dot_length, offset; + + cmd_get_value(dot_length, cbp); + cmd_get_value(offset, cbp); + memcpy(dash_pattern, cbp, n * sizeof(float)); + + gx_set_dash(&imager_state.line_params.dash, + dash_pattern, n, offset, + NULL); + gx_set_dash_adapt(&imager_state.line_params.dash, + (nb & 0x80) != 0); + gx_set_dot_length(&imager_state.line_params, + dot_length, + (nb & 0x40) != 0); +#ifdef DEBUG + if (gs_debug_c('L')) { + int i; + + dprintf4(" dot=%g(mode %d) adapt=%d offset=%g [", + dot_length, + (nb & 0x40) != 0, + (nb & 0x80) != 0, offset); + for (i = 0; i < n; ++i) + dprintf1("%g ", dash_pattern[i]); + dputs("]\n"); + } +#endif + cbp += n * sizeof(float); + } + break; + case cmd_opv_enable_clip: + pcpath = (use_clip ? &clip_path : NULL); + if_debug0('L', "\n"); + break; + case cmd_opv_disable_clip: + pcpath = NULL; + if_debug0('L', "\n"); + break; + case cmd_opv_begin_clip: + pcpath = NULL; + if_debug0('L', "\n"); + code = gx_cpath_reset(&clip_path); + if (code < 0) + goto out; + gx_cpath_accum_begin(&clip_accum, mem); + gx_cpath_accum_set_cbox(&clip_accum, + &target_box); + tdev = (gx_device *) & clip_accum; + clip_save.lop_enabled = state.lop_enabled; + clip_save.fill_adjust = + imager_state.fill_adjust; + state.lop_enabled = false; + imager_state.log_op = lop_default; + imager_state.fill_adjust.x = + imager_state.fill_adjust.y = fixed_half; + break; + case cmd_opv_end_clip: + if_debug0('L', "\n"); + gx_cpath_accum_end(&clip_accum, &clip_path); + gx_cpath_set_outside(&clip_path, *cbp++); + tdev = target; + /* + * If the entire band falls within the clip + * path, no clipping is needed. + */ + { + gs_fixed_rect cbox; + + gx_cpath_inner_box(&clip_path, &cbox); + use_clip = + !(cbox.p.x <= target_box.p.x && + cbox.q.x >= target_box.q.x && + cbox.p.y <= target_box.p.y && + cbox.q.y >= target_box.q.y); + } + pcpath = (use_clip ? &clip_path : NULL); + state.lop_enabled = clip_save.lop_enabled; + imager_state.log_op = + (state.lop_enabled ? state.lop : + lop_default); + imager_state.fill_adjust = + clip_save.fill_adjust; + break; + case cmd_opv_set_color_space: + { + byte b = *cbp++; + int index = b >> 4; + + if_debug2('L', " %d%s\n", index, + (b & 8 ? " (indexed)" : "")); + switch (index) { + case gs_color_space_index_DeviceGray: + pcs = gs_cspace_DeviceGray(&imager_state); + break; + case gs_color_space_index_DeviceRGB: + pcs = gs_cspace_DeviceRGB(&imager_state); + break; + case gs_color_space_index_DeviceCMYK: + pcs = gs_cspace_DeviceCMYK(&imager_state); + break; + default: + goto bad_op; /* others are NYI */ + } + if (b & 8) { + int num_comp = + gs_color_space_num_components(pcs); + + color_space.type = &gs_color_space_type_Indexed; + color_space.params.indexed.base_space.type = pcs->type; + cmd_getw(color_space.params.indexed.hival, + cbp); + color_space.params.indexed.use_proc = + (b & 4) != 0; +/****** SET map ******/ + pcs = &color_space; + } + } + break; + case cmd_opv_begin_image: + { + byte b = *cbp++; + int bpci = b >> 5; + static const byte bpc[6] = + {1, 1, 2, 4, 8, 12}; + gx_drawing_color devc; + int num_components; + gs_image_format_t format; + + if (bpci == 0) + gs_image_t_init_mask(&image, false); + else + gs_image_t_init(&image, pcs); + if (b & (1 << 4)) { + byte b2 = *cbp++; + + format = b2 >> 6; + image.Interpolate = (b2 & (1 << 5)) != 0; + image.Alpha = + (gs_image_alpha_t) ((b2 >> 3) & 3); + } else { + format = gs_image_format_chunky; + } + cmd_getw(image.Width, cbp); + cmd_getw(image.Height, cbp); + if_debug4('L', " BPCi=%d I=%d size=(%d,%d)", + bpci, (b & 0x10) != 0, + image.Width, image.Height); + if (b & (1 << 3)) { /* Non-standard ImageMatrix */ + cbp = cmd_read_matrix( + &image.ImageMatrix, cbp); + if_debug6('L', " matrix=[%g %g %g %g %g %g]", + image.ImageMatrix.xx, + image.ImageMatrix.xy, + image.ImageMatrix.yx, + image.ImageMatrix.yy, + image.ImageMatrix.tx, + image.ImageMatrix.ty); + } else { + image.ImageMatrix.xx = image.Width; + image.ImageMatrix.xy = 0; + image.ImageMatrix.yx = 0; + image.ImageMatrix.yy = -image.Height; + image.ImageMatrix.tx = 0; + image.ImageMatrix.ty = image.Height; + } + image.BitsPerComponent = bpc[bpci]; + if (bpci == 0) { + num_components = 1; + } else { + image.ColorSpace = pcs; + if (gs_color_space_get_index(pcs) == gs_color_space_index_Indexed) { + image.Decode[0] = 0; + image.Decode[1] = + (1 << image.BitsPerComponent) - 1; + } else { + static const float decode01[] = + { + 0, 1, 0, 1, 0, 1, 0, 1, 0, 1 + }; + + memcpy(image.Decode, decode01, + sizeof(image.Decode)); + } + num_components = + gs_color_space_num_components(pcs); + } + switch (format) { + case gs_image_format_chunky: + image_num_planes = 1; + break; + case gs_image_format_component_planar: + image_num_planes = num_components; + break; + case gs_image_format_bit_planar: + image_num_planes = num_components * + image.BitsPerComponent; + break; + default: + goto bad_op; + } + if (b & (1 << 2)) { /* Non-standard Decode */ + byte dflags = *cbp++; + int i; + + for (i = 0; i < num_components * 2; + dflags <<= 2, i += 2 + ) + switch ((dflags >> 6) & 3) { + case 0: /* default */ + break; + case 1: /* swapped default */ + image.Decode[i] = + image.Decode[i + 1]; + image.Decode[i + 1] = 0; + break; + case 3: + cmd_get_value(image.Decode[i], + cbp); + /* falls through */ + case 2: + cmd_get_value(image.Decode[i + 1], + cbp); + } +#ifdef DEBUG + if (gs_debug_c('L')) { + dputs(" decode=["); + for (i = 0; i < num_components * 2; + ++i + ) + dprintf1("%g ", image.Decode[i]); + dputc(']'); + } +#endif + } + image.adjust = false; + if (b & (1 << 1)) { + if (image.ImageMask) + image.adjust = true; + else + image.CombineWithColor = true; + if_debug1('L', " %s", + (image.ImageMask ? " adjust" : + " CWC")); + } + if (b & (1 << 0)) { /* Non-standard rectangle */ + uint diff; + + cmd_getw(image_rect.p.x, cbp); + cmd_getw(image_rect.p.y, cbp); + cmd_getw(diff, cbp); + image_rect.q.x = image.Width - diff; + cmd_getw(diff, cbp); + image_rect.q.y = image.Height - diff; + if_debug4('L', " rect=(%d,%d),(%d,%d)", + image_rect.p.x, image_rect.p.y, + image_rect.q.x, image_rect.q.y); + } else { + image_rect.p.x = 0; + image_rect.p.y = 0; + image_rect.q.x = image.Width; + image_rect.q.y = image.Height; + } + if_debug0('L', "\n"); + color_set_pure(&devc, state.colors[1]); + code = (*dev_proc(tdev, begin_image)) + (tdev, &imager_state, &image, format, + &image_rect, &devc, pcpath, mem, + &image_info); + if (code < 0) + goto out; + } + break; + case cmd_opv_image_data: + { + uint height; + + cmd_getw(height, cbp); + if (height == 0) { + if_debug0('L', " done image\n"); + code = gx_device_end_image(tdev, + image_info, true); + } else { + uint bytes_per_plane, nbytes; + const byte *data; + byte *data_on_heap = 0; + const byte *planes[64]; + +/****** DOESN'T HANDLE #PLANES YET *****/ + + cmd_getw(bytes_per_plane, cbp); + if_debug2('L', " height=%u raster=%u\n", + height, bytes_per_plane); + nbytes = bytes_per_plane * + image_num_planes * height; + if ( cbuf.end - cbp < nbytes) + cbp = top_up_cbuf(&cbuf, cbp); + if (cbuf.end - cbp >= nbytes) { + data = cbp; + cbp += nbytes; + } else { + uint cleft = cbuf.end - cbp; + uint rleft = nbytes - cleft; + byte *rdata; + + if (nbytes > cbuf.end - cbuf.data) { /* Allocate a separate buffer. */ + rdata = data_on_heap = + gs_alloc_bytes(mem, nbytes, + "clist image_data"); + if (rdata == 0) { + code = gs_note_error(gs_error_VMerror); + goto out; + } + } else + rdata = cbuf.data; + memmove(rdata, cbp, cleft); + sgets(s, rdata + cleft, rleft, + &rleft); + data = rdata; + cbp = cbuf.end; /* force refill */ + } +#ifdef DEBUG + if (gs_debug_c('L')) + cmd_print_bits(data, image_rect.q.x - + image_rect.p.x, + image_num_planes * height, + bytes_per_plane); +#endif + { + int plane; + + for (plane = 0; + plane < image_num_planes; + ++plane + ) + planes[plane] = data + + bytes_per_plane * height * plane; + } + code = gx_device_image_data(tdev, + image_info, planes, data_x, + bytes_per_plane, height); + if (data_on_heap) + gs_free_object(mem, data_on_heap, + "clist image_data"); + data_x = 0; + } + } + if (code < 0) + goto out; + continue; + case cmd_opv_set_color: + { +#define dcb dev_color.colors.colored.c_base +#define dcl dev_color.colors.colored.c_level + byte b = *cbp++; + int i; + + switch (b >> 4) { + case 0: + dcb[0] = (b >> 3) & 1; + dcb[1] = (b >> 2) & 1; + dcb[2] = (b >> 1) & 1; + dcb[3] = b & 1; + break; + case 1: + dcb[0] = ((b & 0xf) << 1) + (*cbp >> 7); + dcb[1] = (*cbp >> 2) & 0x1f; + dcb[2] = ((*cbp & 3) << 3) + (cbp[1] >> 5); + dcb[3] = cbp[1] & 0x1f; + cbp += 2; + break; + default: + goto bad_op; + } + for (i = 0; i < imager_state.dev_ht->num_comp; ++i) + cmd_getw(dcl[i], cbp); + if_debug10('L', " format %d num_comp=%d base=(%u,%u,%u,%u) level=(%u,%u,%u,%u)\n", + b >> 4, + imager_state.dev_ht->num_comp, + dcb[0], dcb[1], dcb[2], dcb[3], + dcl[0], dcl[1], dcl[2], dcl[3]); + color_finish_set_cmyk_halftone(&dev_color, + imager_state.dev_ht); +#undef dcb +#undef dcl + } + continue; + case cmd_opv_put_params: { + gs_c_param_list param_list; + uint cleft; + uint rleft; + bool alloc_data_on_heap = false; + byte *param_buf; + uint param_length; + + cmd_get_value(param_length, cbp); + if_debug1('L', " length=%d\n", param_length); + code = 0; + if (param_length == 0) + break; + + /* Make sure entire serialized param list is in cbuf */ + /* + force void* alignment */ + cbp = top_up_cbuf(&cbuf, cbp); + if (cbuf.end - cbp >= param_length) { + param_buf = (byte *)cbp; + cbp += param_length; + } else { + /* NOTE: param_buf must be maximally aligned */ + param_buf = gs_alloc_bytes(mem, param_length, + "clist put_params"); + if (param_buf == 0) { + code = gs_note_error(gs_error_VMerror); + goto out; + } + alloc_data_on_heap = true; + cleft = cbuf.end - cbp; + rleft = param_length - cleft; + memmove(param_buf, cbp, cleft); + sgets(s, param_buf + cleft, rleft, &rleft); + cbp = cbuf.end; /* force refill */ + } + + /* + * Create a gs_c_param_list & expand into it. + * NB that gs_c_param_list doesn't copy objects into + * it, but rather keeps *pointers* to what's passed. + * That's OK because the serialized format keeps enough + * space to hold expanded versions of the structures, + * but this means we cannot deallocate source buffer + * until the gs_c_param_list is deleted. + */ + gs_c_param_list_write(¶m_list, mem); + code = gs_param_list_unserialize + ( (gs_param_list *)¶m_list, param_buf ); + if (code >= 0 && code != param_length) + code = gs_error_unknownerror; /* must match */ + if (code >= 0) { + gs_c_param_list_read(¶m_list); + code = (*dev_proc(cdev, put_params)) + ((gx_device *)cdev, + (gs_param_list *)¶m_list); + } + gs_c_param_list_release(¶m_list); + if (alloc_data_on_heap) + gs_free_object(mem, param_buf, "clist put_params"); + if (code < 0) + goto out; + if (playback_action == playback_action_setup) + goto out; + } + break; + default: + goto bad_op; + } + continue; + case cmd_op_segment >> 4: + { + fixed vs[6]; + int i, code; + + if (!in_path) { + ppos.x = int2fixed(state.rect.x); + ppos.y = int2fixed(state.rect.y); + if_debug2('L', " (%d,%d)", state.rect.x, + state.rect.y); + notes = sn_none; + in_path = true; + } + for (i = 0; + i < clist_segment_op_num_operands[op & 0xf]; + ++i + ) { + fixed v; + int b = *cbp; + + switch (b >> 5) { + case 0: + case 1: + vs[i++] = + ((fixed) ((b ^ 0x20) - 0x20) << 13) + + ((int)cbp[1] << 5) + (cbp[2] >> 3); + if_debug1('L', " %g", fixed2float(vs[i - 1])); + cbp += 2; + v = (int)((*cbp & 7) ^ 4) - 4; + break; + case 2: + case 3: + v = (b ^ 0x60) - 0x20; + break; + case 4: + case 5: + /* + * Without the following cast, C's + * brain-damaged coercion rules cause the + * result to be considered unsigned, and not + * sign-extended on machines where + * sizeof(long) > sizeof(int). + */ + v = (((b ^ 0xa0) - 0x20) << 8) + (int)*++cbp; + break; + case 6: + v = (b ^ 0xd0) - 0x10; + vs[i] = + ((v << 8) + cbp[1]) << (_fixed_shift - 2); + if_debug1('L', " %g", fixed2float(vs[i])); + cbp += 2; + continue; + default /*case 7 */ : + v = (int)(*++cbp ^ 0x80) - 0x80; + for (b = 0; b < sizeof(fixed) - 3; ++b) + v = (v << 8) + *++cbp; + break; + } + cbp += 3; + /* Absent the cast in the next statement, */ + /* the Borland C++ 4.5 compiler incorrectly */ + /* sign-extends the result of the shift. */ + vs[i] = (v << 16) + (uint) (cbp[-2] << 8) + cbp[-1]; + if_debug1('L', " %g", fixed2float(vs[i])); + } + if_debug0('L', "\n"); + code = clist_decode_segment(&path, op, vs, &ppos, + x0, y0, notes); + if (code < 0) + goto out; + } + continue; + case cmd_op_path >> 4: + { + gx_device_color devc; + gx_device_color *pdevc; + gx_ht_tile ht_tile; + + if_debug0('L', "\n"); + switch (op) { + case cmd_opv_fill: + fill_params.rule = gx_rule_winding_number; + goto fill_pure; + case cmd_opv_eofill: + fill_params.rule = gx_rule_even_odd; + fill_pure:color_set_pure(&devc, state.colors[1]); + pdevc = &devc; + goto fill; + case cmd_opv_htfill: + fill_params.rule = gx_rule_winding_number; + goto fill_ht; + case cmd_opv_hteofill: + fill_params.rule = gx_rule_even_odd; + fill_ht:ht_tile.tiles = state_tile; + color_set_binary_tile(&devc, + state.tile_colors[0], + state.tile_colors[1], + &ht_tile); + pdevc = &devc; + pdevc->phase = tile_phase; + goto fill; + case cmd_opv_colorfill: + fill_params.rule = gx_rule_winding_number; + goto fill_color; + case cmd_opv_coloreofill: + fill_params.rule = gx_rule_even_odd; + fill_color:pdevc = &dev_color; + pdevc->phase = tile_phase; + code = + gx_color_load(pdevc, &imager_state, tdev); + if (code < 0) + break; + fill:fill_params.adjust = imager_state.fill_adjust; + fill_params.flatness = imager_state.flatness; + code = gx_fill_path_only(&path, tdev, + &imager_state, + &fill_params, + pdevc, pcpath); + break; + case cmd_opv_stroke: + color_set_pure(&devc, state.colors[1]); + pdevc = &devc; + goto stroke; + case cmd_opv_htstroke: + ht_tile.tiles = state_tile; + color_set_binary_tile(&devc, + state.tile_colors[0], + state.tile_colors[1], + &ht_tile); + pdevc = &devc; + pdevc->phase = tile_phase; + goto stroke; + case cmd_opv_colorstroke: + pdevc = &dev_color; + pdevc->phase = tile_phase; + code = + gx_color_load(pdevc, &imager_state, tdev); + if (code < 0) + break; + stroke:stroke_params.flatness = imager_state.flatness; + code = gx_stroke_path_only(&path, + (gx_path *) 0, tdev, + &imager_state, &stroke_params, + pdevc, pcpath); + break; + default: + goto bad_op; + } + } + if (in_path) { /* path might be empty! */ + state.rect.x = fixed2int_var(ppos.x); + state.rect.y = fixed2int_var(ppos.y); + in_path = false; + } + gx_path_free(&path, "clist_render_band"); + gx_path_init_local(&path, mem); + if (code < 0) + goto out; + continue; + default: + bad_op:lprintf5("Bad op %02x band y0 = %d file pos %ld buf pos %d/%d\n", + op, y0, stell(s), (int)(cbp - cbuf.data), (int)(cbuf.end - cbuf.data)); + { + const byte *pp; + + for (pp = cbuf.data; pp < cbuf.end; pp += 10) { + dlprintf1("%4d:", (int)(pp - cbuf.data)); + dprintf10(" %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", + pp[0], pp[1], pp[2], pp[3], pp[4], + pp[5], pp[6], pp[7], pp[8], pp[9]); + } + } + code = gs_note_error(gs_error_Fatal); + goto out; + } + if_debug4('L', " x=%d y=%d w=%d h=%d\n", + state.rect.x, state.rect.y, state.rect.width, + state.rect.height); + switch (op >> 4) { + case cmd_op_fill_rect >> 4: + case cmd_op_fill_rect_short >> 4: + case cmd_op_fill_rect_tiny >> 4: + if (!state.lop_enabled) { + code = (*dev_proc(tdev, fill_rectangle)) + (tdev, state.rect.x - x0, state.rect.y - y0, + state.rect.width, state.rect.height, + state.colors[1]); + break; + } + source = NULL; + data_x = 0; + raster = 0; + colors[0] = colors[1] = state.colors[1]; + log_op = state.lop; + pcolor = colors; + do_rop:code = (*dev_proc(tdev, strip_copy_rop)) + (tdev, source, data_x, raster, gx_no_bitmap_id, + pcolor, &state_tile, + (state.tile_colors[0] == gx_no_color_index && + state.tile_colors[1] == gx_no_color_index ? + NULL : state.tile_colors), + state.rect.x - x0, state.rect.y - y0, + state.rect.width - data_x, state.rect.height, + tile_phase.x, tile_phase.y, log_op); + data_x = 0; + break; + case cmd_op_tile_rect >> 4: + case cmd_op_tile_rect_short >> 4: + case cmd_op_tile_rect_tiny >> 4: + /* Currently we don't use lop with tile_rectangle. */ + code = (*dev_proc(tdev, strip_tile_rectangle)) + (tdev, &state_tile, + state.rect.x - x0, state.rect.y - y0, + state.rect.width, state.rect.height, + state.tile_colors[0], state.tile_colors[1], + tile_phase.x, tile_phase.y); + break; + case cmd_op_copy_mono >> 4: + if (state.lop_enabled) { + pcolor = state.colors; + log_op = state.lop; + goto do_rop; + } + if ((op & cmd_copy_use_tile) || pcpath != NULL) { /* + * This call of copy_mono originated as a call + * of fill_mask. + */ + gx_drawing_color dcolor; + gx_ht_tile ht_tile; + + if (op & cmd_copy_ht_color) { /* Screwy C assignment rules don't allow: */ + /* dcolor.colors = state.tile_colors; */ + ht_tile.tiles = state_tile; + color_set_binary_tile(&dcolor, + state.tile_colors[0], + state.tile_colors[1], &ht_tile); + dcolor.phase = tile_phase; + } else { + color_set_pure(&dcolor, state.colors[1]); + } + code = (*dev_proc(tdev, fill_mask)) + (tdev, source, data_x, raster, gx_no_bitmap_id, + state.rect.x - x0, state.rect.y - y0, + state.rect.width - data_x, state.rect.height, + &dcolor, 1, imager_state.log_op, pcpath); + } else + code = (*dev_proc(tdev, copy_mono)) + (tdev, source, data_x, raster, gx_no_bitmap_id, + state.rect.x - x0, state.rect.y - y0, + state.rect.width - data_x, state.rect.height, + state.colors[0], state.colors[1]); + data_x = 0; + break; + case cmd_op_copy_color_alpha >> 4: + if (state.color_is_alpha) { +/****** CAN'T DO ROP WITH ALPHA ******/ + code = (*dev_proc(tdev, copy_alpha)) + (tdev, source, data_x, raster, gx_no_bitmap_id, + state.rect.x - x0, state.rect.y - y0, + state.rect.width - data_x, state.rect.height, + state.colors[1], depth); + } else { + if (state.lop_enabled) { + pcolor = NULL; + log_op = state.lop; + goto do_rop; + } + code = (*dev_proc(tdev, copy_color)) + (tdev, source, data_x, raster, gx_no_bitmap_id, + state.rect.x - x0, state.rect.y - y0, + state.rect.width - data_x, state.rect.height); + } + data_x = 0; + break; + default: /* can't happen */ + goto bad_op; + } + } + /* Clean up before we exit. */ + out:gx_cpath_free(&clip_path, "clist_render_band exit"); + gx_path_free(&path, "clist_render_band exit"); + if (imager_state.ht_cache) + gx_ht_free_cache(mem, imager_state.ht_cache); + gx_device_halftone_release(&dev_ht, mem); + gs_imager_state_release(&imager_state); + gs_free_object(mem, data_bits, "clist_playback_band(data_bits)"); + if (code < 0) + return_error(code); + /* Check whether we have more pages to process. */ + if (playback_action != playback_action_setup && + (cbp < cbuf.end || !seofp(s)) + ) + goto in; + return code; +} + +/* Unpack a short bitmap */ +private void +clist_unpack_short_bits(byte * dest, const byte * src, int width_bytes, + int height, uint raster) +{ + uint bytes = width_bytes * height; + const byte *pdata = src + bytes; + byte *udata = dest + height * raster; + + while (--height >= 0) { + udata -= raster, pdata -= width_bytes; + switch (width_bytes) { + default: + memmove(udata, pdata, width_bytes); + break; + case 6: + udata[5] = pdata[5]; + case 5: + udata[4] = pdata[4]; + case 4: + udata[3] = pdata[3]; + case 3: + udata[2] = pdata[2]; + case 2: + udata[1] = pdata[1]; + case 1: + udata[0] = pdata[0]; + case 0:; /* shouldn't happen */ + } + } +} + +/* Read a rectangle. */ +private const byte * +cmd_read_rect(int op, gx_cmd_rect * prect, const byte * cbp) +{ + cmd_getw(prect->x, cbp); + if (op & 0xf) + prect->y += ((op >> 2) & 3) - 2; + else { + cmd_getw(prect->y, cbp); + } + cmd_getw(prect->width, cbp); + if (op & 0xf) + prect->height += (op & 3) - 2; + else { + cmd_getw(prect->height, cbp); + } + return cbp; +} + +/* Read a transformation matrix. */ +private const byte * +cmd_read_matrix(gs_matrix * pmat, const byte * cbp) +{ + byte b = *cbp++; + float coeff[6]; + int i; + + for (i = 0; i < 4; i += 2, b <<= 2) + if (!(b & 0xc0)) + coeff[i] = coeff[i ^ 3] = 0.0; + else { + float value; + + cmd_get_value(value, cbp); + coeff[i] = value; + switch ((b >> 6) & 3) { + case 1: + coeff[i ^ 3] = value; + break; + case 2: + coeff[i ^ 3] = -value; + break; + case 3: + cmd_get_value(coeff[i ^ 3], cbp); + } + } + for (; i < 6; ++i, b <<= 1) + if (b & 0x80) { + cmd_get_value(coeff[i], cbp); + } else + coeff[i] = 0.0; + pmat->xx = coeff[0]; + pmat->xy = coeff[1]; + pmat->yx = coeff[2]; + pmat->yy = coeff[3]; + pmat->tx = coeff[4]; + pmat->ty = coeff[5]; + return cbp; +} + +/* Select a map for loading with data. */ +/* load = false is not possible for cmd_map_transfer*. */ +private int +cmd_select_map(cmd_map_index map_index, bool load, gs_imager_state * pis, + gx_ht_order * porder, frac ** pmdata, uint * pcount, gs_memory_t * mem) +{ + gx_transfer_map *map; + gx_transfer_map **pmap; + const char *cname; + + switch (map_index) { + case cmd_map_transfer: + if_debug0('L', " transfer"); + map = pis->set_transfer.colored.gray; + pis->effective_transfer.indexed[0] = + pis->effective_transfer.indexed[1] = + pis->effective_transfer.indexed[2] = + pis->effective_transfer.indexed[3] = + map; + break; + case cmd_map_transfer_0: + case cmd_map_transfer_1: + case cmd_map_transfer_2: + case cmd_map_transfer_3: + { + int i = map_index - cmd_map_transfer_0; + + if_debug1('L', " transfer[%d]", i); + rc_unshare_struct(pis->set_transfer.indexed[i], gx_transfer_map, + &st_transfer_map, mem, + return_error(gs_error_VMerror), + "cmd_select_map(transfer)"); + map = pis->set_transfer.indexed[i]; + pis->effective_transfer.indexed[i] = map; + } + break; + case cmd_map_ht_transfer: + if_debug0('L', " ht transfer"); + /* Halftone transfer maps are never shared, but */ + /* rc_unshare_struct is a good way to get one allocated */ + /* if it hasn't been yet. */ + pmap = &porder->transfer; + cname = "cmd_select_map(ht transfer)"; + goto alloc; + case cmd_map_black_generation: + if_debug0('L', " black generation"); + pmap = &pis->black_generation; + cname = "cmd_select_map(black generation)"; + goto alloc; + case cmd_map_undercolor_removal: + if_debug0('L', " undercolor removal"); + pmap = &pis->undercolor_removal; + cname = "cmd_select_map(undercolor removal)"; +alloc: if (!load) { + rc_decrement(*pmap, cname); + *pmap = 0; + *pmdata = 0; + *pcount = 0; + return 0; + } + rc_unshare_struct(*pmap, gx_transfer_map, &st_transfer_map, + mem, return_error(gs_error_VMerror), cname); + map = *pmap; + break; + default: + *pmdata = 0; + return 0; + } + map->proc = gs_mapped_transfer; + *pmdata = map->values; + *pcount = sizeof(map->values); + return 0; +} + +/* Install a halftone order, resizing the bits and levels if necessary. */ +private int +cmd_install_ht_order(gx_ht_order * porder, const gx_ht_order * pnew, + gs_memory_t * mem) +{ + uint *levels = porder->levels; + gx_ht_bit *bits = porder->bits; + + /* + * Note that for resizing a byte array, the element size is 1 byte, + * not the element size given to alloc_byte_array! + */ + if (pnew->num_levels > porder->num_levels) { + if (levels == 0) + levels = (uint *) gs_alloc_byte_array(mem, pnew->num_levels, + sizeof(*levels), + "ht order(levels)"); + else + levels = gs_resize_object(mem, levels, + pnew->num_levels * sizeof(*levels), + "ht order(levels)"); + if (levels == 0) + return_error(gs_error_VMerror); + /* Update porder in case we bail out. */ + porder->levels = levels; + porder->num_levels = pnew->num_levels; + } + if (pnew->num_bits > porder->num_bits) { + if (bits == 0) + bits = (gx_ht_bit *) gs_alloc_byte_array(mem, pnew->num_bits, + sizeof(*bits), + "ht order(bits)"); + else + bits = gs_resize_object(mem, bits, + pnew->num_bits * sizeof(*bits), + "ht order(bits)"); + if (bits == 0) + return_error(gs_error_VMerror); + } + *porder = *pnew; + porder->levels = levels; + porder->bits = bits; + porder->full_height = ht_order_full_height(porder); + return 0; +} + +/* Resize the halftone components array if necessary. */ +private int +cmd_resize_halftone(gx_device_halftone * pdht, uint num_comp, + gs_memory_t * mem) +{ + if (num_comp != pdht->num_comp) { + gx_ht_order_component *pcomp; + + /* + * We must be careful not to shrink or free the components array + * before releasing any relevant elements. + */ + if (num_comp < pdht->num_comp) { + uint i; + + /* Don't release the default order. */ + for (i = pdht->num_comp; i-- > num_comp;) + if (pdht->components[i].corder.bits != pdht->order.bits) + gx_ht_order_release(&pdht->components[i].corder, mem, true); + if (num_comp == 0) { + gs_free_object(mem, pdht->components, "cmd_resize_halftone"); + pcomp = 0; + } else { + pcomp = gs_resize_object(mem, pdht->components, num_comp, + "cmd_resize_halftone"); + if (pcomp == 0) { + pdht->num_comp = num_comp; /* attempt consistency */ + return_error(gs_error_VMerror); + } + } + } else { + /* num_comp > pdht->num_comp */ + if (pdht->num_comp == 0) + pcomp = gs_alloc_struct_array(mem, num_comp, + gx_ht_order_component, + &st_ht_order_component_element, + "cmd_resize_halftone"); + else + pcomp = gs_resize_object(mem, pdht->components, num_comp, + "cmd_resize_halftone"); + if (pcomp == 0) + return_error(gs_error_VMerror); + memset(&pcomp[pdht->num_comp], 0, + sizeof(*pcomp) * (num_comp - pdht->num_comp)); + } + pdht->num_comp = num_comp; + pdht->components = pcomp; + } + return 0; +} + +/* ------ Path operations ------ */ + +/* Decode a path segment. */ +private int +clist_decode_segment(gx_path * ppath, int op, fixed vs[6], + gs_fixed_point * ppos, int x0, int y0, segment_notes notes) +{ + fixed px = ppos->x - int2fixed(x0); + fixed py = ppos->y - int2fixed(y0); + int code; + +#define A vs[0] +#define B vs[1] +#define C vs[2] +#define D vs[3] +#define E vs[4] +#define F vs[5] + + switch (op) { + case cmd_opv_rmoveto: + code = gx_path_add_point(ppath, px += A, py += B); + break; + case cmd_opv_rlineto: + code = gx_path_add_line_notes(ppath, px += A, py += B, notes); + break; + case cmd_opv_hlineto: + code = gx_path_add_line_notes(ppath, px += A, py, notes); + break; + case cmd_opv_vlineto: + code = gx_path_add_line_notes(ppath, px, py += A, notes); + break; + case cmd_opv_rrcurveto: /* a b c d e f => a b a+c b+d a+c+e b+d+f */ + E += (C += A); + F += (D += B); +curve: code = gx_path_add_curve_notes(ppath, px + A, py + B, + px + C, py + D, + px + E, py + F, notes); + px += E, py += F; + break; + case cmd_opv_hvcurveto: /* a b c d => a 0 a+b c a+b c+d */ +hvc: F = C + D, D = C, E = C = A + B, B = 0; + goto curve; + case cmd_opv_vhcurveto: /* a b c d => 0 a b a+c b+d a+c */ +vhc: E = B + D, F = D = A + C, C = B, B = A, A = 0; + goto curve; + case cmd_opv_nrcurveto: /* a b c d => 0 0 a b a+c b+d */ + F = B + D, E = A + C, D = B, C = A, B = A = 0; + goto curve; + case cmd_opv_rncurveto: /* a b c d => a b a+c b+d a+c b+d */ + F = D += B, E = C += A; + goto curve; + case cmd_opv_rmlineto: + if ((code = gx_path_add_point(ppath, px += A, py += B)) < 0) + break; + code = gx_path_add_line_notes(ppath, px += C, py += D, notes); + break; + case cmd_opv_rm2lineto: + if ((code = gx_path_add_point(ppath, px += A, py += B)) < 0 || + (code = gx_path_add_line_notes(ppath, px += C, py += D, + notes)) < 0 + ) + break; + code = gx_path_add_line_notes(ppath, px += E, py += F, notes); + break; + case cmd_opv_vqcurveto: /* a b => VH a b TS(a,b) TS(b,a) */ + if ((A ^ B) < 0) + C = -B, D = -A; + else + C = B, D = A; + goto vhc; + case cmd_opv_hqcurveto: /* a b => HV a TS(a,b) b TS(b,a) */ + if ((A ^ B) < 0) + D = -A, C = B, B = -B; + else + D = A, C = B; + goto hvc; + case cmd_opv_rm3lineto: + if ((code = gx_path_add_point(ppath, px += A, py += B)) < 0 || + (code = gx_path_add_line_notes(ppath, px += C, py += D, + notes)) < 0 || + (code = gx_path_add_line_notes(ppath, px += E, py += F, + notes)) < 0 + ) + break; + code = gx_path_add_line_notes(ppath, px -= C, py -= D, notes); + break; + case cmd_opv_closepath: + code = gx_path_close_subpath(ppath); + gx_path_current_point(ppath, (gs_fixed_point *) vs); + px = A, py = B; + break; + default: + return_error(gs_error_rangecheck); + } +#undef A +#undef B +#undef C +#undef D +#undef E +#undef F + ppos->x = px + int2fixed(x0); + ppos->y = py + int2fixed(y0); + return code; +} diff --git a/gs/src/gxclutil.c b/gs/src/gxclutil.c new file mode 100644 index 000000000..ed50beb62 --- /dev/null +++ b/gs/src/gxclutil.c @@ -0,0 +1,610 @@ +/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: gxclutil.c */ +/* Command list writing utilities. */ + +#include "memory_.h" +#include "string_.h" +#include "gx.h" +#include "gp.h" +#include "gpcheck.h" +#include "gserrors.h" +#include "gxdevice.h" +#include "gxdevmem.h" /* must precede gxcldev.h */ +#include "gxcldev.h" +#include "gxclpath.h" +#include "gsparams.h" + +#define cdev cwdev + +/* ---------------- Statistics ---------------- */ + +#ifdef DEBUG +const char *const cmd_op_names[16] = +{cmd_op_name_strings}; +private const char *const cmd_misc_op_names[16] = +{cmd_misc_op_name_strings}; +private const char *const cmd_misc2_op_names[16] = +{cmd_misc2_op_name_strings}; +private const char *const cmd_segment_op_names[16] = +{cmd_segment_op_name_strings}; +private const char *const cmd_path_op_names[16] = +{cmd_path_op_name_strings}; +const char *const *const cmd_sub_op_names[16] = +{cmd_misc_op_names, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, + 0, cmd_misc2_op_names, cmd_segment_op_names, cmd_path_op_names +}; +struct stats_cmd_s { + ulong op_counts[256]; + ulong op_sizes[256]; + ulong tile_reset, tile_found, tile_added; + ulong same_band, other_band; +} stats_cmd; +extern ulong stats_cmd_diffs[5]; /* in gxclpath.c */ +int +cmd_count_op(int op, uint size) +{ + stats_cmd.op_counts[op]++; + stats_cmd.op_sizes[op] += size; + if (gs_debug_c('L')) { + const char *const *sub = cmd_sub_op_names[op >> 4]; + + if (sub) + dlprintf2(", %s(%u)\n", sub[op & 0xf], size); + else + dlprintf3(", %s %d(%u)\n", cmd_op_names[op >> 4], op & 0xf, + size); + fflush(dstderr); + } + return op; +} +void +cmd_uncount_op(int op, uint size) +{ + stats_cmd.op_counts[op]--; + stats_cmd.op_sizes[op] -= size; +} +#endif + +/* Print statistics. */ +#ifdef DEBUG +void +cmd_print_stats(void) +{ + int ci, cj; + + dlprintf3("[l]counts: reset = %lu, found = %lu, added = %lu\n", + stats_cmd.tile_reset, stats_cmd.tile_found, + stats_cmd.tile_added); + dlprintf5(" diff 2.5 = %lu, 3 = %lu, 4 = %lu, 2 = %lu, >4 = %lu\n", + stats_cmd_diffs[0], stats_cmd_diffs[1], stats_cmd_diffs[2], + stats_cmd_diffs[3], stats_cmd_diffs[4]); + dlprintf2(" same_band = %lu, other_band = %lu\n", + stats_cmd.same_band, stats_cmd.other_band); + for (ci = 0; ci < 0x100; ci += 0x10) { + const char *const *sub = cmd_sub_op_names[ci >> 4]; + + if (sub != 0) { + dlprintf1("[l] %s =", cmd_op_names[ci >> 4]); + for (cj = ci; cj < ci + 0x10; cj += 2) + dprintf6("\n\t%s = %lu(%lu), %s = %lu(%lu)", + sub[cj - ci], + stats_cmd.op_counts[cj], stats_cmd.op_sizes[cj], + sub[cj - ci + 1], + stats_cmd.op_counts[cj + 1], stats_cmd.op_sizes[cj + 1]); + } else { + ulong tcounts = 0, tsizes = 0; + + for (cj = ci; cj < ci + 0x10; cj++) + tcounts += stats_cmd.op_counts[cj], + tsizes += stats_cmd.op_sizes[cj]; + dlprintf3("[l] %s (%lu,%lu) =\n\t", + cmd_op_names[ci >> 4], tcounts, tsizes); + for (cj = ci; cj < ci + 0x10; cj++) + if (stats_cmd.op_counts[cj] == 0) + dputs(" -"); + else + dprintf2(" %lu(%lu)", stats_cmd.op_counts[cj], + stats_cmd.op_sizes[cj]); + } + dputs("\n"); + } +} +#endif /* DEBUG */ + +/* ---------------- Writing utilities ---------------- */ + +/* Write the commands for one band or band range. */ +private int /* ret 0 all ok, -ve error code, or +1 ok w/low-mem warning */ +cmd_write_band(gx_device_clist_writer * cldev, int band_min, int band_max, + cmd_list * pcl, byte cmd_end) +{ + const cmd_prefix *cp = pcl->head; + int code_b = 0; + int code_c = 0; + + if (cp != 0 || cmd_end != cmd_opv_end_run) { + clist_file_ptr cfile = cldev->page_cfile; + clist_file_ptr bfile = cldev->page_bfile; + cmd_block cb; + byte end = cmd_count_op(cmd_end, 1); + + if (cfile == 0 || bfile == 0) + return_error(gs_error_ioerror); + cb.band_min = band_min; + cb.band_max = band_max; + cb.pos = clist_ftell(cfile); + if_debug3('l', "[l]writing for bands (%d,%d) at %ld\n", + band_min, band_max, cb.pos); + clist_fwrite_chars(&cb, sizeof(cb), bfile); + if (cp != 0) { + pcl->tail->next = 0; /* terminate the list */ + for (; cp != 0; cp = cp->next) { +#ifdef DEBUG + if ((const byte *)cp < cldev->cbuf || + (const byte *)cp >= cldev->cend || + cp->size > cldev->cend - (const byte *)cp + ) { + lprintf1("cmd_write_band error at 0x%lx\n", (ulong) cp); + return_error(gs_error_Fatal); + } +#endif + clist_fwrite_chars(cp + 1, cp->size, cfile); + } + pcl->head = pcl->tail = 0; + } + clist_fwrite_chars(&end, 1, cfile); + process_interrupts(); + code_b = clist_ferror_code(bfile); + code_c = clist_ferror_code(cfile); + if (code_b < 0) + return_error(code_b); + if (code_c < 0) + return_error(code_c); + } + return code_b | code_c; +} + +/* Write out the buffered commands, and reset the buffer. */ +int /* ret 0 all-ok, -ve error code, or +1 ok w/low-mem warning */ +cmd_write_buffer(gx_device_clist_writer * cldev, byte cmd_end) +{ + int nbands = cldev->nbands; + gx_clist_state *pcls; + int band; + int code = cmd_write_band(cldev, cldev->band_range_min, + cldev->band_range_max, + &cldev->band_range_list, cmd_opv_end_run); + int warning = code; + + for (band = 0, pcls = cldev->states; + code >= 0 && band < nbands; band++, pcls++ + ) { + code = cmd_write_band(cldev, band, band, &pcls->list, cmd_end); + warning |= code; + } + cldev->cnext = cldev->cbuf; + cldev->ccl = 0; + cldev->band_range_list.head = cldev->band_range_list.tail = 0; +#ifdef DEBUG + if (gs_debug_c('l')) + cmd_print_stats(); +#endif + return_check_interrupt(code != 0 ? code : warning); +} + +/* + * Add a command to the appropriate band list, and allocate space for its + * data. Return the pointer to the data area. If an error or (low-memory + * warning) occurs, set cldev->error_code and return 0. + */ +#define cmd_headroom (sizeof(cmd_prefix) + arch_align_ptr_mod) +byte * +cmd_put_list_op(gx_device_clist_writer * cldev, cmd_list * pcl, uint size) +{ + byte *dp = cldev->cnext; + + if (size + cmd_headroom > cldev->cend - dp) { + if ((cldev->error_code = + cmd_write_buffer(cldev, cmd_opv_end_run)) != 0) { + if (cldev->error_code < 0) + cldev->error_is_retryable = 0; /* hard error */ + else { + /* upgrade lo-mem warning into an error */ + if (!cldev->ignore_lo_mem_warnings) + cldev->error_code = gs_note_error(gs_error_VMerror); + cldev->error_is_retryable = 1; + } + return 0; + } + else + return cmd_put_list_op(cldev, pcl, size); + } + if (cldev->ccl == pcl) { /* We're adding another command for the same band. */ + /* Tack it onto the end of the previous one. */ + cmd_count_add1(stats_cmd.same_band); +#ifdef DEBUG + if (pcl->tail->size > dp - (byte *) (pcl->tail + 1)) { + lprintf1("cmd_put_list_op error at 0x%lx\n", (ulong) pcl->tail); + } +#endif + pcl->tail->size += size; + } else { + /* Skip to an appropriate alignment boundary. */ + /* (We assume the command buffer itself is aligned.) */ + cmd_prefix *cp = (cmd_prefix *) + (dp + ((cldev->cbuf - dp) & (arch_align_ptr_mod - 1))); + + cmd_count_add1(stats_cmd.other_band); + dp = (byte *) (cp + 1); + if (pcl->tail != 0) { +#ifdef DEBUG + if (pcl->tail < pcl->head || + pcl->tail->size > dp - (byte *) (pcl->tail + 1) + ) { + lprintf1("cmd_put_list_op error at 0x%lx\n", + (ulong) pcl->tail); + } +#endif + pcl->tail->next = cp; + } else + pcl->head = cp; + pcl->tail = cp; + cldev->ccl = pcl; + cp->size = size; + } + cldev->cnext = dp + size; + return dp; +} +#ifdef DEBUG +byte * +cmd_put_op(gx_device_clist_writer * cldev, gx_clist_state * pcls, uint size) +{ + if_debug3('L', "[L]band %d: size=%u, left=%u", + (int)(pcls - cldev->states), + size, (uint) (cldev->cend - cldev->cnext)); + return cmd_put_list_op(cldev, &pcls->list, size); +} +#endif + +/* Add a command for a range of bands. */ +byte * +cmd_put_range_op(gx_device_clist_writer * cldev, int band_min, int band_max, + uint size) +{ + if_debug4('L', "[L]band range(%d,%d): size=%u, left=%u", + band_min, band_max, size, + (uint)(cldev->cend - cldev->cnext)); + if (cldev->ccl != 0 && + (cldev->ccl != &cldev->band_range_list || + band_min != cldev->band_range_min || + band_max != cldev->band_range_max) + ) { + if ((cldev->error_code = cmd_write_buffer(cldev, cmd_opv_end_run)) != 0) { + if (cldev->error_code < 0) + cldev->error_is_retryable = 0; /* hard error */ + else { + /* upgrade lo-mem warning into an error */ + cldev->error_code = gs_error_VMerror; + cldev->error_is_retryable = 1; + } + return 0; + } + cldev->band_range_min = band_min; + cldev->band_range_max = band_max; + } + return cmd_put_list_op(cldev, &cldev->band_range_list, size); +} + +/* Write a variable-size positive integer. */ +int +cmd_size_w(register uint w) +{ + register int size = 1; + + while (w > 0x7f) + w >>= 7, size++; + return size; +} +byte * +cmd_put_w(register uint w, register byte * dp) +{ + while (w > 0x7f) + *dp++ = w | 0x80, w >>= 7; + *dp = w; + return dp + 1; +} + +/* Define the encodings of the different settable colors. */ +const clist_select_color_t + clist_select_color0 = {cmd_op_set_color0, cmd_opv_delta2_color0, 0}, + clist_select_color1 = {cmd_op_set_color1, cmd_opv_delta2_color1, 0}, + clist_select_tile_color0 = {cmd_op_set_color0, cmd_opv_delta2_color0, 1}, + clist_select_tile_color1 = {cmd_op_set_color1, cmd_opv_delta2_color1, 1}; +int +cmd_put_color(gx_device_clist_writer * cldev, gx_clist_state * pcls, + const clist_select_color_t * select, + gx_color_index color, gx_color_index * pcolor) +{ + byte *dp; + long diff = (long)color - (long)(*pcolor); + byte op, op_delta2; + int code; + + if (diff == 0) + return 0; + if (select->tile_color) { + code = set_cmd_put_op(dp, cldev, pcls, cmd_opv_set_tile_color, 1); + if (code < 0) + return code; + } + op = select->set_op; + op_delta2 = select->delta2_op; + if (color == gx_no_color_index) { + /* + * We must handle this specially, because it may take more + * bytes than the color depth. + */ + code = set_cmd_put_op(dp, cldev, pcls, op + 15, 1); + if (code < 0) + return code; + } else { + long delta; + byte operand; + + switch ((cldev->color_info.depth + 15) >> 3) { + case 5: + if (!((delta = diff + cmd_delta1_32_bias) & + ~cmd_delta1_32_mask) && + (operand = + (byte) ((delta >> 23) + ((delta >> 18) & 1))) != 0 && + operand != 15 + ) { + code = set_cmd_put_op(dp, cldev, pcls, + (byte) (op + operand), 2); + if (code < 0) + return code; + dp[1] = (byte) (((delta >> 10) & 0300) + + (delta >> 5) + delta); + break; + } + if (!((delta = diff + cmd_delta2_32_bias) & + ~cmd_delta2_32_mask) + ) { + code = set_cmd_put_op(dp, cldev, pcls, op_delta2, 3); + if (code < 0) + return code; + dp[1] = (byte) ((delta >> 20) + (delta >> 16)); + dp[2] = (byte) ((delta >> 4) + delta); + break; + } + code = set_cmd_put_op(dp, cldev, pcls, op, 5); + if (code < 0) + return code; + *++dp = (byte) (color >> 24); + goto b3; + case 4: + if (!((delta = diff + cmd_delta1_24_bias) & + ~cmd_delta1_24_mask) && + (operand = (byte) (delta >> 16)) != 0 && + operand != 15 + ) { + code = set_cmd_put_op(dp, cldev, pcls, + (byte) (op + operand), 2); + if (code < 0) + return code; + dp[1] = (byte) ((delta >> 4) + delta); + break; + } + if (!((delta = diff + cmd_delta2_24_bias) & + ~cmd_delta2_24_mask) + ) { + code = set_cmd_put_op(dp, cldev, pcls, op_delta2, 3); + if (code < 0) + return code; + dp[1] = ((byte) (delta >> 13) & 0xf8) + + ((byte) (delta >> 11) & 7); + dp[2] = (byte) (((delta >> 3) & 0xe0) + delta); + break; + } + code = set_cmd_put_op(dp, cldev, pcls, op, 4); + if (code < 0) + return code; +b3: *++dp = (byte) (color >> 16); + goto b2; + case 3: + code = set_cmd_put_op(dp, cldev, pcls, op, 3); + if (code < 0) + return code; +b2: *++dp = (byte) (color >> 8); + goto b1; + case 2: + if (diff >= -7 && diff < 7) { + code = set_cmd_put_op(dp, cldev, pcls, + op + (int)diff + 8, 1); + if (code < 0) + return code; + break; + } + code = set_cmd_put_op(dp, cldev, pcls, op, 2); + if (code < 0) + return code; +b1: dp[1] = (byte) color; + } + } + *pcolor = color; + return 0; +} + +/* Put out a command to set the tile colors. */ +int +cmd_set_tile_colors(gx_device_clist_writer * cldev, gx_clist_state * pcls, + gx_color_index color0, gx_color_index color1) +{ + int code = 0; + + if (color0 != pcls->tile_colors[0]) { + code = cmd_put_color(cldev, pcls, + &clist_select_tile_color0, + color0, &pcls->tile_colors[0]); + if (code != 0) + return code; + } + if (color1 != pcls->tile_colors[1]) + code = cmd_put_color(cldev, pcls, + &clist_select_tile_color1, + color1, &pcls->tile_colors[1]); + return code; +} + +/* Put out a command to set the tile phase. */ +int +cmd_set_tile_phase(gx_device_clist_writer * cldev, gx_clist_state * pcls, + int px, int py) +{ + int pcsize; + byte *dp; + int code; + + pcsize = 1 + cmd_size2w(px, py); + code = + set_cmd_put_op(dp, cldev, pcls, (byte)cmd_opv_set_tile_phase, pcsize); + if (code < 0) + return code; + ++dp; + cmd_putxy(pcls->tile_phase, dp); + pcls->tile_phase.x = px; + pcls->tile_phase.y = py; + return 0; +} + +/* Write a command to enable or disable the logical operation. */ +int +cmd_put_enable_lop(gx_device_clist_writer * cldev, gx_clist_state * pcls, + int enable) +{ + byte *dp; + int code = set_cmd_put_op(dp, cldev, pcls, + (byte)(enable ? cmd_opv_enable_lop : + cmd_opv_disable_lop), + 1); + + if (code < 0) + return code; + pcls->lop_enabled = enable; + return 0; +} + +/* Write a command to enable or disable clipping. */ +/* This routine is only called if the path extensions are included. */ +int +cmd_put_enable_clip(gx_device_clist_writer * cldev, gx_clist_state * pcls, + int enable) +{ + byte *dp; + int code = set_cmd_put_op(dp, cldev, pcls, + (byte)(enable ? cmd_opv_enable_clip : + cmd_opv_disable_clip), + 1); + + if (code < 0) + return code; + pcls->clip_enabled = enable; + return 0; +} + +/* Write a command to set the logical operation. */ +int +cmd_set_lop(gx_device_clist_writer * cldev, gx_clist_state * pcls, + gs_logical_operation_t lop) +{ + byte *dp; + uint lop_msb = lop >> 6; + int code = set_cmd_put_op(dp, cldev, pcls, + cmd_opv_set_misc, 2 + cmd_size_w(lop_msb)); + + if (code < 0) + return code; + dp[1] = cmd_set_misc_lop + (lop & 0x3f); + cmd_put_w(lop_msb, dp + 2); + pcls->lop = lop; + return 0; +} + +/* Disable (if default) or enable the logical operation, setting it if */ +/* needed. */ +int +cmd_update_lop(gx_device_clist_writer *cldev, gx_clist_state *pcls, + gs_logical_operation_t lop) +{ + int code; + + if (lop == lop_default) + return cmd_disable_lop(cldev, pcls); + code = cmd_set_lop(cldev, pcls, lop); + if (code < 0) + return code; + return cmd_enable_lop(cldev, pcls); +} + +/* Write a parameter list */ +int /* ret 0 all ok, -ve error */ +cmd_put_params(gx_device_clist_writer *cldev, + gs_param_list *param_list) /* NB open for READ */ +{ + byte *dp; + int code; + byte local_buf[512]; /* arbitrary */ + int param_length; + + /* Get serialized list's length + try to get it into local var if it fits. */ + param_length = code = + gs_param_list_serialize(param_list, local_buf, sizeof(local_buf)); + if (param_length > 0) { + /* Get cmd buffer space for serialized */ + code = set_cmd_put_all_op(dp, cldev, cmd_opv_put_params, + 1 + sizeof(unsigned) + param_length); + if (code < 0) + return code; + + /* write param list to cmd list: needs to all fit in cmd buffer */ + if_debug1('l', "[l]put_params, length=%d\n", param_length); + ++dp; + memcpy(dp, ¶m_length, sizeof(unsigned)); + dp += sizeof(unsigned); + if (param_length > sizeof(local_buf)) { + int old_param_length = param_length; + + param_length = code = + gs_param_list_serialize(param_list, dp, old_param_length); + if (param_length >= 0) + code = (old_param_length != param_length ? + gs_note_error(gs_error_unknownerror) : 0); + if (code < 0) { + /* error serializing: back out by writing a 0-length parm list */ + memset(dp - sizeof(unsigned), 0, sizeof(unsigned)); + cmd_shorten_list_op(cldev, &cldev->band_range_list, + old_param_length); + } + } else + memcpy(dp, local_buf, param_length); /* did this when computing length */ + } + return code; +} diff --git a/gs/src/gxcomp.h b/gs/src/gxcomp.h new file mode 100644 index 000000000..5c6151204 --- /dev/null +++ b/gs/src/gxcomp.h @@ -0,0 +1,107 @@ +/* Copyright (C) 1997 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: gxcomp.h */ +/* Definitions for implementing compositing functions */ + +#ifndef gxcomp_INCLUDED +# define gxcomp_INCLUDED + +#include "gscompt.h" +#include "gsrefct.h" +#include "gxbitfmt.h" + +/* + * Define the abstract superclass for all compositing function types. + */ + /*typedef struct gs_composite_s gs_composite_t; *//* in gscompt.h */ + +#ifndef gs_imager_state_DEFINED +# define gs_imager_state_DEFINED +typedef struct gs_imager_state_s gs_imager_state; + +#endif + +#ifndef gx_device_DEFINED +# define gx_device_DEFINED +typedef struct gx_device_s gx_device; + +#endif + +typedef struct gs_composite_type_procs_s { + + /* + * Create the default compositor for a compositing function. + */ +#define composite_create_default_compositor_proc(proc)\ + int proc(P5(const gs_composite_t *pcte, gx_device **pcdev,\ + gx_device *dev, const gs_imager_state *pis, gs_memory_t *mem)) + composite_create_default_compositor_proc((*create_default_compositor)); + + /* + * Test whether this function is equal to another one. + */ +#define composite_equal_proc(proc)\ + bool proc(P2(const gs_composite_t *pcte, const gs_composite_t *pcte2)) + composite_equal_proc((*equal)); + + /* + * Convert the representation of this function to a string + * for writing in a command list. *psize is the amount of space + * available. If it is large enough, the procedure sets *psize + * to the amount used and returns 0; if it is not large enough, + * the procedure sets *psize to the amount needed and returns a + * rangecheck error; in the case of any other error, *psize is + * not changed. + */ +#define composite_write_proc(proc)\ + int proc(P3(const gs_composite_t *pcte, byte *data, uint *psize)) + composite_write_proc((*write)); + + /* + * Convert the string representation of a function back to + * a structure, allocating the structure. + */ +#define composite_read_proc(proc)\ + int proc(P4(gs_composite_t **ppcte, const byte *data, uint size,\ + gs_memory_t *mem)) + composite_read_proc((*read)); + +} gs_composite_type_procs_t; +typedef struct gs_composite_type_s { + gs_composite_type_procs_t procs; +} gs_composite_type_t; + +/* + * Compositing objects are reference-counted, because graphics states will + * eventually reference them. Note that the common part has no + * garbage-collectible pointers and is never actually instantiated, so no + * structure type is needed for it. + */ +#define gs_composite_common\ + const gs_composite_type_t *type;\ + gs_id id; /* see gscompt.h */\ + rc_header rc +struct gs_composite_s { + gs_composite_common; +}; + +/* Replace a procedure with a macro. */ +#define gs_composite_id(pcte) ((pcte)->id) + +#endif /* gxcomp_INCLUDED */ diff --git a/gs/src/gxgetbit.h b/gs/src/gxgetbit.h new file mode 100644 index 000000000..4ba26d6ba --- /dev/null +++ b/gs/src/gxgetbit.h @@ -0,0 +1,95 @@ +/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: gxgetbit.h */ +/* Interface for get_bits_rectangle driver procedure */ + +#ifndef gxgetbit_INCLUDED +# define gxgetbit_INCLUDED + +#include "gxbitfmt.h" + +/* The parameter record typedef is also in gxdevcli.h. */ +#ifndef gs_get_bits_params_DEFINED +# define gs_get_bits_params_DEFINED +typedef struct gs_get_bits_params_s gs_get_bits_params_t; +#endif + +/* + * We define the options for get_bits_rectangle here in a separate file + * so that the great majority of driver implementors and clients, which + * don't care about the details, don't need to be recompiled if the set + * of options changes. + */ +typedef gx_bitmap_format_t gs_get_bits_options_t; + +/* + * Define the parameter record passed to get_bits_rectangle. + * get_bits_rectangle may update members of this structure if + * the options allow it to choose their values, and always updates options + * to indicate what options were actually used (1 option per group). + */ +struct gs_get_bits_params_s { + gs_get_bits_options_t options; + byte *data[32]; + int x_offset; /* in returned data */ + uint raster; +}; + +/* + * gx_bitmap_format_t defines the options passed to get_bits_rectangle, + * which indicate which formats are acceptable for the returned data. If + * successful, get_bits_rectangle sets the options member of the parameter + * record to indicate what options were chosen -- 1 per group, and never the + * _ANY option. Note that the chosen option is not necessarily one that + * appeared in the original options: for example, if GB_RASTER_ANY is the + * only raster option originally set, the chosen option will be + * GB_RASTER_STANDARD or GB_RASTER_SPECIFIED. + * + * If the options mask is 0, get_bits_rectangle must set it to the + * complete set of supported options and return an error. This allows + * clients to determine what options are supported without actually doing + * a transfer. + * + * All devices must support at least one option in each group, and must + * support GB_COLORS_NATIVE. + * + * NOTE: the current default implementation supports only the following + * options in their respective groups (i.e., any other options must be + * supported directly by the device): + * GB_DEPTH_8 + * GB_PACKING_CHUNKY + * GB_RETURN_COPY + * The current default implementation also requires that all devices + * support GB_PACKING_CHUNKY. */ + +/* ---------------- Procedures ---------------- */ + +/* Try to implement get_bits_rectangle by returning a pointer. */ +int gx_get_bits_return_pointer(P6(gx_device * dev, int x, int h, + gs_get_bits_params_t * params, + gs_get_bits_options_t stored, + byte * stored_base)); + +/* Implement get_bits_rectangle by copying. */ +int gx_get_bits_copy(P8(gx_device * dev, int x, int w, int h, + gs_get_bits_params_t * params, + gs_get_bits_options_t stored, + const byte * src_base, uint dev_raster)); + +#endif /* gxgetbit_INCLUDED */ diff --git a/gs/src/gxi12bit.c b/gs/src/gxi12bit.c new file mode 100644 index 000000000..55303d6be --- /dev/null +++ b/gs/src/gxi12bit.c @@ -0,0 +1,294 @@ +/* Copyright (C) 1994, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: gxi12bit.c */ +/* 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 "gzpath.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; +} + +/* ------ Strategy procedure ------ */ + +/* Use special (slow) logic for 12-bit source values. */ +private irender_proc(image_render_frac); +private irender_proc_t +image_strategy_frac(gx_image_enum * penum) +{ + if (penum->bps > 8) { + if_debug0('b', "[b]render=frac\n"); + return image_render_frac; + } + return 0; +} + +void +gs_gxi12bit_init(gs_memory_t * mem) +{ + image_strategies.fracs = image_strategy_frac; + sample_unpack_12_proc = sample_unpack_12; +} + +/* ---------------- Rendering procedures ---------------- */ + +/* ------ Rendering for 12-bit samples ------ */ + +/* Render an image with more than 8 bits per sample. */ +/* The samples have been expanded into fracs. */ +#define longs_per_4_fracs (arch_sizeof_frac * 4 / arch_sizeof_long) +typedef union { + frac v[4]; + long all[longs_per_4_fracs]; /* for fast comparison */ +} color_fracs; + +#if longs_per_4_fracs == 1 +# define color_frac_eq(f1, f2)\ + ((f1).all[0] == (f2).all[0]) +#else +#if longs_per_4_fracs == 2 +# define color_frac_eq(f1, f2)\ + ((f1).all[0] == (f2).all[0] && (f1).all[1] == (f2).all[1]) +#endif +#endif +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; + int device_color = penum->device_color; + const gx_color_map_procs *cmap_procs = gx_device_cmap_procs(dev); + cmap_proc_rgb((*map_rgb)) = cmap_procs->map_rgb; + cmap_proc_cmyk((*map_cmyk)) = cmap_procs->map_cmyk; + gx_device_color devc1, devc2; + gx_device_color *pdevc = &devc1; + gx_device_color *pdevc_next = &devc2; + int spp = penum->spp; + const frac *psrc = (const frac *)buffer + data_x * spp; + 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; + + 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_debug4('b', "[b]y=%d w=%d xt=%f yt=%f\n", + penum->y, w, fixed2float(xl), fixed2float(ytf)); + run.v[0] = run.v[1] = run.v[2] = run.v[3] = 0; + next.v[0] = next.v[1] = next.v[2] = next.v[3] = 0; + cc.paint.values[0] = cc.paint.values[1] = + cc.paint.values[2] = cc.paint.values[3] = 0; + cc.pattern = 0; + (*remap_color) (&cc, pcs, pdevc, pis, dev, gs_color_select_source); + run.v[0] = ~psrc[0]; /* force remap */ + + while (psrc < bufend) { + next.v[0] = psrc[0]; + switch (spp) { + case 4: /* cmyk */ + next.v[1] = psrc[1]; + next.v[2] = psrc[2]; + next.v[3] = psrc[3]; + psrc += 4; + if (color_frac_eq(next, run)) + goto inc; + 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: /* rgb */ + next.v[1] = psrc[1]; + next.v[2] = psrc[2]; + psrc += 3; + if (color_frac_eq(next, run)) + goto inc; + 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: /* gray */ + psrc++; + if (next.v[0] == run.v[0]) + goto inc; + 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; + } + (*remap_color) (&cc, pcs, pdevc_next, pis, dev, + gs_color_select_source); +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; + + 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) + return code; + 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); +} diff --git a/gs/src/gxicolor.c b/gs/src/gxicolor.c new file mode 100644 index 000000000..e4e16a77e --- /dev/null +++ b/gs/src/gxicolor.c @@ -0,0 +1,312 @@ +/* Copyright (C) 1992, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: gxicolor.c */ +/* Color image rendering */ +#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 "gzstate.h" +#include "gxdevice.h" +#include "gxcmap.h" +#include "gxdcconv.h" +#include "gxdcolor.h" +#include "gxistate.h" +#include "gzpath.h" +#include "gxdevmem.h" +#include "gxcpath.h" +#include "gximage.h" + +/* ------ Strategy procedure ------ */ + +private irender_proc(image_render_color); +private irender_proc_t +image_strategy_color(gx_image_enum * penum) +{ + return image_render_color; +} + +void +gs_gxicolor_init(gs_memory_t * mem) +{ + image_strategies.color = image_strategy_color; +} + +/* ------ Rendering procedures ------ */ + +/* Render a color image with 8 or fewer bits per sample. */ +typedef union { + byte v[4]; + bits32 all; /* for fast comparison & clearing */ +} color_samples; +private int +image_render_color(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 vci, vdi; + 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_device_cmap_procs(dev); + cmap_proc_rgb((*map_3)) = cmap_procs->map_rgb; + cmap_proc_cmyk((*map_4)) = + (penum->alpha ? cmap_procs->map_rgb_alpha : cmap_procs->map_cmyk); + gx_image_clue *pic = &penum->clues[0]; +#define pdevc (&pic->dev_color) + gx_image_clue *pic_next = &penum->clues[1]; +#define pdevc_next (&pic_next->dev_color) + gx_image_clue empty_clue; + gx_image_clue clue_temp; + int spp = penum->spp; + const byte *psrc = buffer + data_x * spp; + fixed xrun; /* x at start of run */ + fixed yrun; /* y ditto */ + int irun; /* int x/rrun */ + color_samples run; /* run value */ + color_samples next; /* next sample value */ + bool small = + fixed2int(any_abs(penum->x_extent.x)) < penum->rect.w && + fixed2int(any_abs(penum->x_extent.y)) < penum->rect.w; + const byte *bufend = psrc + w; + bool use_cache = spp * penum->bps <= 12; + int code; + + if (h == 0) + return 0; + pnext = penum->dda.pixel0; + xrun = xl = dda_current(pnext.x); + 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; + switch (posture) { + case image_portrait: + vci = penum->yci, vdi = penum->hci; + irun = fixed2int_var_rounded(xrun); + break; + case image_landscape: + vci = penum->xci, vdi = penum->wci; + irun = fixed2int_var_rounded(yrun); + break; + } + + if_debug4('b', "[b]y=%d w=%d xt=%f yt=%f\n", + penum->y, w, fixed2float(xl), fixed2float(ytf)); + run.all = 0; + next.all = 0; + /* Ensure that we don't get any false dev_color_eq hits. */ + if (use_cache) { + color_set_pure(&empty_clue.dev_color, gx_no_color_index); + pic = &empty_clue; + } + cc.paint.values[0] = cc.paint.values[1] = + cc.paint.values[2] = cc.paint.values[3] = 0; + cc.pattern = 0; + run.v[0] = ~psrc[0]; /* force remap */ + while (psrc < bufend) { + dda_next(pnext.x); +#define xn dda_current(pnext.x) + dda_next(pnext.y); +#define yn dda_current(pnext.y) +#define includes_pixel_center(a, b)\ + (fixed_floor(a < b ? (a - (fixed_half + fixed_epsilon)) ^ (b - fixed_half) :\ + (b - (fixed_half + fixed_epsilon)) ^ (a - fixed_half)) != 0) +#define paint_no_pixels()\ + (small && !includes_pixel_center(xl, xn) &&\ + !includes_pixel_center(ytf, yn) && psrc <= bufend) +#define clue_hash3(next)\ + &penum->clues[(next.v[0] + (next.v[1] << 2) + (next.v[2] << 4)) & 255]; +#define clue_hash4(next)\ + &penum->clues[(next.v[0] + (next.v[1] << 2) + (next.v[2] << 4) +\ + (next.v[3] << 6)) & 255] + + if (spp == 4) { /* cmyk or rgba */ + next.v[0] = psrc[0]; + next.v[1] = psrc[1]; + next.v[2] = psrc[2]; + next.v[3] = psrc[3]; + psrc += 4; +map4: if (next.all == run.all || paint_no_pixels()) + goto inc; + if (use_cache) { + pic_next = clue_hash4(next); + if (pic_next->key == next.all) + goto f; + /* + * If we are really unlucky, pic_next == pic, + * so mapping this color would clobber the one + * we're about to use for filling the run. + */ + if (pic_next == pic) { + clue_temp = *pic; + pic = &clue_temp; + } + pic_next->key = next.all; + } + if (device_color) { + (*map_4)(byte2frac(next.v[0]), byte2frac(next.v[1]), + byte2frac(next.v[2]), byte2frac(next.v[3]), + pdevc_next, pis, dev, + gs_color_select_source); + goto mapped; + } + decode_sample(next.v[3], cc, 3); + if_debug1('B', "[B]cc[3]=%g\n", cc.paint.values[3]); + } else if (spp == 3) { /* rgb */ + next.v[0] = psrc[0]; + next.v[1] = psrc[1]; + next.v[2] = psrc[2]; + psrc += 3; + if (next.all == run.all || paint_no_pixels()) + goto inc; + if (use_cache) { + pic_next = clue_hash3(next); + if (pic_next->key == next.all) + goto f; + /* See above re the following check. */ + if (pic_next == pic) { + clue_temp = *pic; + pic = &clue_temp; + } + pic_next->key = next.all; + } + if (device_color) { + (*map_3)(byte2frac(next.v[0]), byte2frac(next.v[1]), + byte2frac(next.v[2]), + pdevc_next, pis, dev, + gs_color_select_source); + goto mapped; + } + } else if (spp == 2) { /* gray+alpha */ + next.v[2] = next.v[1] = next.v[0] = psrc[0]; + next.v[3] = psrc[1]; + psrc += 2; + goto map4; + } else { /* spp == 5, cmyk+alpha */ + /* Convert CMYK to RGB. */ + frac rgb[3]; + + color_cmyk_to_rgb(byte2frac(psrc[0]), byte2frac(psrc[1]), + byte2frac(psrc[2]), byte2frac(psrc[3]), + pis, rgb); + /* + * It seems silly to do all this converting between + * fracs and bytes, but that's what the current + * APIs require. + */ + next.v[0] = frac2byte(rgb[0]); + next.v[1] = frac2byte(rgb[1]); + next.v[2] = frac2byte(rgb[2]); + next.v[3] = psrc[4]; + psrc += 5; + goto map4; + } + decode_sample(next.v[0], cc, 0); + decode_sample(next.v[1], cc, 1); + decode_sample(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]); + (*remap_color) (&cc, pcs, pdevc_next, pis, dev, + gs_color_select_source); +mapped: if (pic == pic_next) + 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(*pdevc, *pdevc_next)) + goto set; +fill: { /* Fill the region between */ + /* xrun/irun and xl */ + switch (posture) { + case image_portrait: + { /* 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, vci, + wi, vdi, pdevc, dev, lop); + xrun = xl; /* for sake of final run */ + } + break; + case image_landscape: + { /* 90 degree rotated rectangle */ + int yi = irun; + int hi = + (irun = fixed2int_var_rounded(ytf)) - yi; + + if (hi < 0) + yi += hi, hi = -hi; + code = gx_fill_rectangle_device_rop(vci, yi, + vdi, hi, pdevc, dev, lop); + yrun = ytf; /* for sake of final run */ + } + break; + default: + { /* Parallelogram */ + code = (*dev_proc(dev, fill_parallelogram)) + (dev, xrun, yrun, + xl - xrun, ytf - yrun, pdyx, pdyy, + pdevc, lop); + xrun = xl; + yrun = ytf; + } + } + if (code < 0) + return code; + if (use_cache) + pic = pic_next; + else { + gx_image_clue *ptemp = pic; + + pic = pic_next; + pic_next = ptemp; + } + } +set: run.all = next.all; +inc: xl = xn; + ytf = yn; /* harmless if no skew */ +#undef xn +#undef yn + } + /* Fill the last run. */ + code = (*dev_proc(dev, fill_parallelogram)) + (dev, xrun, yrun, xl - xrun, ytf - yrun, pdyx, pdyy, pdevc, lop); + return (code < 0 ? code : 1); +} diff --git a/gs/src/gxidata.c b/gs/src/gxidata.c new file mode 100644 index 000000000..1d374552e --- /dev/null +++ b/gs/src/gxidata.c @@ -0,0 +1,235 @@ +/* Copyright (C) 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: gxidata.c */ +/* Generic image enumeration and cleanup */ +#include "gx.h" +#include "memory_.h" +#include "gserrors.h" +#include "gxdevice.h" +#include "gxcpath.h" +#include "gximage.h" + +/* Process the next piece of an ImageType 1 image. */ +int +gx_image1_plane_data(gx_device * dev, + gx_image_enum_common_t * info, const gx_image_plane_t * planes, int height) +{ + gx_image_enum *penum = (gx_image_enum *) info; + int y = penum->y; + int y_end = min(y + height, penum->rect.h); + int width_spp = penum->rect.w * penum->spp; + int num_planes = penum->num_planes; + +#define bcount(plane) /* bytes per data row */\ + (((penum->rect.w + (plane).data_x) * penum->spp / num_planes * penum->bps\ + + 7) >> 3) + fixed adjust = penum->adjust; + ulong offsets[gs_image_max_components]; + int ignore_data_x; + int code; + + if (height == 0) + return 0; + + /* Set up the clipping and/or RasterOp device if needed. */ + + if (penum->clip_dev) { + gx_device_clip *cdev = penum->clip_dev; + + cdev->target = dev; + dev = (gx_device *) cdev; + } + if (penum->rop_dev) { + gx_device_rop_texture *rtdev = penum->rop_dev; + + ((gx_device_forward *) rtdev)->target = dev; + dev = (gx_device *) rtdev; + } + /* Now render complete rows. */ + + memset(offsets, 0, num_planes * sizeof(offsets[0])); + for (; penum->y < y_end; penum->y++) { + int px; + + /* + * Normally, we unpack the data into the buffer, but if + * there is only one plane and we don't need to expand the + * input samples, we may use the data directly. + */ + int sourcex = planes[0].data_x; + const byte *buffer = + (*penum->unpack) (penum->buffer, &sourcex, + planes[0].data + offsets[0], + planes[0].data_x, bcount(planes[0]), + &penum->map[0].table, penum->spread); + + offsets[0] += planes[0].raster; + for (px = 1; px < num_planes; ++px) { + (*penum->unpack) (penum->buffer + (px << penum->log2_xbytes), + &ignore_data_x, + planes[px].data + offsets[px], + planes[px].data_x, bcount(planes[px]), + &penum->map[px].table, penum->spread); + offsets[px] += planes[px].raster; + } +#ifdef DEBUG + if (gs_debug_c('B')) { + int i, n = width_spp; + + dlputs("[B]row:"); + for (i = 0; i < n; i++) + dprintf1(" %02x", buffer[i]); + dputs("\n"); + } +#endif + penum->cur.x = dda_current(penum->dda.row.x); + dda_next(penum->dda.row.x); + penum->cur.y = dda_current(penum->dda.row.y); + dda_next(penum->dda.row.y); + if (!penum->interpolate) + switch (penum->posture) { + case image_portrait: + { /* Precompute integer y and height, */ + /* and check for clipping. */ + fixed yc = penum->cur.y, yn = dda_current(penum->dda.row.y); + + if (yn < yc) { + fixed temp = yn; + + yn = yc; + yc = temp; + } + yc -= adjust; + if (yc >= penum->clip_outer.q.y) + goto mt; + yn += adjust; + if (yn <= penum->clip_outer.p.y) + goto mt; + penum->yci = fixed2int_pixround(yc); + penum->hci = fixed2int_pixround(yn) - penum->yci; + if (penum->hci == 0) + goto mt; + } + break; + case image_landscape: + { /* Check for no pixel centers in x. */ + fixed xc = penum->cur.x, xn = dda_current(penum->dda.row.x); + + if (xn < xc) { + fixed temp = xn; + + xn = xc; + xc = temp; + } + xc -= adjust; + if (xc >= penum->clip_outer.q.x) + goto mt; + xn += adjust; + if (xn <= penum->clip_outer.p.x) + goto mt; + penum->xci = fixed2int_pixround(xc); + penum->wci = fixed2int_pixround(xn) - penum->xci; + if (penum->wci == 0) + goto mt; + } + break; + case image_skewed: + ; + } + dda_translate(penum->dda.pixel0.x, + penum->cur.x - penum->prev.x); + dda_translate(penum->dda.pixel0.y, + penum->cur.y - penum->prev.y); + penum->prev = penum->cur; + code = (*penum->render) (penum, buffer, sourcex, width_spp, 1, + dev); + if (code < 0) + goto err; + mt:; + } + if (penum->y < penum->rect.h) { + code = 0; + goto out; + } + /* End of data. Render any left-over buffered data. */ + switch (penum->posture) { + case image_portrait: + { + fixed yc = dda_current(penum->dda.row.y); + + penum->yci = fixed2int_rounded(yc - adjust); + penum->hci = fixed2int_rounded(yc + adjust) - penum->yci; + } + break; + case image_landscape: + { + fixed xc = dda_current(penum->dda.row.x); + + penum->xci = fixed2int_rounded(xc - adjust); + penum->wci = fixed2int_rounded(xc + adjust) - penum->xci; + } + break; + case image_skewed: /* pacify compilers */ + ; + } + dda_translate(penum->dda.pixel0.x, penum->cur.x - penum->prev.x); + dda_translate(penum->dda.pixel0.y, penum->cur.y - penum->prev.y); + code = (*penum->render) (penum, NULL, 0, width_spp, 0, dev); + if (code < 0) { + penum->y--; + goto err; + } + code = 1; + goto out; + err: /* Error or interrupt, restore original state. */ + while (penum->y > y) { + dda_previous(penum->dda.row.x); + dda_previous(penum->dda.row.y); + --(penum->y); + } + /* Note that caller must call end_image */ + /* for both error and normal termination. */ + out:return code; +} + +/* Clean up by releasing the buffers. */ +/* Currently we ignore draw_last. */ +int +gx_image1_end_image(gx_device * dev, gx_image_enum_common_t * info, + bool draw_last) +{ + gx_image_enum *penum = (gx_image_enum *) info; + gs_memory_t *mem = penum->memory; + stream_IScale_state *scaler = penum->scaler; + +#ifdef DEBUG + if_debug2('b', "[b]%send_image, y=%d\n", + (penum->y < penum->rect.h ? "premature " : ""), penum->y); +#endif + gs_free_object(mem, penum->rop_dev, "image RasterOp"); + gs_free_object(mem, penum->clip_dev, "image clipper"); + if (scaler != 0) { + (*s_IScale_template.release) ((stream_state *) scaler); + gs_free_object(mem, scaler, "image scaler state"); + } + gs_free_object(mem, penum->line, "image line"); + gs_free_object(mem, penum->buffer, "image buffer"); + gs_free_object(mem, penum, "gx_default_end_image"); + return 0; +} diff --git a/gs/src/gxifast.c b/gs/src/gxifast.c new file mode 100644 index 000000000..685c54f19 --- /dev/null +++ b/gs/src/gxifast.c @@ -0,0 +1,699 @@ +/* Copyright (C) 1989, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: gxifast.c */ +/* Fast monochrome image rendering */ +#include "gx.h" +#include "memory_.h" +#include "gpcheck.h" +#include "gsbittab.h" +#include "gserrors.h" +#include "gxfixed.h" +#include "gxarith.h" +#include "gxmatrix.h" +#include "gsccolor.h" +#include "gspaint.h" +#include "gsutil.h" +#include "gxdevice.h" +#include "gxcmap.h" +#include "gxdcolor.h" +#include "gxistate.h" +#include "gzpath.h" +#include "gxdevmem.h" +#include "gdevmem.h" /* for mem_mono_device */ +#include "gxcpath.h" +#include "gximage.h" +#include "gzht.h" + +/* Conditionally include statistics code. */ +#ifdef DEBUG +# define STATS +#endif + +/* ------ Strategy procedure ------ */ + +/* Use special fast logic for portrait or landscape black-and-white images. */ +private irender_proc(image_render_simple); +private irender_proc(image_render_landscape); +private irender_proc_t +image_strategy_simple(gx_image_enum * penum) +{ + irender_proc_t rproc; + fixed ox = dda_current(penum->dda.pixel0.x); + fixed oy = dda_current(penum->dda.pixel0.y); + + if (penum->use_rop || penum->spp != 1 || penum->bps != 1) + return 0; + switch (penum->posture) { + case image_portrait: + { /* Use fast portrait algorithm. */ + long dev_width = + fixed2long_pixround(ox + penum->x_extent.x) - + fixed2long_pixround(ox); + + if (dev_width != penum->rect.w) { /* Add an extra align_bitmap_mod of padding so that */ + /* we can align scaled rows with the device. */ + long line_size = + bitmap_raster(any_abs(dev_width)) + align_bitmap_mod; + + if (penum->adjust != 0 || line_size > max_uint) + return 0; + /* Must buffer a scan line. */ + penum->line_width = any_abs(dev_width); + penum->line_size = (uint) line_size; + penum->line = gs_alloc_bytes(penum->memory, + penum->line_size, "image line"); + if (penum->line == 0) { + gx_default_end_image(penum->dev, + (gx_image_enum_common_t *) penum, + false); + return 0; + } + } + if_debug2('b', "[b]render=simple, unpack=copy; rect.w=%d, dev_width=%ld\n", + penum->rect.w, dev_width); + rproc = image_render_simple; + break; + } + case image_landscape: + { /* Use fast landscape algorithm. */ + long dev_width = + fixed2long_pixround(oy + penum->x_extent.y) - + fixed2long_pixround(oy); + long line_size = + (dev_width = any_abs(dev_width), + bitmap_raster(dev_width) * 8 + + round_up(dev_width, 8) * align_bitmap_mod); + + if ((dev_width != penum->rect.w && penum->adjust != 0) || + line_size > max_uint + ) + return 0; + /* Must buffer a group of 8N scan lines. */ + penum->line_width = dev_width; + penum->line_size = (uint) line_size; + penum->line = gs_alloc_bytes(penum->memory, + penum->line_size, "image line"); + if (penum->line == 0) { + gx_default_end_image(penum->dev, + (gx_image_enum_common_t *) penum, + false); + return 0; + } + penum->xi_next = penum->line_xy = fixed2int_var_rounded(ox); + if_debug3('b', "[b]render=landscape, unpack=copy; rect.w=%d, dev_width=%ld, line_size=%ld\n", + penum->rect.w, dev_width, line_size); + rproc = image_render_landscape; + /* Precompute values needed for rasterizing. */ + penum->dxy = + float2fixed(penum->matrix.xy + fixed2float(fixed_epsilon) / 2); + break; + } + default: + return 0; + } + /* Precompute values needed for rasterizing. */ + penum->dxx = + float2fixed(penum->matrix.xx + fixed2float(fixed_epsilon) / 2); + /* We don't want to spread the samples, */ + /* but we have to reset unpack_bps to prevent the buffer */ + /* pointer from being incremented by 8 bytes */ + /* per input byte. */ + penum->unpack = sample_unpack_copy; + penum->unpack_bps = 8; + return rproc; +} + +void +gs_gxifast_init(gs_memory_t * mem) +{ + image_strategies.simple = image_strategy_simple; +} + +/* ------ Rendering procedures ------ */ + +/* + * Scale (and possibly reverse) one scan line of a monobit image. + * This is used for both portrait and landscape image processing. + * We pass in an x offset (0 <= line_x < align_bitmap_mod * 8) so that + * we can align the result with the eventual device X. + * + * To be precise, the input to this routine is the w bits starting at + * bit data_x in buffer. These w bits expand to abs(x_extent) bits, + * either inverted (zero = 0xff) or not (zero = 0), starting at bit + * line_x in line which corresponds to coordinate + * fixed2int_pixround(xcur + min(x_extent, 0)). Note that the entire + * bytes containing the first and last output bits are affected: the + * other bits in those bytes are set to zero (i.e., the value of the + * 'zero' argument). + */ +#ifdef STATS +struct stats_image_fast_s { + long + calls, all0s, all1s, runs, lbit0, byte00, byte01, byte02, byte03, + byte04, rbit0, lbit1, byte1, rbit1, thin, thin2, nwide, bwide, + nfill, bfill; +} stats_image_fast; + +# define incs(stat) ++stats_image_fast.stat +# define adds(stat, n) stats_image_fast.stat += n +#else +# define incs(stat) DO_NOTHING +# define adds(stat, n) DO_NOTHING +#endif +private void +image_simple_expand(byte * line, int line_x, uint raster, + const byte * buffer, int data_x, uint w, fixed xcur, fixed x_extent, + byte zero /* 0 or 0xff */ ) +{ + int dbitx = data_x & 7; + byte sbit = 0x80 >> dbitx; + byte sbitmask = 0xff >> dbitx; + uint wx = dbitx + w; + gx_dda_fixed xl; + gx_dda_step_fixed dxx4, dxx8, dxx16, dxx24, dxx32; + register const byte *psrc = buffer + (data_x >> 3); + + /* + * The following 3 variables define the end of the input data row. + * We would put them in a struct, except that no compiler that we + * know of will optimize individual struct members as though they + * were simple variables (e.g., by putting them in registers). + * + * endp points to the byte that contains the bit just beyond the + * end of the row. endx gives the bit number of this bit within + * the byte, with 0 being the *least* significant bit. endbit is + * a mask for this bit. + */ + const byte *endp = psrc + (wx >> 3); + int endx = ~wx & 7; + byte endbit = 1 << endx; + + /* + * The following 3 variables do the same for start of the last run + * of the input row (think of it as a pointer to just beyond the + * end of the next-to-last run). + */ + const byte *stop = endp; + int stopx; + byte stopbit = endbit; + byte data; + byte one = ~zero; + fixed xl0; + + if (w == 0) + return; + incs(calls); + +#define fill_row(value)\ + memset(line + (line_x >> 3), value, raster - (line_x >> 3)) + + /* Scan backward for the last transition. */ + if (stopbit == 0x80) + --stop, stopbit = 1; + else + stopbit <<= 1; + /* Now (stop, stopbit) give the last bit of the row. */ + { + byte stopmask = -stopbit << 1; + byte last = *stop; + + if (stop == psrc) /* only 1 input byte */ + stopmask &= sbitmask; + if (last & stopbit) { /* The last bit is a 1: look for a 0-to-1 transition. */ + if (~last & stopmask) { /* Transition in last byte. */ + last |= stopbit - 1; + } else { /* No transition in the last byte. */ + while (stop > psrc && stop[-1] == 0xff) + --stop; + if (stop == psrc || + (stop == psrc + 1 && !(~*psrc & sbitmask)) + ) { /* The input is all 1s. Clear the row and exit. */ + incs(all1s); + fill_row(one); + return; + } + last = *--stop; + } + stopx = byte_bit_run_length_0[byte_reverse_bits[last]] - 1; + } else { /* The last bit is a 0: look for a 1-to-0 transition. */ + if (last & stopmask) { /* Transition in last byte. */ + last &= -stopbit; + } else { /* No transition in the last byte. */ + while (stop > psrc && stop[-1] == 0) + --stop; + if (stop == psrc || + (stop == psrc + 1 && !(*psrc & sbitmask)) + ) { /* The input is all 0s. Clear the row and exit. */ + incs(all0s); + fill_row(zero); + return; + } + last = *--stop; + } + stopx = + byte_bit_run_length_0[byte_reverse_bits[last ^ 0xff]] - 1; + } + if (stopx < 0) + stopx = 7, ++stop; + stopbit = 1 << stopx; + } + + /* Pre-clear the row. */ + fill_row(zero); +#undef fill_row + + /* Set up the DDAs. */ + xl0 = + (x_extent >= 0 ? + fixed_fraction(fixed_pre_pixround(xcur)) : + fixed_fraction(fixed_pre_pixround(xcur + x_extent)) - x_extent); + xl0 += int2fixed(line_x); + dda_init(xl, xl0, x_extent, w); + dxx4 = xl.step; + dda_step_add(dxx4, xl.step); + dda_step_add(dxx4, dxx4); + dxx8 = dxx4; + dda_step_add(dxx8, dxx4); + dxx16 = dxx8; + dda_step_add(dxx16, dxx8); + dxx24 = dxx16; + dda_step_add(dxx24, dxx8); + dxx32 = dxx24; + dda_step_add(dxx32, dxx8); + + /* + * Loop invariants: + * data = *psrc; + * sbit = 1 << n, 0<=n<=7. + */ + for (data = *psrc;;) { + int x0, n, bit; + byte *bp; + static const byte lmasks[9] = + {0xff, 0x7f, 0x3f, 0x1f, 0xf, 7, 3, 1, 0}; + static const byte rmasks[9] = + {0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff}; + + incs(runs); + + /* Scan a run of zeros. */ + data ^= 0xff; /* invert */ + while (data & sbit) { + dda_next(xl); + sbit >>= 1; + incs(lbit0); + } + if (!sbit) { /* Scan a run of zero bytes. */ + sw:if ((data = psrc[1]) != 0) { + psrc++; + incs(byte00); + } else if ((data = psrc[2]) != 0) { + dda_state_next(xl.state, dxx8); + psrc += 2; + incs(byte01); + } else if ((data = psrc[3]) != 0) { + dda_state_next(xl.state, dxx16); + psrc += 3; + incs(byte02); + } else if ((data = psrc[4]) != 0) { + dda_state_next(xl.state, dxx24); + psrc += 4; + incs(byte03); + } else { + dda_state_next(xl.state, dxx32); + psrc += 4; + incs(byte04); + goto sw; + } + if (data > 0xf) + sbit = 0x80; + else { + sbit = 0x08; + dda_state_next(xl.state, dxx4); + } + data ^= 0xff; /* invert */ + while (data & sbit) { + dda_next(xl); + sbit >>= 1; + incs(rbit0); + } + } + x0 = dda_current_fixed2int(xl); + if (psrc >= stop && sbit == stopbit) { /* We've scanned the last run of 0s. */ + /* Prepare to fill the final run of 1s. */ + n = fixed2int(xl0 + x_extent) - x0; + } else { /* Scan a run of ones. */ + /* We know the current bit is a one. */ + data ^= 0xff; /* un-invert */ + do { + dda_next(xl); + sbit >>= 1; + incs(lbit1); + } + while (data & sbit); + if (!sbit) { /* Scan a run of 0xff bytes. */ + while ((data = *++psrc) == 0xff) { + dda_state_next(xl.state, dxx8); + incs(byte1); + } + if (data < 0xf0) + sbit = 0x80; + else { + sbit = 0x08; + dda_state_next(xl.state, dxx4); + } + while (data & sbit) { + dda_next(xl); + sbit >>= 1; + incs(rbit1); + } + } + n = dda_current_fixed2int(xl) - x0; + } + + /* Fill the run in the scan line. */ + if (n < 0) + x0 += n, n = -n; + bp = line + (x0 >> 3); + bit = x0 & 7; + if ((n += bit) <= 8) { + *bp ^= lmasks[bit] - lmasks[n]; + incs(thin); + } else if ((n -= 8) <= 8) { + *bp ^= lmasks[bit]; + bp[1] ^= rmasks[n]; + incs(thin2); + } else { + *bp++ ^= lmasks[bit]; + if (n >= 56) { + int nb = n >> 3; + + memset(bp, one, nb); + bp += nb; + incs(nwide); + adds(bwide, nb); + } else { + adds(bfill, n >> 3); + while ((n -= 8) >= 0) + *bp++ = one; + incs(nfill); + } + *bp ^= rmasks[n & 7]; + } + if (psrc >= stop && sbit == stopbit) + break; + } +} + +/* Copy one rendered scan line to the device. */ +/* We may expand this (or its fastest case) inline someday. */ +private int +copy_portrait(gx_image_enum * penum, const byte * data, int dx, int raster, + int x, int y, int w, int h, gx_device * dev) +{ + const gx_device_color *pdc0; + const gx_device_color *pdc1; + uint align = alignment_mod(data, align_bitmap_mod); + + /* + * We know that the lookup table maps 1 bit to 1 bit, + * so it can only have 2 states: straight-through or invert. + */ + if (penum->map[0].table.lookup4x1to32[0]) + pdc0 = &penum->icolor1, pdc1 = &penum->icolor0; + else + pdc0 = &penum->icolor0, pdc1 = &penum->icolor1; + data -= align; + dx += align << 3; + if (gx_dc_is_pure(pdc0) && gx_dc_is_pure(pdc1)) { /* Just use copy_mono. */ + dev_proc_copy_mono((*copy_mono)) = + (h == 1 || (raster & (align_bitmap_mod - 1)) == 0 ? + dev_proc(dev, copy_mono) : gx_copy_mono_unaligned); + return (*copy_mono) + (dev, data, dx, raster, gx_no_bitmap_id, + x, y, w, h, pdc0->colors.pure, pdc1->colors.pure); + } + /* At least one color isn't pure; use its fill_masked procedure. */ + { + const gx_device_color *pdc; + bool invert; + +#define dc_is_null(pdc)\ + (gx_dc_is_pure(pdc) && (pdc)->colors.pure == gx_no_color_index) + if (dc_is_null(pdc1)) { + pdc = pdc0; + invert = true; + } else { + if (!dc_is_null(pdc0)) { + int code = gx_device_color_fill_rectangle + (pdc0, x, y, w, h, dev, lop_default, NULL); + + if (code < 0) + return code; + } + pdc = pdc1; + invert = false; + } + return (*pdc->type->fill_masked) + (pdc, data, dx, raster, gx_no_bitmap_id, x, y, w, h, + dev, lop_default, invert); +#undef dc_is_null + } +} + +/* Rendering procedure for a monobit image with no */ +/* skew or rotation and pure colors. */ +private int +image_render_simple(gx_image_enum * penum, const byte * buffer, int data_x, + uint w, int h, gx_device * dev) +{ + dev_proc_copy_mono((*copy_mono)) = dev_proc(dev, copy_mono); + const fixed dxx = penum->dxx; + const byte *line; + uint line_width, line_size; + int line_x; + fixed xcur = dda_current(penum->dda.pixel0.x); + int ix = fixed2int_pixround(xcur); + const int iy = penum->yci, ih = penum->hci; + int dy; + +#define pdc0 (&penum->icolor0) +#define pdc1 (&penum->icolor1) + + if (h == 0) + return 0; + if (penum->line == 0) { /* A direct BitBlt is possible. */ + line = buffer; + line_size = (w + 7) >> 3; + line_width = w; + line_x = 0; + } else if (copy_mono == dev_proc(&mem_mono_device, copy_mono) && + dxx > 0 && gx_dc_is_pure(pdc1) && gx_dc_is_pure(pdc0) && + /* We know the colors must be (0,1) or (1,0). */ + (pdc0->colors.pure ^ pdc1->colors.pure) == 1 && + !penum->clip_image + ) { /* Do the operation directly into the memory device bitmap. */ + int ixr = fixed2int_pixround(xcur + w * dxx) - 1; + int line_ix; + int ib_left = ix >> 3, ib_right = ixr >> 3; + byte *scan_line = scan_line_base((gx_device_memory *) dev, iy); + byte save_left, save_right, mask; + + line_x = ix & (align_bitmap_mod * 8 - 1); + line_ix = ix - line_x; + line_size = (ixr >> 3) + 1 - (line_ix >> 3); + line_width = ixr + 1 - ix; + /* We must save and restore any unmodified bits in */ + /* the two edge bytes. */ + save_left = scan_line[ib_left]; + save_right = scan_line[ib_right]; + image_simple_expand(scan_line + (line_ix >> 3), line_x, + line_size, buffer, data_x, w, xcur, + penum->x_extent.x, + ((pdc0->colors.pure == 0) != + (penum->map[0].table.lookup4x1to32[0] == 0) ? + 0xff : 0)); + if (ix & 7) + mask = (byte) (0xff00 >> (ix & 7)), + scan_line[ib_left] = + (save_left & mask) + (scan_line[ib_left] & ~mask); + if ((ixr + 1) & 7) + mask = (byte) (0xff00 >> ((ixr + 1) & 7)), + scan_line[ib_right] = + (scan_line[ib_right] & mask) + (save_right & ~mask); + if (ih <= 1) + return 1; +/****** MAY BE UNALIGNED ******/ + line = scan_line + (line_ix >> 3); + if (dxx < 0) + ix -= line_width; + for (dy = 1; dy < ih; dy++) { + int code = (*copy_mono) (dev, line, line_x, line_size, + gx_no_bitmap_id, + ix, iy + dy, line_width, 1, + (gx_color_index) 0, + (gx_color_index) 1); + + if (code < 0) + return code; + } + return 0; + } else { + line = penum->line; + line_size = penum->line_size; + line_width = penum->line_width; + line_x = ix & (align_bitmap_mod * 8 - 1); + image_simple_expand(penum->line, line_x, line_size, + buffer, data_x, w, xcur, + penum->x_extent.x, 0); + } + + /* Finally, transfer the scan line to the device. */ + if (dxx < 0) + ix -= line_width; + for (dy = 0; dy < ih; dy++) { + int code = copy_portrait(penum, line, line_x, line_size, + ix, iy + dy, line_width, 1, dev); + + if (code < 0) + return code; + } + + return 1; +#undef pdc0 +#undef pdc1 +} + +/* Rendering procedure for a 90 degree rotated monobit image */ +/* with pure colors. We buffer and then flip 8 scan lines at a time. */ +private int copy_landscape(P5(gx_image_enum *, int, int, bool, gx_device *)); +private int +image_render_landscape(gx_image_enum * penum, const byte * buffer, int data_x, + uint w, int h, gx_device * dev) +{ + byte *line = penum->line; + uint raster = bitmap_raster(penum->line_width); + int ix = penum->xci, iw = penum->wci; + int xinc, xmod; + byte *row; + const byte *orig_row = 0; + bool y_neg = penum->dxy < 0; + + if (is_fneg(penum->matrix.yx)) + ix += iw, iw = -iw, xinc = -1; + else + xinc = 1; + /* + * Because of clipping, there may be discontinuous jumps in the + * values of ix (xci). If this happens, flush the flipping buffer. + */ + if (ix != penum->xi_next) { + int xi = penum->xi_next; + int code = + (xinc > 0 ? + copy_landscape(penum, penum->line_xy, xi, y_neg, dev) : + copy_landscape(penum, xi, penum->line_xy, y_neg, dev)); + + if (code < 0) + return code; + penum->line_xy = ix; + } + if (h != 0) { + for (; iw != 0; iw -= xinc) { + if (xinc < 0) + --ix; + xmod = ix & 7; + row = line + xmod * raster; + if (orig_row == 0) { + image_simple_expand(row, 0, raster, + buffer, data_x, w, + dda_current(penum->dda.pixel0.y), + penum->x_extent.y, 0); + orig_row = row; + } else + memcpy(row, orig_row, raster); + if (xinc > 0) { + ++ix; + if (xmod == 7) { + int code = + copy_landscape(penum, + penum->line_xy, ix, + y_neg, dev); + + if (code < 0) + return code; + orig_row = 0; + penum->line_xy = ix; + } + } else { + if (xmod == 0) { + int code = + copy_landscape(penum, ix, + penum->line_xy, + y_neg, dev); + + if (code < 0) + return code; + orig_row = 0; + penum->line_xy = ix; + } + } + } + penum->xi_next = ix; + return 0; + } else { /* Put out any left-over bits. */ + return + (xinc > 0 ? + copy_landscape(penum, penum->line_xy, ix, y_neg, dev) : + copy_landscape(penum, ix, penum->line_xy, y_neg, dev)); + } +} + +/* Flip and copy one group of scan lines. */ +private int +copy_landscape(gx_image_enum * penum, int x0, int x1, bool y_neg, + gx_device * dev) +{ + byte *line = penum->line; + uint line_width = penum->line_width; + uint raster = bitmap_raster(line_width); + byte *flipped = line + raster * 8; + int w = x1 - x0; + int y = fixed2int_pixround(dda_current(penum->dda.pixel0.y)); + + if (w == 0 || line_width == 0) + return 0; + /* Flip the buffered data from raster x 8 to align_bitmap_mod x */ + /* line_width. */ + if (line_width > 0) { + int i; + + for (i = (line_width - 1) >> 3; i >= 0; --i) + memflip8x8(line + i, raster, + flipped + (i << (log2_align_bitmap_mod + 3)), + align_bitmap_mod); + } + /* Transfer the scan lines to the device. */ + if (w < 0) + x0 = x1, w = -w; + if (y_neg) + y -= line_width; + return copy_portrait(penum, flipped, x0 & 7, align_bitmap_mod, + x0, y, w, line_width, dev); +} diff --git a/gs/src/gxiinit.c b/gs/src/gxiinit.c new file mode 100644 index 000000000..7a461e606 --- /dev/null +++ b/gs/src/gxiinit.c @@ -0,0 +1,911 @@ +/* Copyright (C) 1989, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: gxiinit.c */ +/* Image setup procedures for Ghostscript library */ +#include "gx.h" +#include "math_.h" +#include "memory_.h" +#include "gpcheck.h" +#include "gserrors.h" +#include "gsstruct.h" +#include "gsutil.h" +#include "gxfixed.h" +#include "gxfrac.h" +#include "gxarith.h" +#include "gxmatrix.h" +#include "gsccolor.h" +#include "gspaint.h" +#include "gzstate.h" +#include "gxdevice.h" +#include "gzpath.h" +#include "gzcpath.h" +#include "gxdevmem.h" +#include "gximage.h" +#include "gxiparam.h" +#include "gdevmrop.h" + +/* ---------------- Generic image support ---------------- */ + +/* Initialize the common parts of image structures. */ +void +gs_image_common_t_init(gs_image_common_t * pic) +{ + gs_make_identity(&pic->ImageMatrix); +} +void +gs_data_image_t_init(gs_data_image_t * pim, int num_components) +{ + int i; + + gs_image_common_t_init((gs_image_common_t *) pim); + pim->Width = pim->Height = 0; + pim->BitsPerComponent = 1; + if (num_components >= 0) { + for (i = 0; i < num_components * 2; i += 2) + pim->Decode[i] = 0, pim->Decode[i + 1] = 1; + } else { + for (i = 0; i < num_components * -2; i += 2) + pim->Decode[i] = 1, pim->Decode[i + 1] = 0; + } + pim->Interpolate = false; +} +void +gs_pixel_image_t_init(gs_pixel_image_t * pim, const gs_color_space * color_space) +{ + int num_components; + + if (color_space == 0 || + (num_components = + gs_color_space_num_components(color_space)) < 0 + ) + num_components = 0; + gs_data_image_t_init((gs_data_image_t *) pim, num_components); + pim->format = gs_image_format_chunky; + pim->ColorSpace = color_space; + pim->CombineWithColor = false; +} + +/* Initialize the common part of an image-processing enumerator. */ +int +gx_image_enum_common_init(gx_image_enum_common_t * piec, + const gs_image_common_t * pic, const gx_image_enum_procs_t * piep, + gx_device * dev, int bits_per_component, int num_components, + gs_image_format_t format) +{ + piec->image_type = pic->type; + piec->procs = piep; + piec->dev = dev; + piec->id = gs_next_ids(1); + switch (format) { + case gs_image_format_chunky: + piec->num_planes = 1; + piec->plane_depths[0] = bits_per_component * num_components; + break; + case gs_image_format_component_planar: + piec->num_planes = num_components; + { + int i; + + for (i = 0; i < num_components; ++i) + piec->plane_depths[i] = bits_per_component; + } + break; + case gs_image_format_bit_planar: + piec->num_planes = bits_per_component * num_components; + { + int i; + + for (i = 0; i < piec->num_planes; ++i) + piec->plane_depths[i] = 1; + } +#if 0 /* **************** */ + break; +#endif /* **************** */ + default: + return_error(gs_error_rangecheck); + } + return 0; +} + +/* ---------------- ImageType 1 images ---------------- */ + +/* Structure descriptors */ +private_st_gx_image_enum(); +public_st_gs_image_common(); +public_st_gs_data_image(); +public_st_gs_pixel_image(); + +/* Strategy procedures */ +gx_image_strategies_t image_strategies; + +/* Define the image type for ImageType 1 images. */ +private const gx_image_type_t image1_type = +{image1_type_data}; +private const gx_image_enum_procs_t image1_enum_procs = +{image1_enum_procs_data}; + +/* Define the procedures for initializing gs_image_ts to default values. */ +void +gs_image_t_init(gs_image_t * pim, const gs_color_space * color_space) +{ + gs_pixel_image_t_init((gs_pixel_image_t *) pim, color_space); + pim->type = &image1_type; + pim->ImageMask = pim->adjust = (color_space == NULL); + pim->Alpha = gs_image_alpha_none; +} +void +gs_image_t_init_mask(gs_image_t * pim, bool write_1s) +{ + gs_image_t_init(pim, NULL); + if (write_1s) + pim->Decode[0] = 1, pim->Decode[1] = 0; + else + pim->Decode[0] = 0, pim->Decode[1] = 1; +} + +/* Compute the source size of an ordinary image with explicit data. */ +int +gx_data_image_source_size(const gs_imager_state * pis, + const gs_image_common_t * pim, gs_int_point * psize) +{ + const gs_data_image_t *pdi = (const gs_data_image_t *)pim; + + psize->x = pdi->Width; + psize->y = pdi->Height; + return 0; +} + +/* Process the next piece of an image with no source data. */ +/* This procedure should never be called. */ +int +gx_no_image_plane_data(gx_device * dev, + gx_image_enum_common_t * info, const gx_image_plane_t * planes, int height) +{ + return_error(gs_error_Fatal); +} + +/* Clean up after processing an image with no source data. */ +/* This procedure may be called, but should do nothing. */ +int +gx_ignore_end_image(gx_device * dev, gx_image_enum_common_t * info, + bool draw_last) +{ + return 0; +} + +/* GC procedures */ +#define eptr ((gx_image_enum *)vptr) +private +ENUM_PTRS_BEGIN(image_enum_enum_ptrs) +{ + int bps; + gs_ptr_type_t ret; + + /* Enumerate the used members of clues.dev_color. */ + index -= gx_image_enum_num_ptrs; + bps = eptr->unpack_bps; + if (eptr->spp != 1) + bps = 8; + else if (bps > 8 || eptr->unpack == sample_unpack_copy) + bps = 1; + if (index >= (1 << bps) * st_device_color_max_ptrs) /* done */ + return 0; + ret = ENUM_USING(st_device_color, + &eptr->clues[(index / st_device_color_max_ptrs) * + (255 / ((1 << bps) - 1))].dev_color, + sizeof(eptr->clues[0].dev_color), + index % st_device_color_max_ptrs); + if (ret == 0) /* don't stop early */ + ENUM_RETURN(0); + return ret; +} +#define e1(i,elt) ENUM_PTR(i,gx_image_enum,elt); +gx_image_enum_do_ptrs(e1) +#undef e1 +ENUM_PTRS_END +private RELOC_PTRS_BEGIN(image_enum_reloc_ptrs) +{ + int i; + +#define r1(i,elt) RELOC_PTR(gx_image_enum,elt); + gx_image_enum_do_ptrs(r1) +#undef r1 + { + int bps = eptr->unpack_bps; + + if (eptr->spp != 1) + bps = 8; + else if (bps > 8 || eptr->unpack == sample_unpack_copy) + bps = 1; + for (i = 0; i <= 255; i += 255 / ((1 << bps) - 1)) + RELOC_USING(st_device_color, + &eptr->clues[i].dev_color, sizeof(gx_device_color)); + } +} +RELOC_PTRS_END +#undef eptr + +/* Forward declarations */ +private int color_draws_b_w(P2(gx_device * dev, + const gx_drawing_color * pdcolor)); +private void image_init_map(P3(byte * map, int map_size, const float *decode)); +private void image_init_colors(P9(gx_image_enum * penum, int bps, int spp, + bool multi, const float *decode, + const gs_imager_state * pis, gx_device * dev, + const gs_color_space * pcs, bool * pdcb)); + +/* Procedures for unpacking the input data into bytes or fracs. */ + /*extern sample_unpack_proc(sample_unpack_copy); *//* declared above */ +extern sample_unpack_proc(sample_unpack_1); +extern sample_unpack_proc(sample_unpack_2); +extern sample_unpack_proc(sample_unpack_4); +extern sample_unpack_proc(sample_unpack_8); + +sample_unpack_proc((*sample_unpack_12_proc)); /* optional */ + +/* Start processing an ImageType 1 image. */ +/* Note that since this is actually a begin_typed_image procedure, */ +/* the type of pim is the more abstract one. */ +int +gx_begin_image1(gx_device * dev, + const gs_imager_state * pis, const gs_matrix * pmat, + const gs_image_common_t * pic, const gs_int_rect * prect, + const gx_drawing_color * pdcolor, const gx_clip_path * pcpath, + gs_memory_t * mem, gx_image_enum_common_t ** pinfo) +{ + const gs_image_t *pim = (const gs_image_t *)pic; + gs_image_format_t format = pim->format; + gx_image_enum *penum; + const int width = pim->Width; + const int height = pim->Height; + const int bps = pim->BitsPerComponent; + bool masked = pim->ImageMask; + const float *decode = pim->Decode; + bool multi; + int index_bps; + const gs_color_space *pcs = pim->ColorSpace; + gs_logical_operation_t lop = (pis ? pis->log_op : lop_default); + int code; + gs_matrix mat; + int log2_xbytes = (bps <= 8 ? 0 : arch_log2_sizeof_frac); + int spp, nplanes, spread; + uint bsize; + byte *buffer; + fixed mtx, mty; + gs_fixed_point row_extent, col_extent, x_extent, y_extent; + bool device_color; + gs_fixed_rect obox, cbox; + fixed adjust; + + if (width < 0 || height < 0) + return_error(gs_error_rangecheck); + switch (format) { + case gs_image_format_chunky: + multi = false; + break; + case gs_image_format_component_planar: + multi = true; + break; + default: + return_error(gs_error_rangecheck); + } + switch (bps) { + case 1: + index_bps = 0; + break; + case 2: + index_bps = 1; + break; + case 4: + index_bps = 2; + break; + case 8: + index_bps = 3; + break; + case 12: + index_bps = 4; + break; + default: + return_error(gs_error_rangecheck); + } + if (prect) { + if (prect->p.x < 0 || prect->p.y < 0 || + prect->q.x < prect->p.x || prect->q.y < prect->p.y || + prect->q.x > width || prect->q.y > height + ) + return_error(gs_error_rangecheck); + } + if (pmat == 0) + pmat = &ctm_only(pis); + if ((code = gs_matrix_invert(&pim->ImageMatrix, &mat)) < 0 || + (code = gs_matrix_multiply(&mat, pmat, &mat)) < 0 || + (code = + gs_distance_transform2fixed((const gs_matrix_fixed *)&mat, + (floatp) width, (floatp) 0, + &row_extent)) < 0 || + (code = + gs_distance_transform2fixed((const gs_matrix_fixed *)&mat, + (floatp) 0, (floatp) height, + &col_extent)) < 0 + ) + return code; + penum = gs_alloc_struct(mem, gx_image_enum, &st_gx_image_enum, + "gx_default_begin_image"); + if (penum == 0) + return_error(gs_error_VMerror); + gx_image_enum_common_init((gx_image_enum_common_t *) penum, pic, + &image1_enum_procs, dev, bps, + (masked ? 1 : cs_num_components(pcs)), + format); + if (prect) { + penum->rect.x = prect->p.x, penum->rect.y = prect->p.y; + penum->rect.w = prect->q.x - prect->p.x, + penum->rect.h = prect->q.y - prect->p.y; + if ((code = + gs_distance_transform2fixed((const gs_matrix_fixed *)&mat, + (floatp) penum->rect.w, (floatp) 0, + &x_extent)) < 0 || + (code = + gs_distance_transform2fixed((const gs_matrix_fixed *)&mat, + (floatp) 0, (floatp) penum->rect.h, + &y_extent)) < 0 + ) { + gs_free_object(mem, penum, "gx_default_begin_image"); + return code; + } + } else { + penum->rect.x = 0, penum->rect.y = 0; + penum->rect.w = width, penum->rect.h = height; + x_extent = row_extent; + y_extent = col_extent; + } + if ((penum->masked = masked)) { /* This is imagemask. */ + if (bps != 1 || multi || pcs != NULL || pim->Alpha || + !((decode[0] == 0.0 && decode[1] == 1.0) || + (decode[0] == 1.0 && decode[1] == 0.0)) + ) { + gs_free_object(mem, penum, "gx_default_begin_image"); + return_error(gs_error_rangecheck); + } + /* Initialize color entries 0 and 255. */ + color_set_pure(&penum->icolor0, gx_no_color_index); + penum->icolor1 = *pdcolor; + memcpy(&penum->map[0].table.lookup4x1to32[0], + (decode[0] == 0 ? lookup4x1to32_inverted : + lookup4x1to32_identity), + 16 * 4); + penum->map[0].decoding = sd_none; + spp = 1; + adjust = (pim->adjust ? float2fixed(0.25) : fixed_0); + lop = rop3_know_S_0(lop); + } else { /* This is image, not imagemask. */ + const gs_color_space_type *pcst = pcs->type; + int b_w_color; + + spp = cs_num_components(pcs); + if (spp < 0) { /* Pattern not allowed */ + gs_free_object(mem, penum, "gx_default_begin_image"); + return_error(gs_error_rangecheck); + } + if (pim->Alpha) + ++spp; + if (spp == 1) + multi = false; + device_color = (*pcst->concrete_space) (pcs, pis) == pcs; + image_init_colors(penum, bps, spp, multi, decode, pis, dev, + pcs, &device_color); + adjust = fixed_0; + /* Try to transform non-default RasterOps to something */ + /* that we implement less expensively. */ + if (!pim->CombineWithColor) + lop = rop3_know_T_0(lop); + else { + if (rop3_uses_T(lop)) + switch (color_draws_b_w(dev, pdcolor)) { + case 0: + lop = rop3_know_T_0(lop); + break; + case 1: + lop = rop3_know_T_1(lop); + break; + default: + ; + } + } + if (lop != rop3_S && /* if best case, no more work needed */ + !rop3_uses_T(lop) && bps == 1 && spp == 1 && + (b_w_color = + color_draws_b_w(dev, &penum->icolor0)) >= 0 && + color_draws_b_w(dev, &penum->icolor1) == (b_w_color ^ 1) + ) { + if (b_w_color) { /* Swap the colors and invert the RasterOp source. */ + gx_device_color dcolor; + + dcolor = penum->icolor0; + penum->icolor0 = penum->icolor1; + penum->icolor1 = dcolor; + lop = rop3_invert_S(lop); + } + /* + * At this point, we know that the source pixels + * correspond directly to the S input for the raster op, + * i.e., icolor0 is black and icolor1 is white. + */ + switch (lop) { + case rop3_D & rop3_S: + /* Implement this as an inverted mask writing 0s. */ + penum->icolor1 = penum->icolor0; + /* (falls through) */ + case rop3_D | rop3_not(rop3_S): + /* Implement this as an inverted mask writing 1s. */ + memcpy(&penum->map[0].table.lookup4x1to32[0], + lookup4x1to32_inverted, 16 * 4); + rmask: /* Fill in the remaining parameters for a mask. */ + penum->masked = masked = true; + color_set_pure(&penum->icolor0, gx_no_color_index); + penum->map[0].decoding = sd_none; + lop = rop3_T; + break; + case rop3_D & rop3_not(rop3_S): + /* Implement this as a mask writing 0s. */ + penum->icolor1 = penum->icolor0; + /* (falls through) */ + case rop3_D | rop3_S: + /* Implement this as a mask writing 1s. */ + memcpy(&penum->map[0].table.lookup4x1to32[0], + lookup4x1to32_identity, 16 * 4); + goto rmask; + default: + ; + } + } + } + penum->device_color = device_color; + /* + * Adjust width upward for unpacking up to 7 trailing bits in + * the row, plus 1 byte for end-of-run, plus up to 7 leading + * bits for data_x offset within a packed byte. + */ + bsize = ((bps > 8 ? width * 2 : width) + 15) * spp; + buffer = gs_alloc_bytes(mem, bsize, "image buffer"); + if (buffer == 0) { + gs_free_object(mem, penum, "gx_default_begin_image"); + return_error(gs_error_VMerror); + } + penum->bps = bps; + penum->unpack_bps = bps; + penum->log2_xbytes = log2_xbytes; + penum->spp = spp; + penum->alpha = pim->Alpha; + nplanes = (multi ? spp : 1); + penum->num_planes = nplanes; + spread = nplanes << log2_xbytes; + penum->spread = spread; + penum->matrix = mat; + penum->x_extent = x_extent; + penum->y_extent = y_extent; + penum->posture = + ((x_extent.y | y_extent.x) == 0 ? image_portrait : + (x_extent.x | y_extent.y) == 0 ? image_landscape : + image_skewed); + mtx = float2fixed(mat.tx); + mty = float2fixed(mat.ty); + penum->pis = pis; + penum->pcs = pcs; + penum->memory = mem; + penum->buffer = buffer; + penum->buffer_size = bsize; + penum->line = 0; + penum->line_size = 0; + /* + * If we're asked to interpolate in a partial image, we have to + * assume that the client either really only is interested in + * the given sub-image, or else is constructing output out of + * overlapping pieces. + */ + penum->interpolate = pim->Interpolate; + penum->use_rop = lop != (masked ? rop3_T : rop3_S); +#ifdef DEBUG + if (gs_debug_c('*')) { + if (penum->use_rop) + dprintf1("[%03x]", lop); + dprintf5("%c%d%c%dx%d ", + (masked ? (color_is_pure(pdcolor) ? 'm' : 'h') : 'i'), + bps, + (penum->posture == image_portrait ? ' ' : + penum->posture == image_landscape ? 'L' : 'T'), + width, height); + } +#endif + penum->slow_loop = 0; + if (pcpath == 0) { + (*dev_proc(dev, get_clipping_box)) (dev, &obox); + cbox = obox; + penum->clip_image = 0; + } else + penum->clip_image = + (gx_cpath_outer_box(pcpath, &obox) | /* not || */ + gx_cpath_inner_box(pcpath, &cbox) ? + 0 : image_clip_region); + penum->clip_outer = obox; + penum->clip_inner = cbox; + penum->log_op = rop3_T; /* rop device takes care of this */ + penum->clip_dev = 0; /* in case we bail out */ + penum->rop_dev = 0; /* ditto */ + penum->scaler = 0; /* ditto */ + /* + * If all four extrema of the image fall within the clipping + * rectangle, clipping is never required. When making this check, + * we must carefully take into account the fact that we only care + * about pixel centers. + */ + { + fixed + epx = min(row_extent.x, 0) + min(col_extent.x, 0), + eqx = max(row_extent.x, 0) + max(col_extent.x, 0), + epy = min(row_extent.y, 0) + min(col_extent.y, 0), + eqy = max(row_extent.y, 0) + max(col_extent.y, 0); + + { + int hwx, hwy; + + switch (penum->posture) { + case image_portrait: + hwx = width, hwy = height; + break; + case image_landscape: + hwx = height, hwy = width; + break; + default: + hwx = hwy = 0; + } + /* + * If the image is only 1 sample wide or high, + * and is less than 1 device pixel wide or high, + * move it slightly so that it covers pixel centers. + * This is a hack to work around a bug in some old + * versions of TeX/dvips, which use 1-bit-high images + * to draw horizontal and vertical lines without + * positioning them properly. + */ + if (hwx == 1 && eqx - epx < fixed_1) { + fixed diff = + arith_rshift_1(row_extent.x + col_extent.x); + + mtx = (((mtx + diff) | fixed_half) & -fixed_half) - diff; + } + if (hwy == 1 && eqy - epy < fixed_1) { + fixed diff = + arith_rshift_1(row_extent.y + col_extent.y); + + mty = (((mty + diff) | fixed_half) & -fixed_half) - diff; + } + } + if_debug11('b', + "[b]Image: cbox=(%g,%g),(%g,%g), obox=(%g,%g),(%g,%g)\n mt=(%g,%g) clip_image=0x%x\n", + fixed2float(cbox.p.x), fixed2float(cbox.p.y), + fixed2float(cbox.q.x), fixed2float(cbox.q.y), + fixed2float(obox.p.x), fixed2float(obox.p.y), + fixed2float(obox.q.x), fixed2float(obox.q.y), + fixed2float(mtx), fixed2float(mty), + penum->clip_image); + dda_init(penum->dda.row.x, mtx, col_extent.x, height); + dda_init(penum->dda.row.y, mty, col_extent.y, height); + if (penum->rect.y) { + dda_advance(penum->dda.row.x, penum->rect.y); + dda_advance(penum->dda.row.y, penum->rect.y); + } + penum->cur.x = penum->prev.x = dda_current(penum->dda.row.x); + penum->cur.y = penum->prev.y = dda_current(penum->dda.row.y); + dda_init(penum->dda.pixel0.x, penum->cur.x, row_extent.x, + width); + dda_init(penum->dda.pixel0.y, penum->cur.y, row_extent.y, + width); + if (penum->rect.x) { + dda_advance(penum->dda.pixel0.x, penum->rect.x); + dda_advance(penum->dda.pixel0.y, penum->rect.x); + } { + fixed ox = dda_current(penum->dda.pixel0.x); + fixed oy = dda_current(penum->dda.pixel0.y); + + if (!penum->clip_image) /* i.e., not clip region */ + penum->clip_image = + (fixed_pixround(ox + epx) < fixed_pixround(cbox.p.x) ? + image_clip_xmin : 0) + + (fixed_pixround(ox + eqx) >= fixed_pixround(cbox.q.x) ? + image_clip_xmax : 0) + + (fixed_pixround(oy + epy) < fixed_pixround(cbox.p.y) ? + image_clip_ymin : 0) + + (fixed_pixround(oy + eqy) >= fixed_pixround(cbox.q.y) ? + image_clip_ymax : 0); + } + } + penum->y = 0; + penum->adjust = adjust; + { + static const sample_unpack_proc_t procs[4] = + { + sample_unpack_1, sample_unpack_2, + sample_unpack_4, sample_unpack_8 + }; + + if (index_bps == 4) { + if ((penum->unpack = sample_unpack_12_proc) == 0) { /* 12-bit samples are not supported. */ + gx_default_end_image(dev, + (gx_image_enum_common_t *) penum, + false); + return_error(gs_error_rangecheck); + } + } else { + penum->unpack = procs[index_bps]; + if_debug1('b', "[b]unpack=%d\n", bps); + } +#define use_strategy(sp)\ + (image_strategies.sp != 0 &&\ + (penum->render = (*image_strategies.sp)(penum)) != 0) + if ( + use_strategy(interpolate) || + use_strategy(simple) || + use_strategy(fracs) || + use_strategy(mono) || + use_strategy(color) + ) + DO_NOTHING; +#undef use_strategy + else { /* No available strategy can handle this image. */ + gx_default_end_image(dev, (gx_image_enum_common_t *) penum, + false); + return_error(gs_error_rangecheck); + } + } + if (penum->clip_image && pcpath) { /* Set up the clipping device. */ + gx_device_clip *cdev = + gs_alloc_struct(mem, gx_device_clip, + &st_device_clip, "image clipper"); + + if (cdev == 0) { + gx_default_end_image(dev, + (gx_image_enum_common_t *) penum, + false); + return_error(gs_error_VMerror); + } + gx_make_clip_device(cdev, cdev, gx_cpath_list(pcpath)); + cdev->target = dev; + (*dev_proc(cdev, open_device)) ((gx_device *) cdev); + penum->clip_dev = cdev; + } + if (penum->use_rop) { /* Set up the RasterOp source device. */ + gx_device_rop_texture *rtdev; + + code = gx_alloc_rop_texture_device(&rtdev, mem, + "image RasterOp"); + if (code < 0) { + gx_default_end_image(dev, (gx_image_enum_common_t *) penum, + false); + return code; + } + gx_make_rop_texture_device(rtdev, + (penum->clip_dev != 0 ? + (gx_device *) penum->clip_dev : + dev), lop, pdcolor); + penum->rop_dev = rtdev; + } +#ifdef DEBUG + if (gs_debug_c('b')) { + dlprintf2("[b]Image: w=%d h=%d", width, height); + if (prect) + dprintf4(" ((%d,%d),(%d,%d))", + prect->p.x, prect->p.y, prect->q.x, prect->q.y); + dprintf6(" [%g %g %g %g %g %g]\n", + mat.xx, mat.xy, mat.yx, mat.yy, mat.tx, mat.ty); + } +#endif + *pinfo = (gx_image_enum_common_t *) penum; + return 0; +} + +/* If a drawing color is black or white, return 0 or 1 respectively, */ +/* otherwise return -1. */ +private int +color_draws_b_w(gx_device * dev, const gx_drawing_color * pdcolor) +{ + if (color_is_pure(pdcolor)) { + gx_color_value rgb[3]; + + (*dev_proc(dev, map_color_rgb)) (dev, gx_dc_pure_color(pdcolor), + rgb); + if (!(rgb[0] | rgb[1] | rgb[2])) + return 0; + if ((rgb[0] & rgb[1] & rgb[2]) == gx_max_color_value) + return 1; + } + return -1; +} + +/* Initialize the color mapping tables for a non-mask image. */ +private void +image_init_colors(gx_image_enum * penum, int bps, int spp, bool multi, + const float *decode /*[spp*2] */ , const gs_imager_state * pis, gx_device * dev, + const gs_color_space * pcs, bool * pdcb) +{ + int ci; + static const float default_decode[] = + { + 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0 + }; + + /* Initialize the color table */ + +#define ictype(i)\ + penum->clues[i].dev_color.type + switch ((spp == 1 ? bps : 8)) { + case 8: /* includes all color images */ + { + register gx_image_clue *pcht = &penum->clues[0]; + register int n = 64; + + do { + pcht[0].dev_color.type = + pcht[1].dev_color.type = + pcht[2].dev_color.type = + pcht[3].dev_color.type = + gx_dc_type_none; + pcht[0].key = pcht[1].key = + pcht[2].key = pcht[3].key = 0; + pcht += 4; + } + while (--n > 0); + penum->clues[0].key = 1; /* guarantee no hit */ + break; + } + case 4: + ictype(17) = ictype(2 * 17) = ictype(3 * 17) = + ictype(4 * 17) = ictype(6 * 17) = ictype(7 * 17) = + ictype(8 * 17) = ictype(9 * 17) = ictype(11 * 17) = + ictype(12 * 17) = ictype(13 * 17) = ictype(14 * 17) = + gx_dc_type_none; + /* falls through */ + case 2: + ictype(5 * 17) = ictype(10 * 17) = gx_dc_type_none; +#undef ictype + } + + /* Initialize the maps from samples to intensities. */ + + for (ci = 0; ci < spp; ci++) { + sample_map *pmap = &penum->map[ci]; + + /* If the decoding is [0 1] or [1 0], we can fold it */ + /* into the expansion of the sample values; */ + /* otherwise, we have to use the floating point method. */ + + const float *this_decode = &decode[ci * 2]; + const float *map_decode; /* decoding used to */ + + /* construct the expansion map */ + + const float *real_decode; /* decoding for */ + + /* expanded samples */ + + bool no_decode; + + map_decode = real_decode = this_decode; + if (map_decode[0] == 0.0 && map_decode[1] == 1.0) + no_decode = true; + else if (map_decode[0] == 1.0 && map_decode[1] == 0.0) + no_decode = true, + real_decode = default_decode; + else + no_decode = false, + *pdcb = false, + map_decode = default_decode; + if (bps > 2 || multi) { + if (bps <= 8) + image_init_map(&pmap->table.lookup8[0], 1 << bps, + map_decode); + } else { /* The map index encompasses more than one pixel. */ + byte map[4]; + register int i; + + image_init_map(&map[0], 1 << bps, map_decode); + switch (bps) { + case 1: + { + register bits32 *p = &pmap->table.lookup4x1to32[0]; + + if (map[0] == 0 && map[1] == 0xff) + memcpy((byte *) p, lookup4x1to32_identity, 16 * 4); + else if (map[0] == 0xff && map[1] == 0) + memcpy((byte *) p, lookup4x1to32_inverted, 16 * 4); + else + for (i = 0; i < 16; i++, p++) + ((byte *) p)[0] = map[i >> 3], + ((byte *) p)[1] = map[(i >> 2) & 1], + ((byte *) p)[2] = map[(i >> 1) & 1], + ((byte *) p)[3] = map[i & 1]; + } + break; + case 2: + { + register bits16 *p = &pmap->table.lookup2x2to16[0]; + + for (i = 0; i < 16; i++, p++) + ((byte *) p)[0] = map[i >> 2], + ((byte *) p)[1] = map[i & 3]; + } + break; + } + } + pmap->decode_base /* = decode_lookup[0] */ = real_decode[0]; + pmap->decode_factor = + (real_decode[1] - real_decode[0]) / + (bps <= 8 ? 255.0 : (float)frac_1); + pmap->decode_max /* = decode_lookup[15] */ = real_decode[1]; + if (no_decode) + pmap->decoding = sd_none; + else if (bps <= 4) { + int step = 15 / ((1 << bps) - 1); + int i; + + pmap->decoding = sd_lookup; + for (i = 15 - step; i > 0; i -= step) + pmap->decode_lookup[i] = pmap->decode_base + + i * (255.0 / 15) * pmap->decode_factor; + } else + pmap->decoding = sd_compute; + if (spp == 1) { /* and ci == 0 *//* Pre-map entries 0 and 255. */ + gs_client_color cc; + + cc.paint.values[0] = real_decode[0]; + (*pcs->type->remap_color) (&cc, pcs, &penum->icolor0, + pis, dev, gs_color_select_source); + cc.paint.values[0] = real_decode[1]; + (*pcs->type->remap_color) (&cc, pcs, &penum->icolor1, + pis, dev, gs_color_select_source); + } + } + +} +/* Construct a mapping table for sample values. */ +/* map_size is 2, 4, 16, or 256. Note that 255 % (map_size - 1) == 0, */ +/* so the division 0xffffL / (map_size - 1) is always exact. */ +private void +image_init_map(byte * map, int map_size, const float *decode) +{ + float min_v = decode[0]; + float diff_v = decode[1] - min_v; + + if (diff_v == 1 || diff_v == -1) { /* We can do the stepping with integers, without overflow. */ + byte *limit = map + map_size; + uint value = min_v * 0xffffL; + int diff = diff_v * (0xffffL / (map_size - 1)); + + for (; map != limit; map++, value += diff) + *map = value >> 8; + } else { /* Step in floating point, with clamping. */ + int i; + + for (i = 0; i < map_size; ++i) { + int value = (int)((min_v + diff_v * i / (map_size - 1)) * 255); + + map[i] = (value < 0 ? 0 : value > 255 ? 255 : value); + } + } +} diff --git a/gs/src/gximono.c b/gs/src/gximono.c new file mode 100644 index 000000000..e47e2e6a8 --- /dev/null +++ b/gs/src/gximono.c @@ -0,0 +1,477 @@ +/* Copyright (C) 1989, 1995, 1996, 1997 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: gximono.c */ +/* General mono-component image rendering */ +#include "gx.h" +#include "memory_.h" +#include "gpcheck.h" +#include "gserrors.h" +#include "gxfixed.h" +#include "gxarith.h" +#include "gxmatrix.h" +#include "gsccolor.h" +#include "gspaint.h" +#include "gsutil.h" +#include "gxdevice.h" +#include "gxcmap.h" +#include "gxdcolor.h" +#include "gxistate.h" +#include "gzpath.h" +#include "gxdevmem.h" +#include "gdevmem.h" /* for mem_mono_device */ +#include "gxcpath.h" +#include "gximage.h" +#include "gzht.h" + +/* ------ Strategy procedure ------ */ + +/* We can bypass X clipping for portrait mono-component images. */ +private irender_proc(image_render_mono); +private irender_proc_t +image_strategy_mono(gx_image_enum * penum) +{ /* Use slow loop for imagemask with a halftone, */ + /* or for a non-default logical operation. */ + penum->slow_loop = + (penum->masked && !color_is_pure(&penum->icolor1)) || + penum->use_rop; + if (penum->spp == 1) { + if (!(penum->slow_loop || penum->posture != image_portrait)) + penum->clip_image &= ~(image_clip_xmin | image_clip_xmax); + if_debug0('b', "[b]render=mono\n"); + /* Precompute values needed for rasterizing. */ + penum->dxx = + float2fixed(penum->matrix.xx + fixed2float(fixed_epsilon) / 2); + return image_render_mono; + } + return 0; +} + +void +gs_gximono_init(gs_memory_t * mem) +{ + image_strategies.mono = image_strategy_mono; +} + +/* ------ Rendering procedure ------ */ + +/* Provide a fake map_gray procedure for the DevicePixel color space. */ +private void +no_map_gray(frac pixel, gx_device_color * pdc, + const gs_imager_state * pis, gx_device * dev, gs_color_select_t select) +{ + color_set_pure(pdc, frac2byte(pixel)); +} + +/* + * Rendering procedure for general mono-component images, dealing with + * multiple bit-per-sample images, general transformations, and arbitrary + * single-component color spaces (DeviceGray, DevicePixel, CIEBasedA, + * Separation, Indexed). This procedure handles a single scan line. + */ +private int +image_render_mono(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; + const int masked = penum->masked; + const gs_color_space *pcs; /* only set for non-masks */ + + cmap_proc_gray((*map_gray)); /* ditto */ + cs_proc_remap_color((*remap_color)); /* ditto */ + gs_client_color cc; + gx_device_color *pdevc = &penum->icolor1; /* color for masking */ + + /* Make sure the cache setup matches the graphics state. */ + /* Also determine whether all tiles fit in the cache. */ + int tiles_fit = gx_check_tile_cache(pis); + +#define image_set_gray(sample_value)\ + { pdevc = &penum->clues[sample_value].dev_color;\ + if ( !color_is_set(pdevc) )\ + { if ( penum->device_color )\ + (*map_gray)(byte2frac(sample_value), pdevc, pis, dev, gs_color_select_source);\ + else\ + { decode_sample(sample_value, cc, 0);\ + (*remap_color)(&cc, pcs, pdevc, pis, dev, gs_color_select_source);\ + }\ + }\ + else if ( !color_is_pure(pdevc) )\ + { if ( !tiles_fit )\ + { code = gx_color_load_select(pdevc, pis, dev, gs_color_select_source);\ + if ( code < 0 ) return code;\ + }\ + }\ + } + gx_dda_fixed_point next; /* (y not used in fast loop) */ + gx_dda_step_fixed dxx2, dxx3, dxx4; /* (not used in all loops) */ + register const byte *psrc = buffer + data_x; + const byte *endp = psrc + w; + const byte *stop = endp; + fixed xrun; /* x at start of run */ + register byte run; /* run value */ + int htrun = /* halftone run value */ + (masked ? 255 : -2); + int code = 0; + + if (h == 0) + return 0; + next = penum->dda.pixel0; + xrun = dda_current(next.x); + if (!masked) { + pcs = penum->pcs; /* (may not be set for masks) */ + if (penum->device_color) + map_gray = + (gs_color_space_get_index(pcs) == + gs_color_space_index_DeviceGray ? + gx_device_cmap_procs(dev)->map_gray : + no_map_gray /*DevicePixel */ ); + else + remap_color = pcs->type->remap_color; + } + run = *psrc; + /* Find the last transition in the input. */ + { + byte last = stop[-1]; + + while (stop > psrc && stop[-1] == last) + --stop; + } + if (penum->slow_loop || penum->posture != image_portrait) { /* Skewed, rotated, or imagemask with a halftone. */ + fixed yrun; + const fixed pdyx = dda_current(penum->dda.row.x) - penum->cur.x; + const fixed pdyy = dda_current(penum->dda.row.y) - penum->cur.y; + + dev_proc_fill_parallelogram((*fill_pgram)) = + dev_proc(dev, fill_parallelogram); + +#define xl dda_current(next.x) +#define ytf dda_current(next.y) + yrun = ytf; + if (masked) { + pdevc = &penum->icolor1; + code = gx_color_load(pdevc, pis, dev); + if (code < 0) + return code; + if (stop <= psrc) + goto last; + if (penum->posture == image_portrait) { /* We don't have to worry about the Y DDA, */ + /* and the fill regions are rectangles. */ + /* Calculate multiples of the DDA step. */ + dxx2 = next.x.step; + dda_step_add(dxx2, next.x.step); + dxx3 = dxx2; + dda_step_add(dxx3, next.x.step); + dxx4 = dxx3; + dda_step_add(dxx4, next.x.step); + for (;;) { /* Skip a run of zeros. */ + while (!psrc[0]) + if (!psrc[1]) { + if (!psrc[2]) { + if (!psrc[3]) { + psrc += 4; + dda_state_next(next.x.state, dxx4); + continue; + } + psrc += 3; + dda_state_next(next.x.state, dxx3); + break; + } + psrc += 2; + dda_state_next(next.x.state, dxx2); + break; + } else { + ++psrc; + dda_next(next.x); + break; + } + xrun = xl; + if (psrc >= stop) + break; + for (; *psrc; ++psrc) + dda_next(next.x); + code = (*fill_pgram) (dev, xrun, yrun, xl - xrun, + fixed_0, fixed_0, pdyy, + pdevc, lop); + if (code < 0) + return code; + if (psrc >= stop) + break; + } + } else + for (;;) { + for (; !*psrc; ++psrc) { + dda_next(next.x); + dda_next(next.y); + } + yrun = ytf; + xrun = xl; + if (psrc >= stop) + break; + for (; *psrc; ++psrc) { + dda_next(next.x); + dda_next(next.y); + } + code = (*fill_pgram) (dev, xrun, yrun, xl - xrun, + ytf - yrun, pdyx, pdyy, pdevc, lop); + if (code < 0) + return code; + if (psrc >= stop) + break; + } + } else if (penum->posture == image_portrait || + penum->posture == image_landscape + ) { /* Not masked, and we can fill runs quickly. */ + if (stop <= psrc) + goto last; + for (;;) { + if (*psrc != run) { + if (run != htrun) { + htrun = run; + image_set_gray(run); + } + code = (*fill_pgram) (dev, xrun, yrun, xl - xrun, + ytf - yrun, pdyx, pdyy, + pdevc, lop); + if (code < 0) + return code; + yrun = ytf; + xrun = xl; + if (psrc >= stop) + break; + run = *psrc; + } + psrc++; + dda_next(next.x); + dda_next(next.y); + } + } else { /* not masked *//* Since we have to check for the end after every pixel */ + /* anyway, we may as well avoid the last-run code. */ + stop = endp; + for (;;) { /* We can't skip large constant regions quickly, */ + /* because this leads to rounding errors. */ + /* Just fill the region between xrun and xl. */ + psrc++; + if (run != htrun) { + htrun = run; + image_set_gray(run); + } + code = (*fill_pgram) (dev, xrun, yrun, xl - xrun, + ytf - yrun, pdyx, pdyy, pdevc, lop); + if (code < 0) + return code; + yrun = ytf; + xrun = xl; + if (psrc > stop) { + --psrc; + break; + } + run = psrc[-1]; + dda_next(next.x); + dda_next(next.y); /* harmless if no skew */ + } + } + /* Fill the last run. */ + last:if (stop < endp && (*stop || !masked)) { + if (!masked) { + image_set_gray(*stop); + } + dda_advance(next.x, endp - stop); + dda_advance(next.y, endp - stop); + code = (*fill_pgram) (dev, xrun, yrun, xl - xrun, + ytf - yrun, pdyx, pdyy, pdevc, lop); + } +#undef xl +#undef ytf + } else { /* fast loop *//* No skew, and not imagemask with a halftone. */ + const fixed adjust = penum->adjust; + const fixed dxx = penum->dxx; + fixed xa = (dxx >= 0 ? adjust : -adjust); + const int yt = penum->yci, iht = penum->hci; + + dev_proc_fill_rectangle((*fill_proc)) = + dev_proc(dev, fill_rectangle); + dev_proc_strip_tile_rectangle((*tile_proc)) = + dev_proc(dev, strip_tile_rectangle); + dev_proc_copy_mono((*copy_mono_proc)) = + dev_proc(dev, copy_mono); + /* + * If each pixel is likely to fit in a single halftone tile, + * determine that now (tile_offset = offset of row within tile). + * Don't do this for band devices; they handle halftone fills + * more efficiently than copy_mono. + */ + int bstart; + int phase_x; + int tile_offset = + ((*dev_proc(dev, get_band)) (dev, yt, &bstart) == 0 ? + gx_check_tile_size(pis, + fixed2int_ceiling(any_abs(dxx) + (xa << 1)), + yt, iht, gs_color_select_source, &phase_x) : + -1); + int xmin = fixed2int_pixround(penum->clip_outer.p.x); + int xmax = fixed2int_pixround(penum->clip_outer.q.x); + +#define xl dda_current(next.x) + /* Fold the adjustment into xrun and xl, */ + /* including the +0.5-epsilon for rounding. */ + xrun = xrun - xa + (fixed_half - fixed_epsilon); + dda_translate(next.x, xa + (fixed_half - fixed_epsilon)); + xa <<= 1; + /* Calculate multiples of the DDA step. */ + dxx2 = next.x.step; + dda_step_add(dxx2, next.x.step); + dxx3 = dxx2; + dda_step_add(dxx3, next.x.step); + dxx4 = dxx3; + dda_step_add(dxx4, next.x.step); + if (stop > psrc) + for (;;) { /* Skip large constant regions quickly, */ + /* but don't slow down transitions too much. */ + skf:if (psrc[0] == run) { + if (psrc[1] == run) { + if (psrc[2] == run) { + if (psrc[3] == run) { + psrc += 4; + dda_state_next(next.x.state, dxx4); + goto skf; + } else { + psrc += 4; + dda_state_next(next.x.state, dxx3); + } + } else { + psrc += 3; + dda_state_next(next.x.state, dxx2); + } + } else { + psrc += 2; + dda_next(next.x); + } + } else + psrc++; + { /* Now fill the region between xrun and xl. */ + int xi = fixed2int_var(xrun); + int wi = fixed2int_var(xl) - xi; + int xei, tsx; + const gx_strip_bitmap *tile; + + if (wi <= 0) { + if (wi == 0) + goto mt; + xi += wi, wi = -wi; + } + if ((xei = xi + wi) > xmax || xi < xmin) { /* Do X clipping */ + if (xi < xmin) + wi -= xmin - xi, xi = xmin; + if (xei > xmax) + wi -= xei - xmax; + if (wi <= 0) + goto mt; + } + switch (run) { + case 0: + if (masked) + goto mt; + if (!color_is_pure(&penum->icolor0)) + goto ht; + code = (*fill_proc) (dev, xi, yt, wi, iht, + penum->icolor0.colors.pure); + break; + case 255: /* just for speed */ + if (!color_is_pure(&penum->icolor1)) + goto ht; + code = (*fill_proc) (dev, xi, yt, wi, iht, + penum->icolor1.colors.pure); + break; + default: + ht: /* Use halftone if needed */ + if (run != htrun) { + image_set_gray(run); + htrun = run; + } + /* We open-code gx_fill_rectangle, */ + /* because we've done some of the work for */ + /* halftone tiles in advance. */ + if (color_is_pure(pdevc)) { + code = (*fill_proc) (dev, xi, yt, wi, iht, + pdevc->colors.pure); + } else if (!color_is_binary_halftone(pdevc)) { + code = + gx_fill_rectangle_device_rop(xi, yt, wi, iht, + pdevc, dev, lop); + } else if (tile_offset >= 0 && + (tile = &pdevc->colors.binary.b_tile->tiles, + (tsx = (xi + phase_x) % tile->rep_width) + wi <= tile->size.x) + ) { /* The pixel(s) fit(s) in a single (binary) tile. */ + byte *row = tile->data + tile_offset; + + code = (*copy_mono_proc) + (dev, row, tsx, tile->raster, gx_no_bitmap_id, + xi, yt, wi, iht, + pdevc->colors.binary.color[0], + pdevc->colors.binary.color[1]); + } else { + code = (*tile_proc) (dev, + &pdevc->colors.binary.b_tile->tiles, + xi, yt, wi, iht, + pdevc->colors.binary.color[0], + pdevc->colors.binary.color[1], + pdevc->phase.x, pdevc->phase.y); + } + } + if (code < 0) + return code; + mt:xrun = xl - xa; /* original xa << 1 */ + if (psrc > stop) { + --psrc; + break; + } + run = psrc[-1]; + } + dda_next(next.x); + } + /* Fill the last run. */ + if (*stop != 0 || !masked) { + int xi = fixed2int_var(xrun); + int wi, xei; + + dda_advance(next.x, endp - stop); + wi = fixed2int_var(xl) - xi; + if (wi <= 0) { + if (wi == 0) + goto lmt; + xi += wi, wi = -wi; + } + if ((xei = xi + wi) > xmax || xi < xmin) { /* Do X clipping */ + if (xi < xmin) + wi -= xmin - xi, xi = xmin; + if (xei > xmax) + wi -= xei - xmax; + if (wi <= 0) + goto lmt; + } + image_set_gray(*stop); + code = gx_fill_rectangle_device_rop(xi, yt, wi, iht, + pdevc, dev, lop); + lmt:; + } + } +#undef xl + return (code < 0 ? code : 1); +} diff --git a/gs/src/gxiscale.c b/gs/src/gxiscale.c new file mode 100644 index 000000000..7d3846f38 --- /dev/null +++ b/gs/src/gxiscale.c @@ -0,0 +1,243 @@ +/* Copyright (C) 1996, 1997, 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: gxiscale.c */ +/* Interpolated image procedures */ +#include "gx.h" +#include "math_.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 "gzpath.h" +#include "gxdevmem.h" +#include "gxcpath.h" +#include "gximage.h" + +/* ------ Strategy procedure ------ */ + +/* If we're interpolating, use special logic. */ +private irender_proc(image_render_interpolate); +private irender_proc_t +image_strategy_interpolate(gx_image_enum * penum) +{ + const gs_imager_state *pis = penum->pis; + gs_memory_t *mem = penum->memory; + stream_IScale_state iss; + stream_IScale_state *pss; + byte *line; + const gs_color_space *pcs = penum->pcs; + const gs_color_space *pccs; + gs_point dst_xy; + + if (!penum->interpolate) + return 0; + if (penum->posture != image_portrait || penum->masked || + penum->alpha + ) { + /* We can't handle these cases yet. Punt. */ + penum->interpolate = false; + return 0; + } + iss.memory = mem; + /* Non-ANSI compilers require the following casts: */ + gs_distance_transform((float)penum->rect.w, (float)penum->rect.h, + &penum->matrix, &dst_xy); + if (penum->bps <= 8 && penum->device_color) { + iss.BitsPerComponentIn = 8; + iss.MaxValueIn = 0xff; + } else { + iss.BitsPerComponentIn = sizeof(frac) * 8; + iss.MaxValueIn = frac_1; + } + iss.BitsPerComponentOut = sizeof(frac) * 8; + iss.MaxValueOut = frac_1; + iss.WidthOut = (int)ceil(fabs(dst_xy.x)); + iss.HeightOut = (int)ceil(fabs(dst_xy.y)); + iss.WidthIn = penum->rect.w; + iss.HeightIn = penum->rect.h; + pccs = cs_concrete_space(pcs, pis); + iss.Colors = cs_num_components(pccs); + /* Allocate a buffer for one source/destination line. */ + { + uint in_size = + iss.WidthIn * iss.Colors * (iss.BitsPerComponentIn / 8); + uint out_size = + iss.WidthOut * iss.Colors * + max(iss.BitsPerComponentOut / 8, sizeof(gx_color_index)); + + line = gs_alloc_bytes(mem, max(in_size, out_size), + "image scale src line"); + } + pss = gs_alloc_struct(mem, stream_IScale_state, + &st_IScale_state, "image scale state"); + if (line == 0 || pss == 0 || + (*pss = iss, + (*s_IScale_template.init) ((stream_state *) pss) < 0) + ) { + gs_free_object(mem, pss, "image scale state"); + gs_free_object(mem, line, "image scale src line"); + /* Try again without interpolation. */ + penum->interpolate = false; + return 0; + } + penum->line = line; + penum->scaler = pss; + penum->line_xy = 0; + penum->xyi.x = fixed2int_pixround(dda_current(penum->dda.pixel0.x)); + penum->xyi.y = fixed2int_pixround(dda_current(penum->dda.pixel0.y)); + if_debug0('b', "[b]render=interpolate\n"); + return image_render_interpolate; +} + +void +gs_gxiscale_init(gs_memory_t * mem) +{ + image_strategies.interpolate = image_strategy_interpolate; +} + +/* ------ Rendering for interpolated images ------ */ + +private int +image_render_interpolate(gx_image_enum * penum, const byte * buffer, + int data_x, uint iw, int h, gx_device * dev) +{ + stream_IScale_state *pss = penum->scaler; + const gs_imager_state *pis = penum->pis; + const gs_color_space *pcs = penum->pcs; + gs_logical_operation_t lop = penum->log_op; + int c = pss->Colors; + stream_cursor_read r; + stream_cursor_write w; + + if (h != 0) { + /* Convert the unpacked data to concrete values in */ + /* the source buffer. */ + uint row_size = pss->WidthIn * c * pss->sizeofPixelIn; + const byte *bdata = buffer + data_x * c * pss->sizeofPixelIn; + + if (pss->sizeofPixelIn == 1) { + /* Easy case: 8-bit device color values. */ + r.ptr = bdata - 1; + } else { + /* Messy case: concretize each sample. */ + int bps = penum->bps; + int dc = penum->spp; + const byte *pdata = bdata; + frac *psrc = (frac *) penum->line; + gs_client_color cc; + int i; + + r.ptr = (byte *) psrc - 1; + for (i = 0; i < pss->WidthIn; i++, psrc += c) { + int j; + + if (bps <= 8) + for (j = 0; j < dc; ++pdata, ++j) { + decode_sample(*pdata, cc, j); + } else /* bps == 12 */ + for (j = 0; j < dc; pdata += sizeof(frac), ++j) { + decode_frac(*(const frac *)pdata, cc, j); + } + (*pcs->type->concretize_color) (&cc, pcs, psrc, + pis); + } + } + r.limit = r.ptr + row_size; + } else /* h == 0 */ + r.ptr = 0, r.limit = 0; + + /* + * Process input and/or collect output. By construction, the pixels are + * 1-for-1 with the device, but the Y coordinate might be inverted. + */ + + { + int xo = penum->xyi.x; + int yo = penum->xyi.y; + int width = pss->WidthOut; + int dy; + const gs_color_space *pconcs = cs_concrete_space(pcs, pis); + int bpp = dev->color_info.depth; + uint raster = bitmap_raster(width * bpp); + + if (penum->matrix.yy > 0) + dy = 1; + else + dy = -1, yo--; + for (;;) { + int ry = yo + penum->line_xy * dy; + int x; + const frac *psrc; + gx_device_color devc; + int code; + + declare_line_accum(penum->line, bpp, xo); + + w.limit = penum->line + width * c * + sizeof(gx_color_index) - 1; + w.ptr = w.limit - width * c * + (sizeof(gx_color_index) - pss->sizeofPixelOut); + psrc = (const frac *)(w.ptr + 1); + code = (*s_IScale_template.process) + ((stream_state *) pss, &r, &w, h == 0); + if (code < 0 && code != EOFC) + return_error(gs_error_ioerror); + if (w.ptr == w.limit) { + for (x = xo; x < xo + width; x++, psrc += c) { + (*pconcs->type->remap_concrete_color) + (psrc, &devc, pis, dev, + gs_color_select_source); + if (color_is_pure(&devc)) { + /* Just pack colors into a scan line. */ + gx_color_index color = devc.colors.pure; + + line_accum(color, bpp); + } else { + int rcode; + + line_accum_copy(dev, penum->line, bpp, + xo, x, raster, ry); + rcode = gx_fill_rectangle_device_rop(x, ry, + 1, 1, &devc, dev, lop); + if (rcode < 0) + return rcode; + line_accum_skip(bpp); + l_xprev = x + 1; + } + } + line_accum_copy(dev, penum->line, bpp, + xo, x, raster, ry); + penum->line_xy++; + } + if ((code == 0 && r.ptr == r.limit) || code == EOFC) + break; + } + } + + return (h == 0 ? 0 : 1); +} diff --git a/gs/src/gxropc.h b/gs/src/gxropc.h new file mode 100644 index 000000000..cb3485c84 --- /dev/null +++ b/gs/src/gxropc.h @@ -0,0 +1,47 @@ +/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: gxropc.h */ +/* Internals for RasterOp compositing */ + +#ifndef gxropc_INCLUDED +# define gxropc_INCLUDED + +#include "gsropc.h" +#include "gxcomp.h" + +/* Define RasterOp-compositing objects. */ +typedef struct gs_composite_rop_s { + gs_composite_common; + gs_composite_rop_params_t params; +} gs_composite_rop_t; + +#define private_st_composite_rop() /* in gsropc.c */\ + gs_private_st_ptrs1(st_composite_rop, gs_composite_rop_t,\ + "gs_composite_rop_t", composite_rop_enum_ptrs, composite_rop_reloc_ptrs,\ + params.texture) + +/* + * Initialize a RasterOp compositing function from parameters. + * We make this visible so that clients can allocate gs_composite_rop_t + * objects on the stack, to reduce memory manager overhead. + */ +void gx_init_composite_rop(P2(gs_composite_rop_t * pcte, + const gs_composite_rop_params_t * params)); + +#endif /* gxropc_INCLUDED */ diff --git a/gs/src/gxshade.c b/gs/src/gxshade.c new file mode 100644 index 000000000..3d763062c --- /dev/null +++ b/gs/src/gxshade.c @@ -0,0 +1,363 @@ +/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: gxshade.c */ +/* Shading rendering support */ +#include "math_.h" +#include "gx.h" +#include "gserrors.h" +#include "gsrect.h" +#include "gxcspace.h" +#include "gscie.h" /* requires gscspace.h */ +#include "gxdevcli.h" +#include "gxistate.h" +#include "gxdht.h" /* for computing # of different colors */ +#include "gxpaint.h" +#include "gxshade.h" + +/* ================ Packed coordinate streams ================ */ + +/* Forward references */ +private int cs_next_packed_value(P3(shade_coord_stream_t *, int, uint *)); +private int cs_next_array_value(P3(shade_coord_stream_t *, int, uint *)); +private int cs_next_packed_decoded(P4(shade_coord_stream_t *, int, + const float[2], float *)); +private int cs_next_array_decoded(P4(shade_coord_stream_t *, int, + const float[2], float *)); + +/* Initialize a packed value stream. */ +void +shade_next_init(shade_coord_stream_t * cs, + const gs_shading_mesh_params_t * params, const gs_imager_state * pis) +{ + cs->params = params; + cs->pctm = &pis->ctm; + if (data_source_is_stream(params->DataSource)) { + cs->s = params->DataSource.data.strm; + } else { + sread_string(&cs->ds, params->DataSource.data.str.data, + params->DataSource.data.str.size); + cs->s = &cs->ds; + } + if (data_source_is_array(params->DataSource)) + cs->get_value = cs_next_array_value, + cs->get_decoded = cs_next_array_decoded; + else + cs->get_value = cs_next_packed_value, + cs->get_decoded = cs_next_packed_decoded; + cs->left = 0; +} + +/* Get the next (integer) value from a packed value stream. */ +/* 1 <= num_bits <= sizeof(uint) * 8. */ +private int +cs_next_packed_value(shade_coord_stream_t * cs, int num_bits, uint * pvalue) +{ + uint bits = cs->bits; + int left = cs->left; + + if (left >= num_bits) { + /* We can satisfy this request with the current buffered bits. */ + cs->left = left -= num_bits; + *pvalue = (bits >> left) & ((1 << num_bits) - 1); + return 0; + } + /* We need more bits. */ + { + int needed = num_bits - left; + uint value = bits & ((1 << left) - 1); /* all the remaining bits */ + + for (; needed >= 8; needed -= 8) { + int b = sgetc(cs->s); + + if (b < 0) + return_error(gs_error_rangecheck); + value = (value << 8) + b; + } + if (needed == 0) { + cs->left = 0; + *pvalue = value; + return 0; + } { + int b = sgetc(cs->s); + + if (b < 0) + return_error(gs_error_rangecheck); + cs->bits = b; + cs->left = left = 8 - needed; + *pvalue = (value << needed) + (b >> left); + return 0; + } + } +} + +/* Get the next (integer) value from an unpacked array. */ +private int +cs_next_array_value(shade_coord_stream_t * cs, int num_bits, uint * pvalue) +{ + float value; + uint read; + + if (sgets(cs->s, (byte *) & value, sizeof(float), &read) < 0 || + read != sizeof(float) || value < 0 || value >= (1 << num_bits) || + value != (int)value + ) + return_error(gs_error_rangecheck); + + *pvalue = (uint) value; + return 0; +} + +/* Get the next decoded floating point value. */ +private int +cs_next_packed_decoded(shade_coord_stream_t * cs, int num_bits, + const float decode[2], float *pvalue) +{ + uint value; + int code = next_value(cs, num_bits, &value); + double max_value = (double)(uint) ((1 << num_bits) - 1); + + if (code < 0) + return code; + *pvalue = + (decode == 0 ? value / max_value : + decode[0] + value * (decode[1] - decode[0]) / max_value); + return 0; +} + +/* Get the next floating point value from an array, without decoding. */ +private int +cs_next_array_decoded(shade_coord_stream_t * cs, int num_bits, + const float decode[2], float *pvalue) +{ + float value; + uint read; + + if (sgets(cs->s, (byte *) & value, sizeof(float), &read) < 0 || + read != sizeof(float) + ) + return_error(gs_error_rangecheck); + + *pvalue = value; + return 0; +} + +/* Get the next flag value. */ +/* Note that this always starts a new data byte. */ +int +shade_next_flag(shade_coord_stream_t * cs, int BitsPerFlag) +{ + uint flag; + int code; + + cs->left = 0; /* start a new byte if packed */ + code = next_value(cs, BitsPerFlag, &flag); + return (code < 0 ? code : flag); +} + +/* Get one or more coordinate pairs. */ +int +shade_next_coords(shade_coord_stream_t * cs, gs_fixed_point * ppt, + int num_points) +{ + int num_bits = cs->params->BitsPerCoordinate; + const float *decode = cs->params->Decode; + int code = 0; + int i; + + for (i = 0; i < num_points; ++i) { + float x, y; + + if ((code = next_decoded(cs, num_bits, decode, &x)) < 0 || + (code = next_decoded(cs, num_bits, decode, &y)) < 0 || + (code = gs_point_transform2fixed(cs->pctm, x, y, &ppt[i])) < 0 + ) + break; + } + return code; +} + +/* Get a color. Currently all this does is look up Indexed colors. */ +int +shade_next_color(shade_coord_stream_t * cs, float *pc) +{ + const float *decode = cs->params->Decode + 4; /* skip coord decode */ + const gs_color_space *pcs = cs->params->ColorSpace; + gs_color_space_index index = gs_color_space_get_index(pcs); + int num_bits = cs->params->BitsPerComponent; + + if (index == gs_color_space_index_Indexed) { + uint i; + int code = next_value(cs, num_bits, &i); + + if (code < 0) + return code; +/****** DO INDEXED LOOKUP TO pc[] ******/ + } else { + int i, code; + int ncomp = gs_color_space_num_components(pcs); + + for (i = 0; i < ncomp; ++i) + if ((code = next_decoded(cs, num_bits, decode + i * 2, &pc[i])) < 0) + return code; + } + return 0; +} + +/* Get the next vertex for a mesh element. */ +int +shade_next_vertex(shade_coord_stream_t * cs, mesh_vertex_t * vertex) +{ + int code = shade_next_coords(cs, &vertex->p, 1); + + if (code >= 0) + code = shade_next_color(cs, vertex->cc); + return code; +} + +/* ================ Shading rendering ================ */ + +/* Fill a (user space) rectangle with a shading. */ +int +gs_shading_fill_rectangle(const gs_shading_t * psh, const gs_rect * rect, + gx_device * dev, gs_imager_state * pis) +{ + gs_rect box; + const gs_rect *prect; + + if (psh->params.have_BBox) { + box = *rect; + rect_intersect(box, psh->params.BBox); + prect = &box; + } else { + prect = rect; + } + return (*psh->head.fill_rectangle) (psh, rect, dev, pis); +} + +/* Initialize the common parts of the recursion state. */ +void +shade_init_fill_state(shading_fill_state_t * pfs, const gs_shading_t * psh, + gx_device * dev, gs_imager_state * pis) +{ + const gs_color_space *pcs = psh->params.ColorSpace; + float max_error = pis->smoothness; + /* + * There's no point in trying to achieve smoothness beyond what + * the device can implement, i.e., the number of representable + * colors times the size of the halftone cell. + */ + long num_colors = + max(dev->color_info.max_gray, dev->color_info.max_color) + 1; + const gs_range *ranges = 0; + int ci; + + pfs->dev = dev; + pfs->pis = pis; + pfs->num_components = gs_color_space_num_components(pcs); +top: + switch ( gs_color_space_get_index(pcs) ) + { + case gs_color_space_index_Indexed: + pcs = gs_cspace_base_space(pcs); + goto top; + case gs_color_space_index_CIEDEFG: + ranges = pcs->params.defg->RangeDEFG.ranges; + break; + case gs_color_space_index_CIEDEF: + ranges = pcs->params.def->RangeDEF.ranges; + break; + case gs_color_space_index_CIEABC: + ranges = pcs->params.abc->RangeABC.ranges; + break; + case gs_color_space_index_CIEA: + ranges = &pcs->params.a->RangeA; + break; + default: + break; + } + if (num_colors <= 32) { + /****** WRONG FOR MULTI-PLANE HALFTONES ******/ + num_colors *= pis->dev_ht->order.num_levels; + } + if (max_error < 1.0 / num_colors) + max_error = 1.0 / num_colors; + for (ci = 0; ci < pfs->num_components; ++ci) + pfs->cc_max_error[ci] = + (ranges == 0 ? max_error : + max_error * (ranges[ci].rmax - ranges[ci].rmin)); +} + +/* Transform a bounding box into device space. */ +int +shade_bbox_transform2fixed(const gs_rect * rect, const gs_imager_state * pis, + gs_fixed_rect * rfixed) +{ + gs_rect dev_rect; + int code = gs_bbox_transform(rect, &ctm_only(pis), &dev_rect); + + if (code >= 0) { + rfixed->p.x = float2fixed(dev_rect.p.x); + rfixed->p.y = float2fixed(dev_rect.p.y); + rfixed->q.x = float2fixed(dev_rect.q.x); + rfixed->q.y = float2fixed(dev_rect.q.y); + } + return code; +} + +/* Check whether 4 colors fall within the smoothness criterion. */ +bool +shade_colors4_converge(const gs_client_color cc[4], + const shading_fill_state_t * pfs) +{ + int ci; + + for (ci = 0; ci < pfs->num_components; ++ci) { + float + c0 = cc[0].paint.values[ci], c1 = cc[1].paint.values[ci], + c2 = cc[2].paint.values[ci], c3 = cc[3].paint.values[ci]; + float min01, max01, min23, max23; + + if (c0 < c1) + min01 = c0, max01 = c1; + else + min01 = c1, max01 = c0; + if (c2 < c3) + min23 = c2, max23 = c3; + else + min23 = c3, max23 = c2; + if (max(max01, max23) - min(min01, min23) > pfs->cc_max_error[ci]) + return false; + } + return true; +} + +/* Fill one piece of a shading. */ +int +shade_fill_path(const shading_fill_state_t * pfs, gx_path * ppath, + gx_device_color * pdevc) +{ + gx_fill_params params; + + params.rule = -1; /* irrelevant */ + params.adjust = pfs->pis->fill_adjust; + params.flatness = 0; /* irrelevant */ + params.fill_zero_width = false; + return (*dev_proc(pfs->dev, fill_path)) (pfs->dev, pfs->pis, ppath, + ¶ms, pdevc, NULL); +} diff --git a/gs/src/gxshade.h b/gs/src/gxshade.h new file mode 100644 index 000000000..9c6798212 --- /dev/null +++ b/gs/src/gxshade.h @@ -0,0 +1,257 @@ +/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: gxshade.h */ +/* Internal definitions for shading rendering */ + +#ifndef gxshade_INCLUDED +# define gxshade_INCLUDED + +#include "gsshade.h" +#include "gxfixed.h" /* for gxmatrix.h */ +#include "gxmatrix.h" /* for gs_matrix_fixed */ +#include "stream.h" + +/* + All shadings are defined with respect to some parameter that varies + continuously over some range; the shading defines a mapping from the + parameter values to colors and user space coordinates. Here are the + mappings for the 7 currently defined shading types: + + Type Param space Param => color Param => User space + ---- ----------- -------------- ------------------- + 1 2-D Domain Function Matrix + 2 1-D Domain Function + Extend perp. to Coords + 3 1-D Domain Function + Extend circles per Coords + 4,5 triangle x Gouraud interp. on Gouraud interp. on + 2-D in tri. Decode => corner triangle corners + values => Function + 6 patch x (u,v) Decode => bilinear Sc + Sd - Sb on each patch + in patch interp. on corner + values => Function + 7 see 6 see 6 Sum(i) Sum(j) Pij*Bi(u)*Bj(v) + + To be able to render a portion of a shading usefully, we must be able to + do two things: + + - Determine what range of parameter values is sufficient to cover + the region being filled; + + - Evaluate the color at enough points to fill the region (in + device space). + + Note that the latter may be implemented by a mix of evaluation and + interpolation, especially for types 3, 6, and 7 where an exact mapping + may be prohibitively expensive. + + Except for type 3, where circles turn into ellipses, the CTM can get + folded into the parameter => user space mapping, since in all other + cases, the mapping space is closed under linear transformations of + the output. + */ + +/* Define types and rendering procedures for the individual shadings. */ +typedef struct gs_shading_Fb_s { + gs_shading_head_t head; + gs_shading_Fb_params_t params; +} gs_shading_Fb_t; + +shading_fill_rectangle_proc(gs_shading_Fb_fill_rectangle); +typedef struct gs_shading_A_s { + gs_shading_head_t head; + gs_shading_A_params_t params; +} gs_shading_A_t; + +shading_fill_rectangle_proc(gs_shading_A_fill_rectangle); +typedef struct gs_shading_R_s { + gs_shading_head_t head; + gs_shading_R_params_t params; +} gs_shading_R_t; + +shading_fill_rectangle_proc(gs_shading_R_fill_rectangle); +typedef struct gs_shading_FfGt_s { + gs_shading_head_t head; + gs_shading_FfGt_params_t params; +} gs_shading_FfGt_t; + +shading_fill_rectangle_proc(gs_shading_FfGt_fill_rectangle); +typedef struct gs_shading_LfGt_s { + gs_shading_head_t head; + gs_shading_LfGt_params_t params; +} gs_shading_LfGt_t; + +shading_fill_rectangle_proc(gs_shading_LfGt_fill_rectangle); +typedef struct gs_shading_Cp_s { + gs_shading_head_t head; + gs_shading_Cp_params_t params; +} gs_shading_Cp_t; + +shading_fill_rectangle_proc(gs_shading_Cp_fill_rectangle); +typedef struct gs_shading_Tpp_s { + gs_shading_head_t head; + gs_shading_Tpp_params_t params; +} gs_shading_Tpp_t; + +shading_fill_rectangle_proc(gs_shading_Tpp_fill_rectangle); + +/* We should probably get this from somewhere else.... */ +#define max_color_components 4 + +/* Define a stream for decoding packed coordinate values. */ +typedef struct shade_coord_stream_s shade_coord_stream_t; +struct shade_coord_stream_s { + stream ds; /* stream if DataSource isn't one already -- */ + /* first for GC-ability (maybe unneeded?) */ + stream *s; /* DataSource or &ds */ + uint bits; /* shifted bits of current byte */ + int left; /* # of bits left in bits */ + const gs_shading_mesh_params_t *params; + const gs_matrix_fixed *pctm; +#define next_value(cs, num_bits, pvalue)\ + ((*(cs)->get_value)(cs, num_bits, pvalue)) + int (*get_value) (P3(shade_coord_stream_t *, int, uint *)); +#define next_decoded(cs, num_bits, decode, pvalue)\ + ((*(cs)->get_decoded)(cs, num_bits, decode, pvalue)) + int (*get_decoded) (P4(shade_coord_stream_t *, int, const float[2], + float *)); +}; + +/* Define one vertex of a mesh. */ +typedef struct mesh_vertex_s { + gs_fixed_point p; + float cc[max_color_components]; +} mesh_vertex_t; + +/* Initialize a packed value stream. */ +void shade_next_init(P3(shade_coord_stream_t * cs, + const gs_shading_mesh_params_t * params, + const gs_imager_state * pis)); + +/* Get the next flag value. */ +int shade_next_flag(P2(shade_coord_stream_t * cs, int BitsPerFlag)); + +/* Get one or more coordinate pairs. */ +int shade_next_coords(P3(shade_coord_stream_t * cs, gs_fixed_point * ppt, + int num_points)); + +/* Get a color. Currently all this does is look up Indexed colors. */ +int shade_next_color(P2(shade_coord_stream_t * cs, float *pc)); + +/* Get the next vertex for a mesh element. */ +int shade_next_vertex(P2(shade_coord_stream_t * cs, mesh_vertex_t * vertex)); + +/* + Currently, all shading fill procedures follow the same algorithm: + + - Conservatively inverse-transform the rectangle being filled to a linear + or rectangular range of values in the parameter space. + + - Compute the color values at the extrema of the range. + + - If possible, compute the parameter range corresponding to a single + device pixel. + + - Recursively do the following, passing the parameter range and extremal + color values as the recursion arguments: + + - If the color values are equal to within the tolerance given by the + smoothness in the graphics state, or if the range of parameters maps + to a single device pixel, fill the range with the (0) or (0,0) color. + + - Otherwise, subdivide and recurse. If the parameter range is 2-D, + subdivide the axis with the largest color difference. + + For shadings based on a function, if the function is not monotonic, the + smoothness test must only be applied when the parameter range extrema are + all interpolated from the same entries in the Function. + + */ + +/* + * Fill a (user space) rectangle with a shading. This is the only procedure + * in this file meant to be called from outside the shading machinery. + */ +shading_fill_rectangle_proc(gs_shading_fill_rectangle); + +/* Define the common structure for recursive subdivision. */ +#define shading_fill_state_common\ + gx_device *dev;\ + gs_imager_state *pis;\ + int num_components; /* # of color components */\ + float cc_max_error[max_color_components] +typedef struct shading_fill_state_s { + shading_fill_state_common; +} shading_fill_state_t; + +/* Initialize the common parts of the recursion state. */ +void shade_init_fill_state(P4(shading_fill_state_t * pfs, + const gs_shading_t * psh, gx_device * dev, + gs_imager_state * pis)); + +/* Transform a bounding box into device space. */ +int shade_bbox_transform2fixed(P3(const gs_rect * rect, + const gs_imager_state * pis, + gs_fixed_rect * rfixed)); + +/* Check whether 4 colors fall within the smoothness criterion. */ +bool shade_colors4_converge(P2(const gs_client_color cc[4], + const shading_fill_state_t * pfs)); + +/* Fill one piece of a shading. */ +#ifndef gx_device_color_DEFINED +# define gx_device_color_DEFINED +typedef struct gx_device_color_s gx_device_color; + +#endif +#ifndef gx_path_DEFINED +# define gx_path_DEFINED +typedef struct gx_path_s gx_path; + +#endif +int shade_fill_path(P3(const shading_fill_state_t * pfs, gx_path * ppath, + gx_device_color * pdevc)); + +#endif /* gxshade_INCLUDED */ + +#if 0 /*************************************************************** */ + +/* + * Here is a sketch of what will be needed to generalize Patterns for + * (the) new PatternType(s). + */ +typedef struct gs_pattern_instance_s { + rc_header rc; /* ?? */ + const gs_pattern_type_t *type; + gs_uid XIUD; /* ?? */ + gs_state *saved; /* ?? */ + void *data; +} gs_pattern_instance_t; +typedef struct gs_pattern1_instance_data_s { + ... +} gs_pattern1_instance_data_t; + +#define gs_pattern2_instance_data_common\ + const gs_shading_t *shading;\ + gx_device_color *background;\ + const gs_color_space *color_space;\ + gs_matrix param_to_device_matrix +typedef struct gs_pattern2_instance_data_common_s { + gs_pattern2_instance_data_common; +} gs_pattern2_instance_data_common_t; + +#endif /*************************************************************** */ diff --git a/gs/src/gxshade1.c b/gs/src/gxshade1.c new file mode 100644 index 000000000..9c1b1eec9 --- /dev/null +++ b/gs/src/gxshade1.c @@ -0,0 +1,511 @@ +/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: gxshade1.c */ +/* Rendering for non-mesh shadings */ +#include "math_.h" +#include "gx.h" +#include "gserrors.h" +#include "gsmatrix.h" /* for gscoord.h */ +#include "gscoord.h" +#include "gspath.h" +#include "gxcspace.h" +#include "gxdcolor.h" +#include "gxfarith.h" +#include "gxfixed.h" +#include "gxistate.h" +#include "gxpath.h" +#include "gxshade.h" + +/* ================ Utilities ================ */ + +/* Check whether 2 colors fall within the smoothness criterion. */ +private bool +shade_colors2_converge(const gs_client_color cc[2], + const shading_fill_state_t * pfs) +{ + int ci; + + for (ci = pfs->num_components - 1; ci >= 0; --ci) + if (fabs(cc[1].paint.values[ci] - cc[0].paint.values[ci]) > + pfs->cc_max_error[ci] + ) + return false; + return true; +} + +/* Fill a user space rectangle that is also a device space rectangle. */ +private int +shade_fill_device_rectangle(const shading_fill_state_t * pfs, + const gs_fixed_point * p0, const gs_fixed_point * p1, gx_device_color * pdevc) +{ + gs_imager_state *pis = pfs->pis; + fixed xmin, ymin, xmax, ymax; + int x, y; + + if (p0->x < p1->x) + xmin = p0->x, xmax = p1->x; + else + xmin = p1->x, xmax = p0->x; + if (p0->y < p1->y) + ymin = p0->y, ymax = p1->y; + else + ymin = p1->y, ymax = p0->y; +/****** CONFINE TO rect ******/ +/****** NOT QUITE RIGHT FOR PIXROUND ******/ + xmin -= pis->fill_adjust.x; + xmax += pis->fill_adjust.x; + ymin -= pis->fill_adjust.y; + ymax += pis->fill_adjust.y; + x = fixed2int_var(xmin); + y = fixed2int_var(ymin); + return + gx_fill_rectangle_device_rop(x, y, + fixed2int_var(xmax) - x, + fixed2int_var(ymax) - y, + pdevc, pfs->dev, pis->log_op); +} + +/* ================ Specific shadings ================ */ + +/* ---------------- Function-based shading ---------------- */ + +typedef struct Fb_fill_state_s { + shading_fill_state_common; + const gs_shading_Fb_t *psh; + gs_matrix_fixed ptm; /* parameter space -> device space */ + gs_client_color cc[4]; +} Fb_fill_state_t; + +private int +Fb_fill_region(Fb_fill_state_t * pfs, floatp x0, floatp y0, floatp x1, + floatp y1) +{ + const gs_shading_Fb_t *psh = pfs->psh; + gs_imager_state *pis = pfs->pis; + + top:if (!shade_colors4_converge(pfs->cc, + (const shading_fill_state_t *)pfs) + ) { + /* + * The colors don't converge. Does the region color more than + * a single pixel? + */ + gs_rect region; + + region.p.x = x0, region.p.y = y0; + region.q.x = x1, region.q.y = y1; + gs_bbox_transform(®ion, (const gs_matrix *)&pfs->ptm, ®ion); + if (region.q.x - region.p.x >= 1 || region.q.y - region.p.y >= 1) + goto recur; + { + /* + * More precisely, does the bounding box of the region, + * taking fill adjustment into account, span more than 1 + * pixel center in either X or Y? + */ + fixed ax = pis->fill_adjust.x; + int nx = + fixed2int_pixround(float2fixed(region.q.x) + ax) - + fixed2int_pixround(float2fixed(region.p.x) - ax); + fixed ay = pis->fill_adjust.y; + int ny = + fixed2int_pixround(float2fixed(region.q.y) + ay) - + fixed2int_pixround(float2fixed(region.p.y) - ay); + + if ((nx > 1 && ny != 0) || (ny > 1 && nx != 0)) + goto recur; + } + /* We could do the 1-pixel case a lot faster! */ + } + /* Fill the region with the color. */ + { + gx_device_color dev_color; + const gs_color_space *pcs = psh->params.ColorSpace; + gs_fixed_point pts[4]; + int code; + + if_debug0('|', "[|]... filling region\n"); + (*pcs->type->restrict_color) (&pfs->cc[0], pcs); + (*pcs->type->remap_color) (&pfs->cc[0], pcs, &dev_color, pis, + pfs->dev, gs_color_select_texture); + gs_point_transform2fixed(&pfs->ptm, x0, y0, &pts[0]); + gs_point_transform2fixed(&pfs->ptm, x1, y1, &pts[2]); + if (is_xxyy(&pfs->ptm) || is_xyyx(&pfs->ptm)) { + code = + shade_fill_device_rectangle((const shading_fill_state_t *)pfs, + &pts[0], &pts[2], &dev_color); + } else { + gx_path *ppath = gx_path_alloc(pis->memory, "Fb_fill"); + + gs_point_transform2fixed(&pfs->ptm, x1, y0, &pts[1]); + gs_point_transform2fixed(&pfs->ptm, x0, y1, &pts[3]); + gx_path_add_point(ppath, pts[0].x, pts[0].y); + gx_path_add_lines(ppath, pts + 1, 3); + code = shade_fill_path((const shading_fill_state_t *)pfs, + ppath, &dev_color); + gx_path_free(ppath, "Fb_fill"); + } + return code; + } + + /* + * No luck. Subdivide the region and recur. + * + * We should subdivide on the axis that has the largest color + * discrepancy, but for now we subdivide on the axis with the + * largest coordinate difference. + */ + recur:{ + gs_client_color cc[4]; + gs_function_t *pfn = psh->params.Function; + float v[2]; + + if (y1 - y0 > x1 - x0) { + /* Subdivide in Y. */ + float ym = (y0 + y1) * 0.5; + + if_debug1('|', "[|]dividing at y=%g\n", ym); + v[1] = ym; + v[0] = x0; + gs_function_evaluate(pfn, v, cc[0].paint.values); + v[0] = x1; + gs_function_evaluate(pfn, v, cc[1].paint.values); + cc[2].paint = pfs->cc[2].paint; + cc[3].paint = pfs->cc[3].paint; + pfs->cc[2].paint = cc[0].paint; + pfs->cc[3].paint = cc[1].paint; + Fb_fill_region(pfs, x0, y0, x1, ym); + y0 = ym; + } else { + /* Subdivide in X. */ + float xm = (x0 + x1) * 0.5; + + if_debug1('|', "[|]dividing at x=%g\n", xm); + v[0] = xm; + v[1] = y0; + gs_function_evaluate(pfn, v, cc[0].paint.values); + v[1] = y1; + gs_function_evaluate(pfn, v, cc[2].paint.values); + cc[1].paint = pfs->cc[1].paint; + cc[3].paint = pfs->cc[3].paint; + pfs->cc[1].paint = cc[0].paint; + pfs->cc[3].paint = cc[2].paint; + Fb_fill_region(pfs, x0, y0, xm, y1); + x0 = xm; + } + pfs->cc[0].paint = cc[0].paint; + pfs->cc[1].paint = cc[1].paint; + pfs->cc[2].paint = cc[2].paint; + pfs->cc[3].paint = cc[3].paint; + } + goto top; +} + +int +gs_shading_Fb_fill_rectangle(const gs_shading_t * psh0, const gs_rect * rect, + gx_device * dev, gs_imager_state * pis) +{ + const gs_shading_Fb_t *psh = (const gs_shading_Fb_t *)psh0; + gs_matrix save_ctm; + int xi, yi, code; + float x[2], y[2]; + Fb_fill_state_t state; + + shade_init_fill_state((shading_fill_state_t *) & state, psh0, dev, pis); + state.psh = psh; +/****** HACK ******/ + gs_currentmatrix((gs_state *) pis, &save_ctm); + gs_concat((gs_state *) pis, &psh->params.Matrix); + state.ptm = pis->ctm; + /* Compute the parameter X and Y ranges. */ + { + gs_rect pbox; + + gs_bbox_transform_inverse(rect, &psh->params.Matrix, &pbox); + x[0] = max(pbox.p.x, psh->params.Domain[0]); + x[1] = min(pbox.q.x, psh->params.Domain[1]); + y[0] = max(pbox.p.y, psh->params.Domain[2]); + y[1] = min(pbox.q.y, psh->params.Domain[3]); + } + for (xi = 0; xi < 2; ++xi) + for (yi = 0; yi < 2; ++yi) { + float v[2]; + + v[0] = x[xi], v[1] = y[yi]; + gs_function_evaluate(psh->params.Function, v, + state.cc[yi * 2 + xi].paint.values); + } + code = Fb_fill_region(&state, x[0], y[0], x[1], y[1]); +/****** HACK ******/ + gs_setmatrix((gs_state *) pis, &save_ctm); + return code; +} + +/* ---------------- Axial shading ---------------- */ + +typedef struct A_fill_state_s { + shading_fill_state_common; + const gs_shading_A_t *psh; + gs_rect rect; + gs_client_color cc[2]; + gs_point delta; + double length, dd; +} A_fill_state_t; + +/* Note t0 and t1 vary over [0..1], not the Domain. */ +private int +A_fill_region(A_fill_state_t * pfs, floatp t0, floatp t1) +{ + const gs_shading_A_t *psh = pfs->psh; + + top:if (!shade_colors2_converge(pfs->cc, + (const shading_fill_state_t *)pfs) + ) { + /* + * The colors don't converge. Is the stripe less than 1 pixel wide? + */ + if (pfs->length * (t1 - t0) > 1) + goto recur; + /* We could do the 1-pixel case a lot faster! */ + } + /* Fill the region with the color. */ + { + gx_device_color dev_color; + const gs_color_space *pcs = psh->params.ColorSpace; + gs_imager_state *pis = pfs->pis; + double + x0 = psh->params.Coords[0] + pfs->delta.x * t0, y0 = psh->params.Coords[1] + pfs->delta.y * t0; + double + x1 = psh->params.Coords[0] + pfs->delta.x * t1, y1 = psh->params.Coords[1] + pfs->delta.y * t1; + gs_fixed_point pts[4]; + int code; + + (*pcs->type->restrict_color) (&pfs->cc[0], pcs); + (*pcs->type->remap_color) (&pfs->cc[0], pcs, &dev_color, pis, + pfs->dev, gs_color_select_texture); + if (x0 == x1) { + /* Stripe is horizontal. */ + x0 = pfs->rect.p.x; + x1 = pfs->rect.q.x; + } else if (y0 == y1) { + /* Stripe is vertical. */ + y0 = pfs->rect.p.y; + y1 = pfs->rect.q.y; + } else { + /* + * Stripe is neither horizontal nor vertical. + * Extend it to the edges of the rectangle. + */ + gx_path *ppath = gx_path_alloc(pis->memory, "A_fill"); + + /* + * Figuring out how far to extend the stripe is hard, + * since one or both of x0/y0 and x1/y1 may lie outside + * the rectangle but part of the stripe may lie inside. + * For now, we do something simple and ****** WRONG ******. + */ + double dist = max(pfs->rect.q.x - pfs->rect.p.x, + pfs->rect.q.y - pfs->rect.p.y); + double denom = hypot(pfs->delta.x, pfs->delta.y); + double dx = dist * pfs->delta.y / denom, dy = -dist * pfs->delta.x / denom; + + if_debug6('|', "[|]p0=(%g,%g), p1=(%g,%g), dxy=(%g,%g)\n", + x0, y0, x1, y1, dx, dy); + gs_point_transform2fixed(&pis->ctm, x0 - dx, y0 - dy, &pts[0]); + gs_point_transform2fixed(&pis->ctm, x0 + dx, y0 + dy, &pts[1]); + gs_point_transform2fixed(&pis->ctm, x1 + dx, y1 + dy, &pts[2]); + gs_point_transform2fixed(&pis->ctm, x1 - dx, y1 - dy, &pts[3]); + gx_path_add_point(ppath, pts[0].x, pts[0].y); + gx_path_add_lines(ppath, pts + 1, 3); + code = shade_fill_path((const shading_fill_state_t *)pfs, + ppath, &dev_color); + gx_path_free(ppath, "A_fill"); + } + /* Stripe is horizontal or vertical. */ + gs_point_transform2fixed(&pis->ctm, x0, y0, &pts[0]); + gs_point_transform2fixed(&pis->ctm, x1, y1, &pts[1]); + return + shade_fill_device_rectangle((const shading_fill_state_t *)pfs, + &pts[0], &pts[1], &dev_color); + } + + /* + * No luck. Subdivide the interval and recur. + */ + recur:{ + gs_client_color ccm, cc1; + gs_function_t *pfn = psh->params.Function; + float tm = (t0 + t1) * 0.5; + float dm = tm * pfs->dd + psh->params.Domain[0]; + + cc1.paint = pfs->cc[1].paint; + gs_function_evaluate(pfn, &dm, ccm.paint.values); + pfs->cc[1].paint = ccm.paint; + A_fill_region(pfs, t0, tm); + pfs->cc[0].paint = ccm.paint; + pfs->cc[1].paint = cc1.paint; + t0 = tm; + goto top; + } + return 0; +} + +int +gs_shading_A_fill_rectangle(const gs_shading_t * psh0, const gs_rect * rect, + gx_device * dev, gs_imager_state * pis) +{ + const gs_shading_A_t *const psh = (const gs_shading_A_t *)psh0; + A_fill_state_t state; + float d0 = psh->params.Domain[0], d1 = psh->params.Domain[1], dd = d1 - d0; + float t[2]; + gs_point dist; + int i; + + shade_init_fill_state((shading_fill_state_t *) & state, psh0, dev, pis); + state.psh = psh; + state.rect = *rect; + /* Compute the parameter range. */ + t[0] = d0; + t[1] = d1; +/****** INTERSECT Domain WITH rect ******/ + for (i = 0; i < 2; ++i) + gs_function_evaluate(psh->params.Function, &t[i], + state.cc[i].paint.values); + state.delta.x = psh->params.Coords[2] - psh->params.Coords[0]; + state.delta.y = psh->params.Coords[3] - psh->params.Coords[1]; + gs_distance_transform(state.delta.x, state.delta.y, &ctm_only(pis), + &dist); + state.length = hypot(dist.x, dist.y); /* device space line length */ + state.dd = dd; +/****** DOESN'T HANDLE Extend ******/ + return A_fill_region(&state, (t[0] - d0) / dd, (t[1] - d0) / dd); +} + +/* ---------------- Radial shading ---------------- */ + +typedef struct R_fill_state_s { + shading_fill_state_common; + const gs_shading_R_t *psh; + gs_rect rect; + gs_client_color cc[2]; + gs_point delta; + double dr, width, dd; +} R_fill_state_t; + +/* Note t0 and t1 vary over [0..1], not the Domain. */ +private int +R_fill_region(R_fill_state_t * pfs, floatp t0, floatp t1) +{ + const gs_shading_R_t *psh = pfs->psh; + + top:if (!shade_colors2_converge(pfs->cc, + (const shading_fill_state_t *)pfs) + ) { + /* + * The colors don't converge. Is the annulus less than 1 pixel wide? + */ + if (pfs->width * (t1 - t0) > 1) + goto recur; + /* We could do the 1-pixel case a lot faster! */ + } + /* Fill the region with the color. */ + { + gx_device_color dev_color; + const gs_color_space *pcs = psh->params.ColorSpace; + gs_imager_state *pis = pfs->pis; + double + x0 = psh->params.Coords[0] + pfs->delta.x * t0, y0 = psh->params.Coords[1] + pfs->delta.y * t0, + r0 = psh->params.Coords[2] + pfs->dr * t0; + double + x1 = psh->params.Coords[0] + pfs->delta.x * t1, y1 = psh->params.Coords[1] + pfs->delta.y * t1, + r1 = psh->params.Coords[2] + pfs->dr * t1; + gx_path *ppath = gx_path_alloc(pis->memory, "R_fill"); + int code; + + (*pcs->type->restrict_color) (&pfs->cc[0], pcs); + (*pcs->type->remap_color) (&pfs->cc[0], pcs, &dev_color, pis, + pfs->dev, gs_color_select_texture); + if ((code = gs_imager_arc_add(ppath, pis, false, x0, y0, r0, + 0.0, 360.0, false)) >= 0 && + (code = gs_imager_arc_add(ppath, pis, true, x1, y1, r1, + 0.0, 360.0, false)) >= 0 + ) { + code = shade_fill_path((const shading_fill_state_t *)pfs, + ppath, &dev_color); + } + gx_path_free(ppath, "R_fill"); + return code; + } + + /* + * No luck. Subdivide the interval and recur. + */ + recur:{ + gs_client_color ccm, cc1; + gs_function_t *pfn = psh->params.Function; + float tm = (t0 + t1) * 0.5; + float dm = tm * pfs->dd + psh->params.Domain[0]; + + cc1.paint = pfs->cc[1].paint; + gs_function_evaluate(pfn, &dm, ccm.paint.values); + pfs->cc[1].paint = ccm.paint; + R_fill_region(pfs, t0, tm); + pfs->cc[0].paint = ccm.paint; + pfs->cc[1].paint = cc1.paint; + t0 = tm; + goto top; + } + return 0; +} + +int +gs_shading_R_fill_rectangle(const gs_shading_t * psh0, const gs_rect * rect, + gx_device * dev, gs_imager_state * pis) +{ + const gs_shading_R_t *const psh = (const gs_shading_R_t *)psh0; + R_fill_state_t state; + float d0 = psh->params.Domain[0], d1 = psh->params.Domain[1], dd = d1 - d0; + float t[2]; + int i; + + shade_init_fill_state((shading_fill_state_t *) & state, psh0, dev, pis); + state.psh = psh; + state.rect = *rect; + /* Compute the parameter range. */ + t[0] = d0; + t[1] = d1; +/****** INTERSECT Domain WITH rect ******/ + for (i = 0; i < 2; ++i) + gs_function_evaluate(psh->params.Function, &t[i], + state.cc[i].paint.values); + state.delta.x = psh->params.Coords[3] - psh->params.Coords[0]; + state.delta.y = psh->params.Coords[4] - psh->params.Coords[1]; + state.dr = psh->params.Coords[5] - psh->params.Coords[2]; + /* + * Compute the annulus width in its thickest direction. This is + * only used for a conservative check, so it can be pretty crude + * (and it is!). + */ + state.width = + (fabs(pis->ctm.xx) + fabs(pis->ctm.xy) + fabs(pis->ctm.yx) + + fabs(pis->ctm.yy)) * fabs(state.dr); + state.dd = dd; +/****** DOESN'T HANDLE Extend ******/ + return R_fill_region(&state, (t[0] - d0) / dd, (t[1] - d0) / dd); +} diff --git a/gs/src/gxshade4.c b/gs/src/gxshade4.c new file mode 100644 index 000000000..4e63aa37d --- /dev/null +++ b/gs/src/gxshade4.c @@ -0,0 +1,289 @@ +/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: gxshade4.c */ +/* Rendering for Gouraud triangle shadings */ +#include "memory_.h" +#include "gx.h" +#include "gserrors.h" +#include "gsmatrix.h" /* for gscoord.h */ +#include "gscoord.h" +#include "gxcspace.h" +#include "gxdcolor.h" +#include "gxdevcli.h" +#include "gxistate.h" +#include "gxpath.h" +#include "gxshade.h" +#include "gxshade4.h" + +/* ---------------- Triangle mesh filling ---------------- */ + +/* Initialize the fill state for triangle shading. */ +void +mesh_init_fill_state(mesh_fill_state_t * pfs, const gs_shading_mesh_t * psh, + const gs_rect * rect, gx_device * dev, gs_imager_state * pis) +{ + shade_init_fill_state((shading_fill_state_t *) pfs, + (const gs_shading_t *)psh, dev, pis); + pfs->pshm = psh; + shade_bbox_transform2fixed(rect, pis, &pfs->rect); +} + +#define SET_MIN_MAX_3(vmin, vmax, a, b, c)\ + if ( a < b ) vmin = a, vmax = b; else vmin = b, vmax = a;\ + if ( c < vmin ) vmin = c; else if ( c > vmax ) vmax = c + +private int +mesh_fill_region(const mesh_fill_state_t * pfs, fixed xa, fixed ya, fixed xb, + fixed yb, fixed xc, fixed yc, const gs_client_color cc[3], bool check) +{ + const gs_shading_mesh_t *psh = pfs->pshm; + int ci; + + /* + * Fill the triangle with vertices at x/ya, x/yb, and x/yc + * with color cc[0]. + * If check is true, check for whether the triangle is entirely + * inside the rectangle, entirely outside, or partly inside; + * if check is false, assume the triangle is entirely inside. + */ + if (check) { + fixed xmin, ymin, xmax, ymax; + + SET_MIN_MAX_3(xmin, xmax, xa, xb, xc); + SET_MIN_MAX_3(ymin, ymax, ya, yb, yc); + if (xmin >= pfs->rect.p.x && xmax <= pfs->rect.q.x && + ymin >= pfs->rect.p.y && ymax <= pfs->rect.q.y + ) { + /* The triangle is entirely inside the rectangle. */ + check = false; + } else if (xmin >= pfs->rect.q.x || xmax <= pfs->rect.p.x || + ymin >= pfs->rect.q.y || ymax <= pfs->rect.p.y + ) { + /* The triangle is entirely outside the rectangle. */ + return 0; + } else { +/****** CLIP HERE ******/ + } + } + /* Check whether the colors fall within the smoothness criterion. */ + for (ci = 0; ci < pfs->num_components; ++ci) { + float + c0 = cc[0].paint.values[ci], c1 = cc[1].paint.values[ci], + c2 = cc[2].paint.values[ci]; + float cmin, cmax; + + SET_MIN_MAX_3(cmin, cmax, c0, c1, c2); + if (cmax - cmin > pfs->cc_max_error[ci]) + goto recur; + } + /* Fill the triangle with the color. */ + { + gx_device_color dev_color; + const gs_color_space *pcs = psh->params.ColorSpace; + gs_imager_state *pis = pfs->pis; + gx_path *ppath; + gs_client_color fcc; + int code; + + fcc.paint = cc[0].paint; + (*pcs->type->restrict_color) (&fcc, pcs); + (*pcs->type->remap_color) (&fcc, pcs, &dev_color, pis, + pfs->dev, gs_color_select_texture); +/****** SHOULD ADD adjust ON ANY OUTSIDE EDGES ******/ +#if 0 + ppath = gx_path_alloc(pis->memory, "Gt_fill"); + gx_path_add_point(ppath, xa, ya); + gx_path_add_line(ppath, xb, yb); + gx_path_add_line(ppath, xc, yc); + code = shade_fill_path((const shading_fill_state_t *)pfs, + ppath, &dev_color); + gx_path_free(ppath, "Gt_fill"); +#else + code = (*dev_proc(pfs->dev, fill_triangle)) + (pfs->dev, xa, ya, xb - xa, yb - ya, xc - xa, yc - ya, + &dev_color, pis->log_op); +#endif + return code; + } + /* + * Subdivide the triangle and recur. The only subdivision method + * that doesn't seem to create anomalous shapes divides the + * triangle in 4, using the midpoints of each side. + */ + recur:{ +#define midpoint_fast(a,b)\ + arith_rshift_1((a) + (b) + 1) + fixed + xab = midpoint_fast(xa, xb), yab = midpoint_fast(ya, yb), + xac = midpoint_fast(xa, xc), yac = midpoint_fast(ya, yc), + xbc = midpoint_fast(xb, xc), ybc = midpoint_fast(yb, yc); +#undef midpoint_fast + gs_client_color rcc[5]; + int i; + + for (i = 0; i < pfs->num_components; ++i) { + float + ta = cc[0].paint.values[i], tb = cc[1].paint.values[i], + tc = cc[2].paint.values[i]; + + rcc[1].paint.values[i] = (ta + tb) * 0.5; + rcc[2].paint.values[i] = (ta + tc) * 0.5; + rcc[3].paint.values[i] = (tb + tc) * 0.5; + } + /* Do the "A" triangle. */ + rcc[0].paint = cc[0].paint; /* rcc: a,ab,ac,bc,- */ + mesh_fill_region(pfs, xa, ya, xab, yab, xac, yac, rcc, check); + /* Do the central triangle. */ + mesh_fill_region(pfs, xab, yab, xac, yac, xbc, ybc, rcc + 1, check); + /* Do the "C" triangle. */ + rcc[4].paint = cc[2].paint; /* rcc: a,ab,ac,bc,c */ + mesh_fill_region(pfs, xac, yac, xbc, ybc, xc, yc, rcc + 2, check); + /* Do the "B" triangle. */ + rcc[2].paint = cc[1].paint; /* rcc: a,ab,b,bc,c */ + mesh_fill_region(pfs, xab, yab, xb, yb, xbc, ybc, rcc + 1, check); + return 0; + } +} + +int +mesh_fill_triangle(const mesh_fill_state_t * pfs, const gs_fixed_point * pa, + const float *pca, const gs_fixed_point * pb, const float *pcb, + const gs_fixed_point * pc, const float *pcc, bool check) +{ + gs_client_color cc[3]; + + memcpy(cc[0].paint.values, pca, sizeof(cc[0].paint.values)); + memcpy(cc[1].paint.values, pcb, sizeof(cc[1].paint.values)); + memcpy(cc[2].paint.values, pcc, sizeof(cc[2].paint.values)); + return mesh_fill_region(pfs, pa->x, pa->y, pb->x, pb->y, + pc->x, pc->y, cc, check); +} + +/* ---------------- Gouraud triangle shadings ---------------- */ + +private int +Gt_next_vertex(const gs_shading_mesh_t * psh, shade_coord_stream_t * cs, + mesh_vertex_t * vertex) +{ + int code = shade_next_vertex(cs, vertex); + + if (code >= 0 && psh->params.Function) { + /* Decode the color with the function. */ + gs_function_evaluate(psh->params.Function, vertex->cc, vertex->cc); + } + return code; +} + +private int +Gt_fill_triangle(const mesh_fill_state_t * pfs, const mesh_vertex_t * va, + const mesh_vertex_t * vb, const mesh_vertex_t * vc) +{ + return mesh_fill_triangle(pfs, &va->p, va->cc, &vb->p, vb->cc, + &vc->p, vc->cc, true); +} + +int +gs_shading_FfGt_fill_rectangle(const gs_shading_t * psh0, const gs_rect * rect, + gx_device * dev, gs_imager_state * pis) +{ + const gs_shading_FfGt_t *psh = (const gs_shading_FfGt_t *)psh0; + mesh_fill_state_t state; + shade_coord_stream_t cs; + int num_bits = psh->params.BitsPerFlag; + int flag; + mesh_vertex_t va, vb, vc; + + mesh_init_fill_state(&state, (const gs_shading_mesh_t *)psh, rect, + dev, pis); + shade_next_init(&cs, (const gs_shading_mesh_params_t *)&psh->params, + pis); + while ((flag = shade_next_flag(&cs, num_bits)) >= 0) { + int code; + + switch (flag) { + default: + return_error(gs_error_rangecheck); + case 0: + if ((code = Gt_next_vertex(state.pshm, &cs, &va)) < 0 || + (code = shade_next_flag(&cs, num_bits)) < 0 || + (code = Gt_next_vertex(state.pshm, &cs, &vb)) < 0 || + (code = shade_next_flag(&cs, num_bits)) < 0 + ) + return code; + goto v2; + case 1: + va = vb; + case 2: + vb = vc; + v2:if ((code = Gt_next_vertex(state.pshm, &cs, &vc)) < 0) + return code; + code = Gt_fill_triangle(&state, &va, &vb, &vc); + if (code < 0) + return code; + } + } + return 0; +} + +int +gs_shading_LfGt_fill_rectangle(const gs_shading_t * psh0, const gs_rect * rect, + gx_device * dev, gs_imager_state * pis) +{ + const gs_shading_LfGt_t *psh = (const gs_shading_LfGt_t *)psh0; + mesh_fill_state_t state; + shade_coord_stream_t cs; + mesh_vertex_t *vertex; + mesh_vertex_t next; + int per_row = psh->params.VerticesPerRow; + int i, code; + + mesh_init_fill_state(&state, (const gs_shading_mesh_t *)psh, rect, + dev, pis); + shade_next_init(&cs, (const gs_shading_mesh_params_t *)&psh->params, + pis); + vertex = (mesh_vertex_t *) + gs_alloc_byte_array(pis->memory, per_row, sizeof(*vertex), + "gs_shading_LfGt_render"); + if (vertex == 0) + return_error(gs_error_VMerror); + for (i = 0; i < per_row; ++i) + if ((code = Gt_next_vertex(state.pshm, &cs, &vertex[i])) < 0) + goto out; + while (!seofp(cs.s)) { + code = Gt_next_vertex(state.pshm, &cs, &next); + if (code < 0) + goto out; + for (i = 1; i < per_row; ++i) { + code = Gt_fill_triangle(&state, &vertex[i - 1], &vertex[i], &next); + if (code < 0) + goto out; + vertex[i - 1] = next; + code = Gt_next_vertex(state.pshm, &cs, &next); + if (code < 0) + goto out; + code = Gt_fill_triangle(&state, &vertex[i], &vertex[i - 1], &next); + if (code < 0) + goto out; + } + vertex[per_row - 1] = next; + } + out: + gs_free_object(pis->memory, vertex, "gs_shading_LfGt_render"); + return code; +} diff --git a/gs/src/gxshade4.h b/gs/src/gxshade4.h new file mode 100644 index 000000000..bf1b471c3 --- /dev/null +++ b/gs/src/gxshade4.h @@ -0,0 +1,53 @@ +/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: gxshade4.h */ +/* Internal definitions for triangle shading rendering */ + +#ifndef gxshade4_INCLUDED +# define gxshade4_INCLUDED + +/* + * Define the fill state structure for triangle shadings. This is used + * both for the Gouraud triangle shading types and for the Coons and + * tensor patch types. + * + * The shading pointer is named pshm rather than psh in case subclasses + * also want to store a pointer of a more specific type. + */ +#define mesh_fill_state_common\ + shading_fill_state_common;\ + const gs_shading_mesh_t *pshm;\ + gs_fixed_rect rect +typedef struct mesh_fill_state_s { + mesh_fill_state_common; +} mesh_fill_state_t; + +/* Initialize the fill state for triangle shading. */ +void mesh_init_fill_state(P5(mesh_fill_state_t * pfs, + const gs_shading_mesh_t * psh, const gs_rect * rect, + gx_device * dev, gs_imager_state * pis)); + +/* Fill one triangle in a mesh. */ +int mesh_fill_triangle(P8(const mesh_fill_state_t * pfs, + const gs_fixed_point * pa, const float *pca, + const gs_fixed_point * pb, const float *pcb, + const gs_fixed_point * pc, const float *pcc, + bool check_clipping)); + +#endif /* gxshade4_INCLUDED */ diff --git a/gs/src/gxshade6.c b/gs/src/gxshade6.c new file mode 100644 index 000000000..b52bb4d2a --- /dev/null +++ b/gs/src/gxshade6.c @@ -0,0 +1,552 @@ +/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: gxshade6.c */ +/* Rendering for Coons and tensor patch shadings */ +#include "memory_.h" +#include "gx.h" +#include "gserrors.h" +#include "gsmatrix.h" /* for gscoord.h */ +#include "gscoord.h" +#include "gxcspace.h" +#include "gxdcolor.h" +#include "gxistate.h" +#include "gxshade.h" +#include "gxshade4.h" +#include "gzpath.h" + +/* ================ Utilities ================ */ + +/* Define one segment (vertex and next control points) of a curve. */ +typedef struct patch_curve_s { + mesh_vertex_t vertex; + gs_fixed_point control[2]; +} patch_curve_t; + +/* Get colors for patch vertices. */ +private int +shade_next_colors(shade_coord_stream_t * cs, patch_curve_t * curves, + int num_vertices) +{ + int i, code = 0; + + for (i = 0; i < num_vertices && code >= 0; ++i) + code = shade_next_color(cs, curves[i].vertex.cc); + return code; +} + +/* Get a Bezier or tensor patch element. */ +private int +shade_next_curve(shade_coord_stream_t * cs, patch_curve_t * curve) +{ + int code = shade_next_coords(cs, &curve->vertex.p, 1); + + if (code >= 0) + code = shade_next_coords(cs, curve->control, + countof(curve->control)); + return code; +} + +/* Define a color to be used in curve rendering. */ +/* This may be a real client color, or a parametric function argument. */ +typedef struct patch_color_s { + float t; /* parametric value */ + gs_client_color cc; +} patch_color_t; + +/* + * Parse the next patch out of the input stream. Return 1 if done, + * 0 if patch, <0 on error. + */ +private int +shade_next_patch(shade_coord_stream_t * cs, int BitsPerFlag, +patch_curve_t curve[4], gs_fixed_point interior[4] /* 0 for Coons patch */ ) +{ + int flag = shade_next_flag(cs, BitsPerFlag); + int num_colors, code; + + if (flag < 0) + return 1; /* no more data */ + switch (flag & 3) { + default: + return_error(gs_error_rangecheck); /* not possible */ + case 0: + if ((code = shade_next_curve(cs, &curve[0])) < 0 || + (code = shade_next_coords(cs, &curve[1].vertex.p, 1)) < 0 + ) + return code; + num_colors = 4; + goto vx; + case 1: + curve[0] = curve[1], curve[1].vertex = curve[2].vertex; + goto v3; + case 2: + curve[0] = curve[2], curve[1].vertex = curve[3].vertex; + goto v3; + case 3: + curve[1].vertex = curve[0].vertex, curve[0] = curve[3]; + v3:num_colors = 2; + vx:if ((code = shade_next_coords(cs, curve[1].control, 2)) < 0 || + (code = shade_next_curve(cs, &curve[2])) < 0 || + (code = shade_next_curve(cs, &curve[3])) < 0 || + (interior != 0 && + (code = shade_next_coords(cs, interior, 4)) < 0) || + (code = shade_next_colors(cs, &curve[4 - num_colors], + num_colors)) < 0 + ) + return code; + } + return 0; +} + +/* Define the common state for rendering Coons and tensor patches. */ +typedef struct patch_fill_state_s { + mesh_fill_state_common; + const gs_function_t *Function; +} patch_fill_state_t; + +/* Calculate the interpolated color at a given point. */ +/* Note that we must do this twice for bilinear interpolation. */ +private void +patch_interpolate_color(patch_color_t * ppc, const patch_color_t * ppc0, + const patch_color_t * ppc1, const patch_fill_state_t * pfs, floatp t) +{ + if (pfs->Function) + ppc->t = ppc0->t + t * (ppc1->t - ppc0->t); + else { + int ci; + + for (ci = pfs->num_components - 1; ci >= 0; --ci) + ppc->cc.paint.values[ci] = + ppc0->cc.paint.values[ci] + + t * (ppc1->cc.paint.values[ci] - ppc0->cc.paint.values[ci]); + } +} + +/* Resolve a patch color using the Function if necessary. */ +private void +patch_resolve_color(patch_color_t * ppc, const patch_fill_state_t * pfs) +{ + if (pfs->Function) + gs_function_evaluate(pfs->Function, &ppc->t, ppc->cc.paint.values); +} + +/* ================ Specific shadings ================ */ + +/* + * The curves are stored in a clockwise or counter-clockwise order that maps + * to the patch definition in a non-intuitive way: + */ +/* The starting points of the curves: */ +#define C1START 0 +#define D1START 0 +#define C2START 3 +#define D2START 1 +/* The control points of the curves (x means reversed order): */ +#define C1CTRL 0 +#define D1XCTRL 3 +#define C2XCTRL 2 +#define D2CTRL 1 +/* The end points of the curves: */ +#define C1END 1 +#define D1END 3 +#define C2END 2 +#define D2END 2 + +/* ---------------- Common code ---------------- */ + +/* Evaluate a curve at a given point. */ +private void +curve_eval(gs_fixed_point * pt, const gs_fixed_point * p0, + const gs_fixed_point * p1, const gs_fixed_point * p2, + const gs_fixed_point * p3, floatp t) +{ + fixed a, b, c, d; + fixed t01, t12; + + d = p0->x; + curve_points_to_coefficients(d, p1->x, p2->x, p3->x, + a, b, c, t01, t12); + pt->x = (fixed) (((a * t + b) * t + c) * t + d); + d = p0->y; + curve_points_to_coefficients(d, p1->y, p2->y, p3->y, + a, b, c, t01, t12); + pt->y = (fixed) (((a * t + b) * t + c) * t + d); + if_debug3('2', "[2]t=%g => (%g,%g)\n", t, fixed2float(pt->x), + fixed2float(pt->y)); +} + +/* + * Merge two arrays of splits, sorted in increasing order. + * Return the number of entries in the result, which might be less than + * n1 + n2 (if an a1 entry is equal to an a2 entry). + * a1 or a2 may overlap out as long as a1 - out >= n2 or a2 - out >= n1 + * respectively. + */ +private int +merge_splits(double *out, const double *a1, int n1, const double *a2, int n2) +{ + double *p = out; + int i1 = 0, i2 = 0; + + /* + * We would like to write the body of the loop as an assignement + * with a conditional expression on the right, but gcc 2.7.2.3 + * generates incorrect code if we do this. + */ + while (i1 < n1 || i2 < n2) + if (i1 == n1) + *p++ = a2[i2++]; + else if (i2 == n2 || a1[i1] < a2[i2]) + *p++ = a1[i1++]; + else if (a1[i1] > a2[i2]) + *p++ = a2[i2++]; + else + i1++, *p++ = a2[i2++]; + return p - out; +} + +/* Split a curve in both X and Y. Return the number of split points. */ +private int +split_xy(double out[4], const patch_curve_t * curve, const gs_fixed_point * p3) +{ + double tx[2], ty[2]; + + return merge_splits(out, tx, + gx_curve_monotonic_points(curve->vertex.p.x, + curve->control[0].x, + curve->control[1].x, + p3->x, tx), + ty, + gx_curve_monotonic_points(curve->vertex.p.y, + curve->control[0].y, + curve->control[1].y, + p3->y, ty)); +} + +/* + * Compute the joint split points of 2 curves. + * Return the number of split points. + */ +private int +split2_xy(double out[8], const patch_curve_t * curve1, + const gs_fixed_point * p31, const patch_curve_t * curve2, + const gs_fixed_point * p32) +{ + double t1[4], t2[4]; + + return merge_splits(out, t1, split_xy(t1, curve1, p31), + t2, split_xy(t2, curve2, p32)); +} + +private int +patch_fill(const patch_fill_state_t * pfs, const patch_curve_t curve[4], + const gs_fixed_point interior[4], + void (*transform) (P5(gs_fixed_point *, const patch_curve_t[4], + const gs_fixed_point[4], floatp, floatp))) +{ /* + * The specification says the output must appear to be produced in + * order of increasing values of v, and for equal v, in order of + * increasing u. However, all we actually have to do is follow this + * order with respect to sub-patches that might overlap, which can + * only occur as a result of non-monotonic curves; we can render + * each monotonic sub-patch in any order we want. Therefore, we + * begin by breaking up the patch into pieces that are monotonic + * with respect to all 4 edges. Since each edge has no more than + * 2 X and 2 Y split points (for a total of 4), taking both edges + * together we have a maximum of 8 split points for each axis. + * + * The current documentation doesn't say how the 4 curves + * correspond to the 'u' or 'v' edges. Pending clarification from + * Adobe, we assume the 1st and 3rd are the 'u' edges and the + * 2nd and 4th are the 'v' edges. + */ + double u[9], v[9]; + int nu = split2_xy(u, &curve[0], &curve[1].vertex.p, + &curve[2], &curve[3].vertex.p); + int nv = split2_xy(v, &curve[1], &curve[2].vertex.p, + &curve[3], &curve[0].vertex.p); + int iu, iv, ju, jv, ku, kv; + double dku, dkv; + + /* + * At some future time, we should set check = false if the curves + * fall entirely within the bounding rectangle. (Only a small + * performance optimization, to avoid making this check for each + * triangle.) + */ + bool check = true; + +#ifdef DEBUG + if (gs_debug_c('2')) { + int k; + + dlputs("[2]patch curves:\n"); + for (k = 0; k < 4; ++k) + dprintf6(" (%g,%g) (%g,%g)(%g,%g)\n", + fixed2float(curve[k].vertex.p.x), + fixed2float(curve[k].vertex.p.y), + fixed2float(curve[k].control[0].x), + fixed2float(curve[k].control[0].y), + fixed2float(curve[k].control[1].x), + fixed2float(curve[k].control[1].y)); + } +#endif + /* Add boundary values to simplify the iteration. */ + u[nu] = 1; + v[nv] = 1; + + /* + * We're going to fill the curves by flatting them and then filling + * the resulting triangles. Start by computing the number of + * segments required for flattening each side of the patch. + */ + { + fixed flatness = float2fixed(pfs->pis->flatness); + int i; + int log2_k[4]; + + for (i = 0; i < 4; ++i) { + curve_segment cseg; + + cseg.p1 = curve[i].control[0]; + cseg.p2 = curve[i].control[1]; + cseg.pt = curve[(i + 1) & 3].vertex.p; + log2_k[i] = + gx_curve_log2_samples(curve[i].vertex.p.x, curve[i].vertex.p.y, + &cseg, flatness); + } + ku = 1 << max(log2_k[0], log2_k[2]); + kv = 1 << max(log2_k[1], log2_k[3]); + } + dku = (double)ku; + dkv = (double)kv; + + /* Now iterate over the sub-patches. */ + for (iv = 0, jv = 0; jv < kv;) { + double v0 = jv / dkv, v1 = (jv + 1) / dkv; + patch_color_t c0, c1, cv[4]; + + /* Subdivide the interval if it cross a split point. */ + +#define CHECK_SPLIT(ix, jx, x1, ax)\ + if ( x1 < ax[ix] ) jx++;\ + else if ( x1 == ax[ix] ) jx++, ix++;\ + else x1 = ax[ix++] + + CHECK_SPLIT(iv, jv, v1, v); + +#define PATCH_SET_COLOR(c, v)\ + if ( pfs->Function ) c.t = v.cc[0];\ + else memcpy(c.cc.paint.values, v.cc, sizeof(c.cc.paint.values)) + + PATCH_SET_COLOR(c0, curve[0].vertex); + PATCH_SET_COLOR(c1, curve[3].vertex); + patch_interpolate_color(&cv[0], &c0, &c1, + (const patch_fill_state_t *)pfs, v0); + patch_interpolate_color(&cv[1], &c0, &c1, + (const patch_fill_state_t *)pfs, v1); + PATCH_SET_COLOR(c0, curve[1].vertex); + PATCH_SET_COLOR(c1, curve[2].vertex); + patch_interpolate_color(&cv[2], &c0, &c1, + (const patch_fill_state_t *)pfs, v0); + patch_interpolate_color(&cv[3], &c0, &c1, + (const patch_fill_state_t *)pfs, v1); + +#undef PATCH_SET_COLOR + + for (iu = 0, ju = 0; ju < ku;) { + double u0 = ju / dku, u1 = (ju + 1) / dku; + patch_color_t cu[4]; + int code; + + CHECK_SPLIT(iu, ju, u1, u); + patch_interpolate_color(&cu[0], &cv[0], &cv[2], + (const patch_fill_state_t *)pfs, u0); + patch_resolve_color(&cu[0], (const patch_fill_state_t *)pfs); + patch_interpolate_color(&cu[1], &cv[0], &cv[2], + (const patch_fill_state_t *)pfs, u1); + patch_resolve_color(&cu[1], (const patch_fill_state_t *)pfs); + patch_interpolate_color(&cu[2], &cv[1], &cv[3], + (const patch_fill_state_t *)pfs, u1); + patch_resolve_color(&cu[2], (const patch_fill_state_t *)pfs); + patch_interpolate_color(&cu[3], &cv[1], &cv[3], + (const patch_fill_state_t *)pfs, u0); + patch_resolve_color(&cu[3], (const patch_fill_state_t *)pfs); + if_debug6('2', "[2]u[%d]=(%g,%g), v[%d]=(%g,%g)\n", + iu, u0, u1, iv, v0, v1); + + /* Fill the sub-patch given by ((u0,v0),(u1,v1)). */ + { + gs_fixed_point pts[4]; + + (*transform) (&pts[0], curve, interior, u0, v0); + (*transform) (&pts[1], curve, interior, u1, v0); + (*transform) (&pts[2], curve, interior, u1, v1); + (*transform) (&pts[3], curve, interior, u0, v1); + code = mesh_fill_triangle((const mesh_fill_state_t *)pfs, + &pts[0], cu[0].cc.paint.values, + &pts[1], cu[1].cc.paint.values, + &pts[2], cu[2].cc.paint.values, check); + if (code < 0) + return code; + code = mesh_fill_triangle((const mesh_fill_state_t *)pfs, + &pts[2], cu[2].cc.paint.values, + &pts[3], cu[3].cc.paint.values, + &pts[0], cu[0].cc.paint.values, check); + if (code < 0) + return code; + } + } + +#undef CHECK_SPLIT + + } + return 0; +} + +/* ---------------- Coons patch shading ---------------- */ + +/* Calculate the device-space coordinate corresponding to (u,v). */ +private void +Cp_transform(gs_fixed_point * pt, const patch_curve_t curve[4], + const gs_fixed_point ignore_interior[4], floatp u, floatp v) +{ + double co_u = 1.0 - u, co_v = 1.0 - v; + gs_fixed_point c1u, d1v, c2u, d2v; + + curve_eval(&c1u, &curve[C1START].vertex.p, + &curve[C1CTRL].control[0], &curve[C1CTRL].control[1], + &curve[C1END].vertex.p, u); + curve_eval(&d1v, &curve[D1START].vertex.p, + &curve[D1XCTRL].control[1], &curve[D1XCTRL].control[0], + &curve[D1END].vertex.p, v); + curve_eval(&c2u, &curve[C2START].vertex.p, + &curve[C2XCTRL].control[1], &curve[C2XCTRL].control[0], + &curve[C2END].vertex.p, u); + curve_eval(&d2v, &curve[D2START].vertex.p, + &curve[D2CTRL].control[0], &curve[D2CTRL].control[1], + &curve[D2END].vertex.p, v); +#define COMPUTE_COORD(xy)\ + pt->xy = (fixed)\ + ((co_v * c1u.xy + v * c2u.xy) + (co_u * d1v.xy + u * d2v.xy) -\ + (co_v * (co_u * curve[C1START].vertex.p.xy +\ + u * curve[C1END].vertex.p.xy) +\ + v * (co_u * curve[C2START].vertex.p.xy +\ + u * curve[C2END].vertex.p.xy))) + COMPUTE_COORD(x); + COMPUTE_COORD(y); +#undef COMPUTE_COORD + if_debug4('2', "[2](u=%g,v=%g) => (%g,%g)\n", + u, v, fixed2float(pt->x), fixed2float(pt->y)); +} + +int +gs_shading_Cp_fill_rectangle(const gs_shading_t * psh0, const gs_rect * rect, + gx_device * dev, gs_imager_state * pis) +{ + const gs_shading_Cp_t *psh = (const gs_shading_Cp_t *)psh0; + patch_fill_state_t state; + shade_coord_stream_t cs; + patch_curve_t curve[4]; + int code; + + mesh_init_fill_state((mesh_fill_state_t *) & state, + (const gs_shading_mesh_t *)psh0, rect, dev, pis); + state.Function = psh->params.Function; + shade_next_init(&cs, (const gs_shading_mesh_params_t *)&psh->params, + pis); + while ((code = shade_next_patch(&cs, psh->params.BitsPerFlag, + curve, NULL)) == 0 && + (code = patch_fill(&state, curve, NULL, Cp_transform)) >= 0 + ) + DO_NOTHING; + return min(code, 0); +} + +/* ---------------- Tensor product patch shading ---------------- */ + +/* Calculate the device-space coordinate corresponding to (u,v). */ +private void +Tpp_transform(gs_fixed_point * pt, const patch_curve_t curve[4], + const gs_fixed_point interior[4], floatp u, floatp v) +{ + double u2 = u * u, co_u = 1.0 - u, co_u2 = co_u * co_u; + double v2 = v * v, co_v = 1.0 - v, co_v2 = co_v * co_v; + double Bu[4], Bv[4]; + gs_fixed_point pts[4][4]; + int i, j; + fixed x = 0, y = 0; + + /* Compute the Bernstein polynomials of u and v. */ + Bu[0] = co_u * co_u2, Bu[1] = 3 * u * co_u2, + Bu[2] = 3 * u2 * co_u, Bu[3] = u * u2; + Bv[0] = co_v * co_v2, Bv[1] = 3 * v * co_v2, + Bv[2] = 3 * v2 * co_v, Bv[3] = v * v2; + + /* Arrange the points into an indexable order. */ + pts[0][0] = curve[0].vertex.p; + pts[1][0] = curve[0].control[0]; + pts[2][0] = curve[0].control[1]; + pts[3][0] = curve[1].vertex.p; + pts[3][1] = curve[1].control[0]; + pts[3][2] = curve[1].control[1]; + pts[3][3] = curve[2].vertex.p; + pts[2][3] = curve[2].control[0]; + pts[1][3] = curve[2].control[1]; + pts[0][3] = curve[3].vertex.p; + pts[0][2] = curve[3].control[0]; + pts[0][1] = curve[3].control[1]; + pts[1][1] = interior[0]; + pts[2][1] = interior[1]; + pts[2][2] = interior[2]; + pts[1][2] = interior[3]; + + /* Now compute the actual point. */ + for (i = 0; i < 4; ++i) + for (j = 0; j < 4; ++j) { + double coeff = Bu[i] * Bv[j]; + + x += pts[i][j].x * coeff, y += pts[i][j].y * coeff; + } + pt->x = x, pt->y = y; +} + +int +gs_shading_Tpp_fill_rectangle(const gs_shading_t * psh0, const gs_rect * rect, + gx_device * dev, gs_imager_state * pis) +{ + const gs_shading_Tpp_t *psh = (const gs_shading_Tpp_t *)psh0; + patch_fill_state_t state; + shade_coord_stream_t cs; + patch_curve_t curve[4]; + gs_fixed_point interior[4]; + int code; + + mesh_init_fill_state((mesh_fill_state_t *) & state, + (const gs_shading_mesh_t *)psh0, rect, dev, pis); + state.Function = psh->params.Function; + shade_next_init(&cs, (const gs_shading_mesh_params_t *)&psh->params, + pis); + while ((code = shade_next_patch(&cs, psh->params.BitsPerFlag, + curve, interior)) == 0 && + (code = patch_fill(&state, curve, interior, Tpp_transform)) >= 0 + ) + DO_NOTHING; + return min(code, 0); +} diff --git a/gs/src/gxtext.h b/gs/src/gxtext.h new file mode 100644 index 000000000..4ed5fbaae --- /dev/null +++ b/gs/src/gxtext.h @@ -0,0 +1,115 @@ +/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: gstext.h */ +/* Driver text interface implementation support */ + +#ifndef gxtext_INCLUDED +# define gxtext_INCLUDED + +#include "gstext.h" + +/* EVERYTHING IN THIS FILE IS SUBJECT TO CHANGE WITHOUT NOTICE. */ + +/* + * Define the control parameter for setting text metrics. + */ +typedef enum { + TEXT_SET_CHAR_WIDTH, + TEXT_SET_CACHE_DEVICE, + TEXT_SET_CACHE_DEVICE2 +} gs_text_cache_control_t; + +/* + * Define the procedures associated with text display. + */ +struct gs_text_enum_procs_s { + + /* + * Process the text. Then client should call this repeatedly until + * it returns <= 0. (> 0 means the client must intervene.) + */ + +#define text_enum_proc_process(proc)\ + int proc(P1(gs_text_enum_t *penum)) + + text_enum_proc_process((*process)); + + /* + * Set the character width and optionally bounding box, + * and enable caching. + */ + +#define text_enum_proc_set_cache(proc)\ + int proc(P3(gs_text_enum_t *penum, const double *values,\ + gs_text_cache_control_t control)) + + text_enum_proc_set_cache((*set_cache)); + +}; + +/* Abstract types */ +#ifndef gs_imager_state_DEFINED +# define gs_imager_state_DEFINED +typedef struct gs_imager_state_s gs_imager_state; + +#endif +#ifndef gx_device_color_DEFINED +# define gx_device_color_DEFINED +typedef struct gx_device_color_s gx_device_color; + +#endif +#ifndef gs_font_DEFINED +# define gs_font_DEFINED +typedef struct gs_font_s gs_font; + +#endif +#ifndef gx_path_DEFINED +# define gx_path_DEFINED +typedef struct gx_path_s gx_path; + +#endif +#ifndef gx_clip_path_DEFINED +# define gx_clip_path_DEFINED +typedef struct gx_clip_path_s gx_clip_path; + +#endif + +/* + * Define the driver procedure for text. + */ +#define dev_t_proc_text_begin(proc, dev_t)\ + int proc(P9(dev_t *dev,\ + gs_imager_state *pis,\ + const gs_text_params_t *text,\ + const gs_font *font,\ + gx_path *path, /* unless DO_NONE & !RETURN_WIDTH */\ + const gx_device_color *pdcolor, /* DO_DRAW */\ + const gx_clip_path *pcpath, /* DO_DRAW */\ + gs_memory_t *memory,\ + gs_text_enum_t **ppenum)) +#define dev_proc_text_begin(proc)\ + dev_t_proc_text_begin(proc, gx_device) + +/* + * Begin processing text. This calls the device procedure, and also + * initializes the common parts of the enumerator. + */ +dev_proc_text_begin(gx_device_text_begin); + +#endif /* gxtext_INCLUDED */ diff --git a/gs/src/icstate.h b/gs/src/icstate.h new file mode 100644 index 000000000..59a2f8abf --- /dev/null +++ b/gs/src/icstate.h @@ -0,0 +1,70 @@ +/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: icstate.h */ +/* Externally visible context state */ +/* Requires iref.h */ + +#ifndef icstate_INCLUDED +# define icstate_INCLUDED + +#include "imemory.h" + +/* + * Define the externally visible state of an interpreter context. + * If we aren't supporting Display PostScript features, there is only + * a single context. + */ +#ifndef gs_context_state_t_DEFINED +# define gs_context_state_t_DEFINED +typedef struct gs_context_state_s gs_context_state_t; + +#endif +#ifndef ref_stack_DEFINED +# define ref_stack_DEFINED +typedef struct ref_stack_s ref_stack; + +#endif +struct gs_context_state_s { + ref_stack *dstack; + ref_stack *estack; + ref_stack *ostack; + gs_state *pgs; + gs_dual_memory_t memory; + ref array_packing; /* t_boolean */ + ref binary_object_format; /* t_integer */ + long rand_state; /* (not in Red Book) */ + long usertime_total; /* total accumulated usertime, */ + /* not counting current time if running */ + bool keep_usertime; /* true if context ever executed usertime */ + /* View clipping is handled in the graphics state. */ + ref userparams; /* t_dictionary */ + ref stdio[2]; /* t_file */ +}; + +/* + * We make st_context_state public because interp.c must allocate one, + * and zcontext.c must subclass it. + */ + /*extern_st(st_context_state); *//* in icontext.h */ +#define public_st_context_state() /* in icontext.c */\ + gs_public_st_complex_only(st_context_state, gs_context_state_t,\ + "gs_context_state_t", context_state_clear_marks,\ + context_state_enum_ptrs, context_state_reloc_ptrs, 0) + +#endif /* icstate_INCLUDED */ diff --git a/gs/src/idictdef.h b/gs/src/idictdef.h new file mode 100644 index 000000000..a5d21e556 --- /dev/null +++ b/gs/src/idictdef.h @@ -0,0 +1,122 @@ +/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: idictdef.h */ +/* Internals of dictionary implementation */ + +#ifndef idictdef_INCLUDED +# define idictdef_INCLUDED + +/* + * A dictionary of capacity M is a structure containing the following + * elements (refs): + * + * keys - a t_shortarray or t_array of M+1 elements, containing + * the keys. + * + * values - a t_array of M+1 elements, containing the values. + * + * count - a t_integer whose value tells how many entries are + * occupied (N). + * + * maxlength - a t_integer whose value gives the client's view of + * the capacity (C). C may be less than M (see below). + * + * memory - a foreign t_struct referencing the allocator used to + * create this dictionary, which will also be used to expand or + * unpack it if necessary. + * + * C < M is possible because on large-memory systems, we usually round up M + * so that M is a power of 2 (see idict.h for details); this allows us to + * use masking rather than division for computing the initial hash probe. + * However, C is always the maxlength specified by the client, so clients + * get a consistent story. + * + * As noted above, the keys may be either in packed or unpacked form. + * The markers for unused and deleted entries are different in the two forms. + * In the packed form: + * unused entries contain packed_key_empty; + * deleted entries contain packed_key_deleted. + * In the unpacked form: + * unused entries contain a literal null; + * deleted entries contain an executable null. + * + * The first entry is always marked deleted, to reduce the cost of the + * wrap-around check. + * + * Note that if the keys slot in the dictionary is new, + * all the key slots are new (more recent than the last save). + * We use this fact to avoid saving stores into packed keys + * for newly created dictionaries. + * + * Note that name keys with indices above packed_name_max_index require using + * the unpacked form. */ +#define dict_is_packed(dct) r_has_type(&(dct)->keys, t_shortarray) +#define packed_key_empty (pt_tag(pt_integer) + 0) +#define packed_key_deleted (pt_tag(pt_integer) + 1) +#define packed_key_impossible pt_tag(pt_full_ref) /* never matches */ +#define packed_name_key(nidx)\ + ((nidx) <= packed_name_max_index ? pt_tag(pt_literal_name) + (nidx) :\ + packed_key_impossible) +/* + * Using a special mark for deleted entries causes lookup time to degrade + * as entries are inserted and deleted. This is not a problem, because + * entries are almost never deleted. + */ +#define d_maxlength(dct) ((uint)((dct)->maxlength.value.intval)) +#define d_set_maxlength(dct,siz) ((dct)->maxlength.value.intval = (siz)) +#define nslots(dct) r_size(&(dct)->values) +#define npairs(dct) (nslots(dct) - 1) +#define d_length(dct) ((uint)((dct)->count.value.intval)) + +/* + * Define macros for searching a packed dictionary. Free variables: + * ref_packed kpack - holds the packed key. + * uint hash - holds the hash of the name. + * dict *pdict - points to the dictionary. + * uint size - holds npairs(pdict). + * Note that the macro is *not* enclosed in {}, so that we can access + * the values of kbot and kp after leaving the loop. + * + * We break the macro into two to avoid overflowing some preprocessors. + */ +/* packed_search_body also uses kp and kbot as free variables. */ +#define packed_search_value_pointer (pdict->values.value.refs + (kp - kbot)) +#define packed_search_body(found1,found2,del,miss)\ + { if_debug2('D', "[D]probe 0x%lx: 0x%x\n", (ulong)kp, *kp);\ + if ( *kp == kpack )\ + { found1;\ + found2;\ + }\ + else if ( !r_packed_is_name(kp) )\ + { /* Empty, deleted, or wraparound. Figure out which. */\ + if ( *kp == packed_key_empty ) miss;\ + if ( kp == kbot ) break; /* wrap */\ + else { del; }\ + }\ + } +#define packed_search_1(found1,found2,del,miss)\ + const ref_packed *kbot = pdict->keys.value.packed;\ + register const ref_packed *kp;\ + for ( kp = kbot + dict_hash_mod(hash, size) + 1; ; kp-- )\ + packed_search_body(found1,found2,del,miss) +#define packed_search_2(found1,found2,del,miss)\ + for ( kp += size; ; kp-- )\ + packed_search_body(found1,found2,del,miss) + +#endif /* idictdef_INCLUDED */ diff --git a/gs/src/idstack.c b/gs/src/idstack.c new file mode 100644 index 000000000..5cc0d457d --- /dev/null +++ b/gs/src/idstack.c @@ -0,0 +1,243 @@ +/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: idstack.c */ +/* Implementation of dictionary stacks */ +#include "ghost.h" +#include "idict.h" +#include "idictdef.h" +#include "idstack.h" +#include "inamedef.h" +#include "iname.h" +#include "ipacked.h" +#include "iutil.h" +#include "ivmspace.h" + +/* Debugging statistics */ +#ifdef DEBUG +#include "idebug.h" +long ds_lookups; /* total lookups */ +long ds_1probe; /* successful lookups on only 1 probe */ +long ds_2probe; /* successful lookups on 2 probes */ + +/* Wrapper for dstack_find_name_by_index */ +ref *real_dstack_find_name_by_index(P2(dict_stack_t * pds, uint nidx)); +ref * +dstack_find_name_by_index(dict_stack_t * pds, uint nidx) +{ + ref *pvalue = real_dstack_find_name_by_index(pds, nidx); + dict *pdict = pds->stack.p->value.pdict; + + ds_lookups++; + if (dict_is_packed(pdict)) { + uint hash = + dict_hash_mod(dict_name_index_hash(nidx), npairs(pdict)) + 1; + + if (pdict->keys.value.packed[hash] == + pt_tag(pt_literal_name) + nidx + ) + ds_1probe++; + else if (pdict->keys.value.packed[hash - 1] == + pt_tag(pt_literal_name) + nidx + ) + ds_2probe++; + } + /* Do the cheap flag test before the expensive remainder test. */ + if (gs_debug_c('d') && !(ds_lookups % 1000)) + dlprintf3("[d]lookups=%ld 1probe=%ld 2probe=%ld\n", + ds_lookups, ds_1probe, ds_2probe); + return pvalue; +} +#define dstack_find_name_by_index real_dstack_find_name_by_index +#endif + +/* Check whether a dictionary is one of the permanent ones on the d-stack. */ +bool +dstack_dict_is_permanent(const dict_stack_t * pds, const ref * pdref) +{ + dict *pdict = pdref->value.pdict; + int i; + + if (pds->stack.extension_size == 0) { /* Only one block of d-stack. */ + for (i = 0; i < pds->min_size; ++i) + if (pds->stack.bot[i].value.pdict == pdict) + return true; + } else { /* More than one block of d-stack. */ + uint count = ref_stack_count(&pds->stack); + + for (i = count - pds->min_size; i < count; ++i) + if (ref_stack_index(&pds->stack, i)->value.pdict == pdict) + return true; + } + return false; +} + +/* + * Look up a name on the dictionary stack. + * Return the pointer to the value if found, 0 if not. + */ +ref * +dstack_find_name_by_index(dict_stack_t * pds, uint nidx) +{ + ds_ptr pdref = pds->stack.p; + +/* Since we know the hash function is the identity function, */ +/* there's no point in allocating a separate variable for it. */ +#define hash dict_name_index_hash(nidx) + ref_packed kpack = packed_name_key(nidx); + + do { + dict *pdict = pdref->value.pdict; + uint size = npairs(pdict); + +#ifdef DEBUG + if (gs_debug_c('D')) { + ref dnref; + + name_index_ref(nidx, &dnref); + dlputs("[D]lookup "); + debug_print_name(&dnref); + dprintf3(" in 0x%lx(%u/%u)\n", + (ulong) pdict, dict_length(pdref), + dict_maxlength(pdref)); + } +#endif + if (dict_is_packed(pdict)) { + packed_search_1(DO_NOTHING, + return packed_search_value_pointer, + DO_NOTHING, goto miss); + packed_search_2(DO_NOTHING, + return packed_search_value_pointer, + DO_NOTHING, break); + miss:; + } else { + ref *kbot = pdict->keys.value.refs; + register ref *kp; + int wrap = 0; + + /* Search the dictionary */ + for (kp = kbot + dict_hash_mod(hash, size) + 2;;) { + --kp; + if (r_has_type(kp, t_name)) { + if (name_index(kp) == nidx) + return pdict->values.value.refs + + (kp - kbot); + } else if (r_has_type(kp, t_null)) { /* Empty, deleted, or wraparound. */ + /* Figure out which. */ + if (!r_has_attr(kp, a_executable)) + break; + if (kp == kbot) { /* wrap */ + if (wrap++) + break; /* 2 wraps */ + kp += size + 1; + } + } + } + } + } + while (pdref-- > pds->stack.bot); + /* The name isn't in the top dictionary block. */ + /* If there are other blocks, search them now (more slowly). */ + if (!pds->stack.extension_size) /* no more blocks */ + return (ref *) 0; + { /* We could use the STACK_LOOP macros, but for now, */ + /* we'll do things the simplest way. */ + ref key; + uint i = pds->stack.p + 1 - pds->stack.bot; + uint size = ref_stack_count(&pds->stack); + ref *pvalue; + + name_index_ref(nidx, &key); + for (; i < size; i++) { + if (dict_find(ref_stack_index(&pds->stack, i), + &key, &pvalue) > 0 + ) + return pvalue; + } + } + return (ref *) 0; +#undef hash +} + +/* Set the cached values computed from the top entry on the dstack. */ +/* See idstack.h for details. */ +private const ref_packed no_packed_keys[2] = +{packed_key_deleted, packed_key_empty}; +void +dstack_set_top(dict_stack_t * pds) +{ + ds_ptr dsp = pds->stack.p; + dict *pdict = dsp->value.pdict; + + if_debug3('d', "[d]dsp = 0x%lx -> 0x%lx, key array type = %d\n", + (ulong) dsp, (ulong) pdict, r_type(&pdict->keys)); + if (dict_is_packed(pdict) && + r_has_attr(dict_access_ref(dsp), a_read) + ) { + pds->top_keys = pdict->keys.value.packed; + pds->top_npairs = npairs(pdict); + pds->top_values = pdict->values.value.refs; + } else { + pds->top_keys = no_packed_keys; + pds->top_npairs = 1; + } + if (!r_has_attr(dict_access_ref(dsp), a_write)) + pds->def_space = -1; + else + pds->def_space = r_space(dsp); +} + +/* After a garbage collection, scan the permanent dictionaries and */ +/* update the cached value pointers in names. */ +void +dstack_gc_cleanup(dict_stack_t * pds) +{ + uint count = ref_stack_count(&pds->stack); + uint dsi; + + for (dsi = pds->min_size; dsi > 0; --dsi) { + const dict *pdict = + ref_stack_index(&pds->stack, count - dsi)->value.pdict; + uint size = nslots(pdict); + ref *pvalue = pdict->values.value.refs; + uint i; + + for (i = 0; i < size; ++i, ++pvalue) { + ref key; + ref *old_pvalue; + + array_get(&pdict->keys, (long)i, &key); + if (r_has_type(&key, t_name) && + pv_valid(old_pvalue = key.value.pname->pvalue) + ) { /* + * The name only has a single definition, + * so it must be this one. Check to see if + * no relocation is actually needed; if so, + * we can skip the entire dictionary. + */ + if (old_pvalue == pvalue) { + if_debug1('d', "[d]skipping dstack entry %d\n", + dsi - 1); + break; + } + /* Update the value pointer. */ + key.value.pname->pvalue = pvalue; + } + } + } +} diff --git a/gs/src/idstack.h b/gs/src/idstack.h new file mode 100644 index 000000000..62cedf6bb --- /dev/null +++ b/gs/src/idstack.h @@ -0,0 +1,119 @@ +/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: idstack.h */ +/* Generic dictionary stack API */ + +#ifndef idstack_INCLUDED +# define idstack_INCLUDED + +#include "istack.h" + +/* Define the dictionary stack structure. */ +typedef struct dict_stack_s { + + ref_stack stack; /* the actual stack of dictionaries */ + +/* + * Switching between Level 1 and Level 2 involves inserting and removing + * globaldict on the dictionary stack. Instead of truly inserting and + * removing entries, we replace globaldict by a copy of systemdict in + * Level 1 mode. min_dstack_size, the minimum number of entries, does not + * change depending on language level; the countdictstack and dictstack + * operators must take this into account. + */ + uint min_size; /* size of stack after clearing */ + + int userdict_index; /* index of userdict on stack */ + +/* + * Cache a value for fast checking of def operations. + * If the top entry on the dictionary stack is a writable dictionary, + * dsspace is the space of the dictionary; if it is a non-writable + * dictionary, dsspace = -1. Then def is legal precisely if + * r_space(pvalue) <= dsspace. Note that in order for this trick to work, + * the result of r_space must be a signed integer; some compilers treat + * enums as unsigned, probably in violation of the ANSI standard. + */ + int def_space; + +/* + * Cache values for fast name lookup. If the top entry on the dictionary + * stack is a readable dictionary with packed keys, dtop_keys, dtop_npairs, + * and dtop_values are keys.value.packed, npairs, and values.value.refs + * for that dictionary; otherwise, these variables point to a dummy + * empty dictionary. + */ + const ref_packed *top_keys; + uint top_npairs; + ref *top_values; + +/* + * Cache a copy of the bottom entry on the stack, which is never deleted. + */ + ref system_dict; + +} dict_stack_t; + +/* + * Reset the cached top values. Every routine that alters the + * dictionary stack (including changing the protection or size of the + * top dictionary on the stack) must call this. + */ +void dstack_set_top(P1(dict_stack_t *)); + +/* Check whether a dictionary is one of the permanent ones on the d-stack. */ +bool dstack_dict_is_permanent(P2(const dict_stack_t *, const ref *)); + +/* Define the type of pointers into the dictionary stack. */ +typedef s_ptr ds_ptr; +typedef const_s_ptr const_ds_ptr; + +/* Clean up a dictionary stack after a garbage collection. */ +void dstack_gc_cleanup(P1(dict_stack_t *)); + +/* + * Define a special fast entry for name lookup on a dictionary stack. + * The key is known to be a name; search the entire dict stack. + * Return the pointer to the value slot. + * If the name isn't found, just return 0. + */ +ref *dstack_find_name_by_index(P2(dict_stack_t *, uint)); + +/* + * Define an extra-fast macro for name lookup, optimized for + * a single-probe lookup in the top dictionary on the stack. + * Amazingly enough, this seems to hit over 90% of the time + * (aside from operators, of course, which are handled either with + * the special cache pointer or with 'bind'). + */ +#define dstack_find_name_by_index_inline(pds,nidx,htemp)\ + ((pds)->top_keys[htemp = dict_hash_mod_inline(dict_name_index_hash(nidx),\ + (pds)->top_npairs) + 1] == pt_tag(pt_literal_name) + (nidx) ?\ + (pds)->top_values + htemp : dstack_find_name_by_index(pds, nidx)) +/* + * Define a similar macro that only checks the top dictionary on the stack. + */ +#define if_dstack_find_name_by_index_top(pds,nidx,htemp,pvslot)\ + if ( (((pds)->top_keys[htemp = dict_hash_mod_inline(dict_name_index_hash(nidx),\ + (pds)->top_npairs) + 1] == pt_tag(pt_literal_name) + (nidx)) ?\ + ((pvslot) = (pds)->top_values + (htemp), 1) :\ + 0)\ + ) + +#endif /* idstack_INCLUDED */ diff --git a/gs/src/iestack.h b/gs/src/iestack.h new file mode 100644 index 000000000..71bf7a295 --- /dev/null +++ b/gs/src/iestack.h @@ -0,0 +1,50 @@ +/* Copyright (C) 1989, 1992, 1993, 1994, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: iestack.h */ +/* Generic execution stack API */ + +#ifndef iestack_INCLUDED +# define iestack_INCLUDED + +#include "istack.h" + +/* Define the execution stack structure. */ +typedef struct exec_stack_s { + + ref_stack stack; /* the actual execution stack */ + +/* + * To improve performance, we cache the currentfile pointer + * (i.e., `shallow-bind' it in Lisp terminology). The invariant is as + * follows: either esfile points to the currentfile slot on the estack + * (i.e., the topmost slot with an executable file), or it is 0. + * To maintain the invariant, it is sufficient that whenever a routine + * pushes or pops anything on the estack, if the object *might* be + * an executable file, invoke esfile_clear_cache(); alternatively, + * immediately after pushing an object, invoke esfile_check_cache(). + */ + ref *current_file; + +} exec_stack_t; + +/* Define pointers into the execution stack. */ +typedef s_ptr es_ptr; +typedef const_s_ptr const_es_ptr; + +#endif /* iestack_INCLUDED */ diff --git a/gs/src/iimage2.h b/gs/src/iimage2.h new file mode 100644 index 000000000..45cbdc71b --- /dev/null +++ b/gs/src/iimage2.h @@ -0,0 +1,44 @@ +/* Copyright (C) 1997 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: iimage2.h */ +/* Requires gsiparam.h */ + +#ifndef iimage2_INCLUDED +# define iimage2_INCLUDED + +/* These procedures are exported by zimage2.c for other modules. */ + +/* + * Define a structure for image parameters other than those defined + * in the gs_*image*_t structure. + */ +typedef struct image_params_s { + bool MultipleDataSources; + ref DataSource[gs_image_max_components]; + const float *pDecode; +} image_params; + +/* Extract and check parameters for an image. */ +int data_image_params(P6(const ref * op, gs_data_image_t * pim, + image_params * pip, bool require_DataSource, + int num_components, int max_bits_per_component)); +int pixel_image_params(P4(const ref * op, gs_pixel_image_t * pim, + image_params * pip, int max_bits_per_component)); + +#endif /* iimage2_INCLUDED */ diff --git a/gs/src/inames.h b/gs/src/inames.h new file mode 100644 index 000000000..ca7aa60ab --- /dev/null +++ b/gs/src/inames.h @@ -0,0 +1,108 @@ +/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: inames.h */ +/* Name table interface */ + +#ifndef inames_INCLUDED +# define inames_INCLUDED + +/* + * This file defines those parts of the name table API that depend neither + * on the implementation nor on the existence of a single distinguished + * instance. Procedures in this file begin with names_. + */ + +/* ---------------- Interface types ---------------- */ + +#ifndef name_table_DEFINED +# define name_table_DEFINED +typedef struct name_table_s name_table; + +#endif + +typedef uint name_index_t; + +/* ---------------- Constant values ---------------- */ + +extern const uint name_max_string; + +/* ---------------- Procedural interface ---------------- */ + +/* Allocate and initialize a name table. */ +name_table *names_init(P2(ulong size, gs_memory_t * mem)); + +/* Get the allocator for a name table. */ +gs_memory_t *names_memory(P1(const name_table * nt)); + +/* + * Look up and/or enter a name in the name table. + * The values of enterflag are: + * -1 -- don't enter (return an error) if missing; + * 0 -- enter if missing, don't copy the string, which was allocated + * statically; + * 1 -- enter if missing, copy the string; + * 2 -- enter if missing, don't copy the string, which was already + * allocated dynamically (using the names_memory allocator). + * Possible errors: VMerror, limitcheck (if string is too long or if + * we have assigned all possible name indices). + */ +int names_ref(P5(name_table * nt, const byte * ptr, uint size, ref * pnref, + int enterflag)); +void names_string_ref(P3(const name_table * nt, const ref * pnref, ref * psref)); + +/* + * names_enter_string calls names_ref with a (permanent) C string. + */ +int names_enter_string(P3(name_table * nt, const char *str, ref * pnref)); + +/* + * names_from_string essentially implements cvn. + * It always enters the name, and copies the executable attribute. + */ +int names_from_string(P3(name_table * nt, const ref * psref, ref * pnref)); + +/* Compare two names for equality. */ +#define names_eq(pnref1, pnref2)\ + ((pnref1)->value.pname == (pnref2)->value.pname) + +/* Invalidate the value cache for a name. */ +void names_invalidate_value_cache(P2(name_table * nt, const ref * pnref)); + +/* Convert between names and indices. */ +name_index_t names_index(P2(const name_table * nt, const ref * pnref)); /* ref => index */ +name *names_index_ptr(P2(const name_table * nt, name_index_t nidx)); /* index => name */ +void names_index_ref(P3(const name_table * nt, name_index_t nidx, ref * pnref)); /* index => ref */ + +/* Get the index of the next valid name. */ +/* The argument is 0 or a valid index. */ +/* Return 0 if there are no more. */ +name_index_t names_next_valid_index(P2(name_table * nt, name_index_t nidx)); + +/* Mark a name for the garbage collector. */ +/* Return true if this is a new mark. */ +bool names_mark_index(P2(name_table * nt, name_index_t nidx)); + +/* Get the object (sub-table) containing a name. */ +/* The garbage collector needs this so it can relocate pointers to names. */ +void /*obj_header_t */ * + names_ref_sub_table(P2(name_table * nt, const ref * pnref)); +void /*obj_header_t */ * + names_index_ptr_sub_table(P3(name_table * nt, name_index_t nidx, name * pname)); + +#endif /* inames_INCLUDED */ diff --git a/gs/src/inouparm.c b/gs/src/inouparm.c new file mode 100644 index 000000000..3239809e5 --- /dev/null +++ b/gs/src/inouparm.c @@ -0,0 +1,28 @@ +/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: inouparm.c */ +/* Dummy set_user_params for Level 1 systems */ +#include "ghost.h" +#include "icontext.h" /* for set_user_params prototype */ + +int +set_user_params(const ref * op) +{ + return 0; +} diff --git a/gs/src/iostack.h b/gs/src/iostack.h new file mode 100644 index 000000000..342914a23 --- /dev/null +++ b/gs/src/iostack.h @@ -0,0 +1,39 @@ +/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: iostack.h */ +/* Generic operand stack API */ + +#ifndef iostack_INCLUDED +# define iostack_INCLUDED + +#include "istack.h" + +/* Define pointers into the operand stack. */ +typedef s_ptr os_ptr; +typedef const_s_ptr const_os_ptr; + +/* Define the operand stack structure. */ +/* Currently this is just a generic ref stack. */ +typedef struct op_stack_s { + + ref_stack stack; /* the actual operand stack */ + +} op_stack_t; + +#endif /* iostack_INCLUDED */ diff --git a/gs/src/pcwin.mak b/gs/src/pcwin.mak new file mode 100644 index 000000000..1bbf5fcec --- /dev/null +++ b/gs/src/pcwin.mak @@ -0,0 +1,100 @@ +# Copyright (C) 1998 Aladdin Enterprises. All rights reserved. +# +# This file is part of Aladdin Ghostscript. +# +# Aladdin 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 Ghostscript Free Public +# License (the "License") for full details. +# +# Every copy of Aladdin 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 Aladdin 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: pcwin.mak +# makefile for PC window system (MS Windows and OS/2) -specific device +# drivers. + +# Define the name of this makefile. +PCWIN_MAK=$(GLSRC)pcwin.mak + +# We have to isolate these in their own file because the MS Windows code +# requires special compilation switches, different from all other files +# and platforms. + +### -------------------- The MS-Windows 3.n DLL ------------------------- ### + +gp_mswin_h=$(GLSRC)gp_mswin.h +gsdll_h=$(GLSRC)gsdll.h + +gdevmswn_h=$(GLSRC)gdevmswn.h $(GDEVH)\ + $(dos__h) $(memory__h) $(string__h) $(windows__h)\ + $(gp_mswin_h) + +$(GLOBJ)gdevmswn.$(OBJ): $(GLSRC)gdevmswn.c $(gdevmswn_h) $(gp_h) $(gpcheck_h)\ + $(gsdll_h) $(gsparam_h) $(gdevpccm_h) + $(GLCCWIN) $(GLO_)gdevmswn.$(OBJ) $(C_) $(GLSRC)gdevmswn.c + +$(GLOBJ)gdevmsxf.$(OBJ): $(GLSRC)gdevmsxf.c $(ctype__h) $(math__h) $(memory__h) $(string__h)\ + $(gdevmswn_h) $(gsstruct_h) $(gsutil_h) $(gxxfont_h) + $(GLCCWIN) $(GLO_)gdevmsxf.$(OBJ) $(C_) $(GLSRC)gdevmsxf.c + +# An implementation using a DIB filled by an image device. +$(GLOBJ)gdevwdib.$(OBJ): $(GLSRC)gdevwdib.c $(gdevmswn_h) $(gsdll_h) $(gxdevmem_h) + $(GLCCWIN) $(GLO_)gdevwdib.$(OBJ) $(C_) $(GLSRC)gdevwdib.c + +mswindll1_=$(GLOBJ)gdevmswn.$(OBJ) $(GLOBJ)gdevmsxf.$(OBJ) $(GLOBJ)gdevwdib.$(OBJ) +mswindll2_=$(GLOBJ)gdevemap.$(OBJ) $(GLOBJ)gdevpccm.$(OBJ) +mswindll_=$(mswindll1_) $(mswindll2_) +mswindll.dev: $(mswindll_) + $(SETDEV) mswindll $(mswindll1_) + $(ADDMOD) mswindll $(mswindll2_) + +### -------------------- The MS-Windows DDB 3.n printer ----------------- ### + +mswinprn_=$(GLOBJ)gdevwprn.$(OBJ) $(GLOBJ)gdevmsxf.$(OBJ) +mswinprn.dev: $(mswinprn_) + $(SETDEV) mswinprn $(mswinprn_) + +$(GLOBJ)gdevwprn.$(OBJ): $(GLSRC)gdevwprn.c $(gdevmswn_h) $(gp_h) + $(GLCCWIN) $(GLO_)gdevwprn.$(OBJ) $(C_) $(GLSRC)gdevwprn.c + +### -------------------- The MS-Windows DIB 3.n printer ----------------- ### + +mswinpr2_=$(GLOBJ)gdevwpr2.$(OBJ) +mswinpr2.dev: $(mswinpr2_) page.dev + $(SETPDEV) mswinpr2 $(mswinpr2_) + +$(GLOBJ)gdevwpr2.$(OBJ): $(GLSRC)gdevwpr2.c $(PDEVH) $(windows__h)\ + $(gdevpccm_h) $(gp_h) $(gp_mswin_h) + $(GLCCWIN) $(GLO_)gdevwpr2.$(OBJ) $(C_) $(GLSRC)gdevwpr2.c + +### ------------------ OS/2 Presentation Manager device ----------------- ### + +os2pm_=$(GLOBJ)gdevpm.$(OBJ) $(GLOBJ)gdevpccm.$(OBJ) +os2pm.dev: $(os2pm_) + $(SETDEV) os2pm $(os2pm_) + +os2dll_=$(GLOBJ)gdevpm.$(OBJ) $(GLOBJ)gdevpccm.$(OBJ) +os2dll.dev: $(os2dll_) + $(SETDEV) os2dll $(os2dll_) + +$(GLOBJ)gdevpm.$(OBJ): $(GLSRC)gdevpm.c $(string__h)\ + $(gp_h) $(gpcheck_h)\ + $(gsdll_h) $(gserrors_h) $(gsexit_h) $(gsparam_h)\ + $(gx_h) $(gxdevice_h) $(gxdevmem_h)\ + $(gdevpccm_h) $(GLSRC)gdevpm.h + $(GLCC) $(GLO_)gdevpm.$(OBJ) $(C_) $(GLSRC)gdevpm.c + +### --------------------------- The OS/2 printer ------------------------ ### + +os2prn_=$(GLOBJ)gdevos2p.$(OBJ) +os2prn.dev: $(os2prn_) page.dev + $(SETPDEV) os2prn $(os2prn_) + +$(GLOBJ)gdevos2p.$(OBJ): gdevos2p.c $(gp_h) $(gdevpccm_h) $(gdevprn_h) $(gscdefs_h) + $(GLCC) $(GLO_)gdevos2p.$(OBJ) $(C_) $(GLSRC)gdevos2p.c diff --git a/gs/src/pipe_.h b/gs/src/pipe_.h new file mode 100644 index 000000000..758a82627 --- /dev/null +++ b/gs/src/pipe_.h @@ -0,0 +1,36 @@ +/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: pipe_.h */ +/* Declaration of popen and pclose */ + +#ifndef pipe__INCLUDED +# define pipe__INCLUDED + +#include "stdio_.h" + +/* + * popen isn't POSIX-standard, so we declare it here. + * Because of inconsistent (and sometimes incorrect) header files, + * we must omit the argument list. Unfortunately, this sometimes causes + * more trouble than it cures. + */ +extern FILE *popen( /* P2(const char *, const char *) */ ); +extern int pclose(P1(FILE *)); + +#endif /* pipe__INCLUDED */ diff --git a/gs/src/scfparam.c b/gs/src/scfparam.c new file mode 100644 index 000000000..233fc6861 --- /dev/null +++ b/gs/src/scfparam.c @@ -0,0 +1,93 @@ +/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: scfparam.c */ +/* CCITTFax filter parameter setting and reading */ +#include "std.h" +#include "gserror.h" +#include "gserrors.h" +#include "gstypes.h" +#include "gsmemory.h" +#include "gsparam.h" +#include "scommon.h" +#include "scf.h" /* for cfe_max_width */ +#include "scfx.h" + +/* Define the CCITTFax parameters. */ +private const gs_param_item_t s_CF_param_items[] = +{ +#define cfp(key, type, memb) { key, type, offset_of(stream_CF_state, memb) } + cfp("Uncompressed", gs_param_type_bool, Uncompressed), + cfp("K", gs_param_type_int, K), + cfp("EndOfLine", gs_param_type_bool, EndOfLine), + cfp("EncodedByteAlign", gs_param_type_bool, EncodedByteAlign), + cfp("Columns", gs_param_type_int, Columns), + cfp("Rows", gs_param_type_int, Rows), + cfp("EndOfBlock", gs_param_type_bool, EndOfBlock), + cfp("BlackIs1", gs_param_type_bool, BlackIs1), + cfp("DamagedRowsBeforeError", gs_param_type_int, DamagedRowsBeforeError), + cfp("FirstBitLowOrder", gs_param_type_bool, FirstBitLowOrder), + cfp("DecodedByteAlign", gs_param_type_int, DecodedByteAlign), +#undef cfp + gs_param_item_end +}; + +/* Define a limit on the Rows parameter, close to max_int. */ +#define cf_max_height 32000 + +/* Get non-default CCITTFax filter parameters. */ +stream_state_proc_get_params(s_CF_get_params, stream_CF_state); /* check */ +int +s_CF_get_params(gs_param_list * plist, const stream_CF_state * ss, bool all) +{ + stream_CF_state cfs_defaults; + const stream_CF_state *defaults; + + if (all) + defaults = 0; + else { + s_CF_set_defaults_inline(&cfs_defaults); + defaults = &cfs_defaults; + } + return gs_param_write_items(plist, ss, defaults, s_CF_param_items); +} + +/* Put CCITTFax filter parameters. */ +stream_state_proc_put_params(s_CF_put_params, stream_CF_state); /* check */ +int +s_CF_put_params(gs_param_list * plist, stream_CF_state * ss) +{ + stream_CF_state state; + int code; + + state = *ss; + code = gs_param_read_items(plist, (void *)&state, s_CF_param_items); + if (code >= 0 && + (state.K < -cf_max_height || state.K > cf_max_height || + state.Columns < 0 || state.Columns > cfe_max_width || + state.Rows < 0 || state.Rows > cf_max_height || + state.DamagedRowsBeforeError < 0 || + state.DamagedRowsBeforeError > cf_max_height || + state.DecodedByteAlign < 1 || state.DecodedByteAlign > 16 || + (state.DecodedByteAlign & (state.DecodedByteAlign - 1)) != 0) + ) + code = gs_note_error(gs_error_rangecheck); + if (code >= 0) + *ss = state; + return code; +} diff --git a/gs/src/sdcparam.c b/gs/src/sdcparam.c new file mode 100644 index 000000000..183e0a59e --- /dev/null +++ b/gs/src/sdcparam.c @@ -0,0 +1,619 @@ +/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: sdcparam.c */ +/* DCT filter parameter setting and reading */ +#include "memory_.h" +#include "jpeglib.h" +#include "gserror.h" +#include "gserrors.h" +#include "gstypes.h" +#include "gsmemory.h" +#include "gsparam.h" +#include "strimpl.h" /* sdct.h requires this */ +#include "sdct.h" +#include "sdcparam.h" +#include "sjpeg.h" + +/* Define the DCT parameters. */ +#define dctp(key, type, stype, memb) { key, type, offset_of(stype, memb) } +private const gs_param_item_t s_DCT_param_items[] = +{ +dctp("ColorTransform", gs_param_type_int, stream_DCT_state, ColorTransform), + dctp("QFactor", gs_param_type_float, stream_DCT_state, QFactor), + gs_param_item_end +}; +private const gs_param_item_t jsd_param_items[] = +{ + dctp("Picky", gs_param_type_int, jpeg_stream_data, Picky), + dctp("Relax", gs_param_type_int, jpeg_stream_data, Relax), + gs_param_item_end +}; + +#undef dctp + +/* + * Adobe specifies the values to be supplied in zigzag order. + * For IJG versions newer than v6, we need to convert this order + * to natural array order. Older IJG versions want zigzag order. + */ +#if JPEG_LIB_VERSION >= 61 + /* natural array position of n'th element of JPEG zigzag order */ +static const byte natural_order[DCTSIZE2] = +{ + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63 +}; + +#define jpeg_order(x) natural_order[x] + /* invert natural_order for getting parameters */ +static const byte inverse_natural_order[DCTSIZE2] = +{ + 0, 1, 5, 6, 14, 15, 27, 28, + 2, 4, 7, 13, 16, 26, 29, 42, + 3, 8, 12, 17, 25, 30, 41, 43, + 9, 11, 18, 24, 31, 40, 44, 53, + 10, 19, 23, 32, 39, 45, 52, 54, + 20, 22, 33, 38, 46, 51, 55, 60, + 21, 34, 37, 47, 50, 56, 59, 61, + 35, 36, 48, 49, 57, 58, 62, 63 +}; + +#define jpeg_inverse_order(x) inverse_natural_order[x] +#else +#define jpeg_order(x) (x) +#define jpeg_inverse_order(x) (x) +#endif + +/* ================ Get parameters ================ */ + +private int +quant_param_string(gs_param_string * pstr, int count, const UINT16 * pvals, + floatp QFactor, gs_memory_t * mem) +{ + byte *data; + int code = 0; + int i; + + data = gs_alloc_string(mem, count, "quant_param_string"); + if (data == 0) + return_error(gs_error_VMerror); + for (i = 0; i < count; ++i) { + floatp val = pvals[jpeg_inverse_order(i)] / QFactor; + + data[i] = + (val < 1 ? (code = 1) : val > 255 ? (code = 255) : (byte) val); + } + pstr->data = data; + pstr->size = count; + pstr->persistent = true; + return code & 1; +} + +private int +quant_param_array(gs_param_float_array * pfa, int count, const UINT16 * pvals, + floatp QFactor, gs_memory_t * mem) +{ + float *data; + int i; + + data = (float *)gs_alloc_byte_array(mem, count, sizeof(float), + "quant_param_array"); + + if (data == 0) + return_error(gs_error_VMerror); + for (i = 0; i < count; ++i) + data[i] = pvals[jpeg_inverse_order(i)] / QFactor; + pfa->data = data; + pfa->size = count; + pfa->persistent = true; + return 0; +} + +int +s_DCT_get_quantization_tables(gs_param_list * plist, + const stream_DCT_state * pdct, const stream_DCT_state * defaults, + bool is_encode) +{ + gs_memory_t *mem = pdct->memory; + jpeg_component_info d_comp_info[4]; + int num_in_tables; + const jpeg_component_info *comp_info; + const jpeg_component_info *default_comp_info; + JQUANT_TBL **table_ptrs; + JQUANT_TBL **default_table_ptrs; + gs_param_array quant_tables; + floatp QFactor = pdct->QFactor; + int i; + int code; + + if (is_encode) { + num_in_tables = pdct->data.compress->cinfo.num_components; + comp_info = pdct->data.compress->cinfo.comp_info; + table_ptrs = pdct->data.compress->cinfo.quant_tbl_ptrs; + if (defaults) { + default_comp_info = defaults->data.compress->cinfo.comp_info; + default_table_ptrs = defaults->data.compress->cinfo.quant_tbl_ptrs; + } + } else { + num_in_tables = quant_tables.size; + for (i = 0; i < num_in_tables; ++i) + d_comp_info[i].quant_tbl_no = i; + comp_info = d_comp_info; + table_ptrs = pdct->data.decompress->dinfo.quant_tbl_ptrs; + if (defaults) { + default_comp_info = d_comp_info; + default_table_ptrs = + defaults->data.decompress->dinfo.quant_tbl_ptrs; + } + } + + /* Check whether all tables match defaults. */ + if (defaults) { + bool match = true; + + for (i = 0; i < num_in_tables; ++i) { + JQUANT_TBL *tbl = table_ptrs[comp_info[i].quant_tbl_no]; + JQUANT_TBL *default_tbl = + (default_comp_info == 0 || default_table_ptrs == 0 ? 0 : + default_table_ptrs[default_comp_info[i].quant_tbl_no]); + + if (tbl == default_tbl) + continue; + if (tbl == 0 || default_tbl == 0 || + memcmp(tbl->quantval, default_tbl->quantval, + DCTSIZE2 * sizeof(UINT16)) + ) { + match = false; + break; + } + } + if (match) + return 0; + } + quant_tables.size = num_in_tables; + code = param_begin_write_collection(plist, "QuantTables", + &quant_tables, + gs_param_collection_array); + if (code < 0) + return code; + for (i = 0; i < num_in_tables; ++i) { + char key[3]; + gs_param_string str; + gs_param_float_array fa; + + sprintf(key, "%d", i); + if (QFactor == 1.0) { + code = quant_param_string(&str, DCTSIZE2, + table_ptrs[comp_info[i].quant_tbl_no]->quantval, + QFactor, mem); + switch (code) { + case 0: + code = param_write_string(quant_tables.list, key, &str); + if (code < 0) + return code; /* should dealloc */ + continue; + default: + return code; /* should dealloc */ + case 1: + break; + } + /* break const to free the string */ + gs_free_string(mem, (byte *) str.data, str.size, + "quant_param_string"); + } + code = quant_param_array(&fa, DCTSIZE2, + table_ptrs[comp_info[i].quant_tbl_no]->quantval, + QFactor, mem); + if (code < 0) + return code; /* should dealloc */ + code = param_write_float_array(quant_tables.list, key, &fa); + if (code < 0) + return code; /* should dealloc */ + } + return param_end_write_dict(plist, "QuantTables", &quant_tables); +} + +private int +pack_huff_table(gs_param_string * pstr, const JHUFF_TBL * table, + gs_memory_t * mem) +{ + int total; + int i; + byte *data; + + for (i = 1, total = 0; i <= 16; ++i) + total += table->bits[i]; + data = gs_alloc_string(mem, 16 + total, "pack_huff_table"); + if (data == 0) + return_error(gs_error_VMerror); + memcpy(data, table->bits + 1, 16); + memcpy(data + 16, table->huffval, total); + pstr->data = data; + pstr->size = 16 + total; + pstr->persistent = true; + return 0; +} + +int +s_DCT_get_huffman_tables(gs_param_list * plist, + const stream_DCT_state * pdct, const stream_DCT_state * defaults, + bool is_encode) +{ + gs_memory_t *mem = pdct->memory; + gs_param_string *huff_data; + gs_param_string_array hta; + int num_in_tables; + jpeg_component_info *comp_info; + JHUFF_TBL **dc_table_ptrs; + JHUFF_TBL **ac_table_ptrs; + int i; + int code = 0; + + if (is_encode) { + dc_table_ptrs = pdct->data.compress->cinfo.dc_huff_tbl_ptrs; + ac_table_ptrs = pdct->data.compress->cinfo.ac_huff_tbl_ptrs; + num_in_tables = pdct->data.compress->cinfo.input_components * 2; + comp_info = pdct->data.compress->cinfo.comp_info; + } else { + dc_table_ptrs = pdct->data.decompress->dinfo.dc_huff_tbl_ptrs; + ac_table_ptrs = pdct->data.decompress->dinfo.ac_huff_tbl_ptrs; + for (i = 2; i > 0; --i) + if (dc_table_ptrs[i - 1] || ac_table_ptrs[i - 1]) + break; + num_in_tables = i * 2; + comp_info = NULL; /* do not set for decompress case */ + } +/****** byte_array IS WRONG ******/ + huff_data = (gs_param_string *) + gs_alloc_byte_array(mem, num_in_tables, sizeof(gs_param_string), + "get huffman tables"); + if (huff_data == 0) + return_error(gs_error_VMerror); + for (i = 0; i < num_in_tables; i += 2) { + if ((code = pack_huff_table(huff_data + i, ac_table_ptrs[i >> 1], mem)) < 0 || + (code = pack_huff_table(huff_data + i + 1, dc_table_ptrs[i >> 1], mem)) + ) + break; + } + if (code < 0) + return code; + hta.data = huff_data; + hta.size = num_in_tables; + hta.persistent = true; + return param_write_string_array(plist, "HuffTables", &hta); +} + +int +s_DCT_get_params(gs_param_list * plist, const stream_DCT_state * ss, + const stream_DCT_state * defaults) +{ + int code = + gs_param_write_items(plist, ss, defaults, s_DCT_param_items); + + if (code >= 0) + code = gs_param_write_items(plist, ss->data.common, + (defaults ? defaults->data.common : + NULL), + jsd_param_items); + return code; +} + +/* ================ Put parameters ================ */ + +stream_state_proc_put_params(s_DCT_put_params, stream_DCT_state); /* check */ + +/* ---------------- Utilities ---------------- */ + +/* + * Get N byte-size values from an array or a string. + * Used for HuffTables, HSamples, VSamples. + */ +int +s_DCT_byte_params(gs_param_list * plist, gs_param_name key, int start, + int count, UINT8 * pvals) +{ + int i; + gs_param_string bytes; + gs_param_float_array floats; + int code = param_read_string(plist, key, &bytes); + + switch (code) { + case 0: + if (bytes.size < start + count) { + code = gs_note_error(gs_error_rangecheck); + break; + } + for (i = 0; i < count; ++i) + pvals[i] = (UINT8) bytes.data[start + i]; + return 0; + default: /* might be a float array */ + code = param_read_float_array(plist, key, &floats); + if (!code) { + if (floats.size < start + count) { + code = gs_note_error(gs_error_rangecheck); + break; + } + for (i = 0; i < count; ++i) { + float v = floats.data[start + i]; + + if (v < 0 || v > 255) { + code = gs_note_error(gs_error_rangecheck); + break; + } + pvals[i] = (UINT8) (v + 0.5); + } + } + } + if (code < 0) + param_signal_error(plist, key, code); + return code; +} + +/* Get N quantization values from an array or a string. */ +private int +quant_params(gs_param_list * plist, gs_param_name key, int count, + UINT16 * pvals, floatp QFactor) +{ + int i; + gs_param_string bytes; + gs_param_float_array floats; + int code = param_read_string(plist, key, &bytes); + + switch (code) { + case 0: + if (bytes.size != count) { + code = gs_note_error(gs_error_rangecheck); + break; + } + for (i = 0; i < count; ++i) { + double v = bytes.data[i] * QFactor; + + pvals[jpeg_order(i)] = + (UINT16) (v < 1 ? 1 : v > 255 ? 255 : v + 0.5); + } + return 0; + default: /* might be a float array */ + code = param_read_float_array(plist, key, &floats); + if (!code) { + if (floats.size != count) { + code = gs_note_error(gs_error_rangecheck); + break; + } + for (i = 0; i < count; ++i) { + double v = floats.data[i] * QFactor; + + pvals[jpeg_order(i)] = + (UINT16) (v < 1 ? 1 : v > 255 ? 255 : v + 0.5); + } + } + } + if (code < 0) + param_signal_error(plist, key, code); + return code; +#undef jpeg_order +} + +/* ---------------- Main procedures ---------------- */ + +/* Put common scalars. */ +int +s_DCT_put_params(gs_param_list * plist, stream_DCT_state * pdct) +{ + int code = + gs_param_read_items(plist, pdct, s_DCT_param_items); + + if (code < 0) + return code; + code = gs_param_read_items(plist, pdct->data.common, jsd_param_items); + if (code < 0) + return code; + if (pdct->data.common->Picky < 0 || pdct->data.common->Picky > 1 || + pdct->data.common->Relax < 0 || pdct->data.common->Relax > 1 || + pdct->ColorTransform < -1 || pdct->ColorTransform > 2 || + pdct->QFactor < 0.0 || pdct->QFactor > 1000000.0 + ) + return_error(gs_error_rangecheck); + return 0; +} + +/* Put quantization tables. */ +int +s_DCT_put_quantization_tables(gs_param_list * plist, stream_DCT_state * pdct, + bool is_encode) +{ + int code; + int i, j; + gs_param_array quant_tables; /* array of strings/arrays */ + int num_in_tables; + int num_out_tables; + jpeg_component_info *comp_info; + JQUANT_TBL **table_ptrs; + JQUANT_TBL *this_table; + + switch ((code = param_begin_read_dict(plist, "QuantTables", + &quant_tables, true)) + ) { + case 1: + return 0; + default: + return param_signal_error(plist, "QuantTables", code); + case 0: + ; + } + if (is_encode) { + num_in_tables = pdct->data.compress->cinfo.num_components; + if (quant_tables.size < num_in_tables) + return_error(gs_error_rangecheck); + comp_info = pdct->data.compress->cinfo.comp_info; + table_ptrs = pdct->data.compress->cinfo.quant_tbl_ptrs; + } else { + num_in_tables = quant_tables.size; + comp_info = NULL; /* do not set for decompress case */ + table_ptrs = pdct->data.decompress->dinfo.quant_tbl_ptrs; + } + num_out_tables = 0; + for (i = 0; i < num_in_tables; ++i) { + char istr[5]; /* i converted to string key */ + UINT16 values[DCTSIZE2]; + + sprintf(istr, "%d", i); + code = quant_params(quant_tables.list, istr, DCTSIZE2, values, + pdct->QFactor); + if (code < 0) + return code; + /* Check for duplicate tables. */ + for (j = 0; j < num_out_tables; j++) { + if (!memcmp(table_ptrs[j]->quantval, values, sizeof(values))) + break; + } + if (comp_info != NULL) + comp_info[i].quant_tbl_no = j; + if (j < num_out_tables) /* found a duplicate */ + continue; + if (++num_out_tables > NUM_QUANT_TBLS) + return_error(gs_error_rangecheck); + this_table = table_ptrs[j]; + if (this_table == NULL) { + this_table = gs_jpeg_alloc_quant_table(pdct); + if (this_table == NULL) + return_error(gs_error_VMerror); + table_ptrs[j] = this_table; + } + memcpy(this_table->quantval, values, sizeof(values)); + } + return 0; +} + +/* Put Huffman tables. */ +private int +find_huff_values(JHUFF_TBL ** table_ptrs, int num_tables, + const UINT8 counts[16], const UINT8 * values, int codes_size) +{ + int j; + + for (j = 0; j < num_tables; ++j) + if (!memcmp(table_ptrs[j]->bits, counts, sizeof(counts)) && + !memcmp(table_ptrs[j]->huffval, values, + codes_size * sizeof(values[0]))) + break; + return j; +} +int +s_DCT_put_huffman_tables(gs_param_list * plist, stream_DCT_state * pdct, + bool is_encode) +{ + int code; + int i, j; + gs_param_array huff_tables; + int num_in_tables; + int ndc, nac; + int codes_size; + jpeg_component_info *comp_info; + JHUFF_TBL **dc_table_ptrs; + JHUFF_TBL **ac_table_ptrs; + JHUFF_TBL **this_table_ptr; + JHUFF_TBL *this_table; + int max_tables = 2; /* baseline limit */ + + switch ((code = param_begin_read_dict(plist, "HuffTables", + &huff_tables, true)) + ) { + case 1: + return 0; + default: + return param_signal_error(plist, "HuffTables", code); + case 0: + ; + } + if (is_encode) { + num_in_tables = pdct->data.compress->cinfo.input_components * 2; + if (huff_tables.size < num_in_tables) + return_error(gs_error_rangecheck); + comp_info = pdct->data.compress->cinfo.comp_info; + dc_table_ptrs = pdct->data.compress->cinfo.dc_huff_tbl_ptrs; + ac_table_ptrs = pdct->data.compress->cinfo.ac_huff_tbl_ptrs; + if (pdct->data.common->Relax) + max_tables = max(pdct->data.compress->cinfo.input_components, 2); + } else { + num_in_tables = huff_tables.size; + comp_info = NULL; /* do not set for decompress case */ + dc_table_ptrs = pdct->data.decompress->dinfo.dc_huff_tbl_ptrs; + ac_table_ptrs = pdct->data.decompress->dinfo.ac_huff_tbl_ptrs; + if (pdct->data.common->Relax) + max_tables = NUM_HUFF_TBLS; + } + ndc = nac = 0; + for (i = 0; i < num_in_tables; ++i) { + char istr[5]; /* i converted to string key */ + UINT8 counts[16], values[256]; + + /* Collect the Huffman parameters. */ + sprintf(istr, "%d", i); + code = s_DCT_byte_params(huff_tables.list, istr, 0, 16, counts); + if (code < 0) + return code; + for (codes_size = 0, j = 0; j < 16; j++) + codes_size += counts[j]; + if (codes_size > 256 /*|| r_size(pa) != codes_size+16 */ ) + return_error(gs_error_rangecheck); + code = s_DCT_byte_params(huff_tables.list, istr, 16, codes_size, + values); + if (code < 0) + return code; + if (i & 1) { + j = find_huff_values(ac_table_ptrs, nac, counts, values, + codes_size); + if (comp_info != NULL) + comp_info[i >> 1].ac_tbl_no = j; + if (j < nac) + continue; + if (++nac > NUM_HUFF_TBLS) + return_error(gs_error_rangecheck); + this_table_ptr = ac_table_ptrs + j; + } else { + j = find_huff_values(dc_table_ptrs, ndc, counts, values, + codes_size); + if (comp_info != NULL) + comp_info[i >> 1].dc_tbl_no = j; + if (j < ndc) + continue; + if (++ndc > NUM_HUFF_TBLS) + return_error(gs_error_rangecheck); + this_table_ptr = dc_table_ptrs + j; + } + this_table = *this_table_ptr; + if (this_table == NULL) { + this_table = gs_jpeg_alloc_huff_table(pdct); + if (this_table == NULL) + return_error(gs_error_VMerror); + *this_table_ptr = this_table; + } + memcpy(this_table->bits, counts, sizeof(counts)); + memcpy(this_table->huffval, values, codes_size * sizeof(values[0])); + } + if (nac > max_tables || ndc > max_tables) + return_error(gs_error_rangecheck); + return 0; +} diff --git a/gs/src/sdcparam.h b/gs/src/sdcparam.h new file mode 100644 index 000000000..aa5501dfb --- /dev/null +++ b/gs/src/sdcparam.h @@ -0,0 +1,51 @@ +/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: sdcparam.h */ +/* DCT filter parameter setting and reading interface */ + +#ifndef sdcparam_INCLUDED +# define sdcparam_INCLUDED + +/* + * All of these procedures are defined in sdcparam.c and are only for + * internal use (by sddparam.c and sdeparam.c), so they are not + * documented here. + */ + +int s_DCT_get_params(P3(gs_param_list * plist, const stream_DCT_state * ss, + const stream_DCT_state * defaults)); +int s_DCT_get_quantization_tables(P4(gs_param_list * plist, + const stream_DCT_state * pdct, + const stream_DCT_state * defaults, + bool is_encode)); +int s_DCT_get_huffman_tables(P4(gs_param_list * plist, + const stream_DCT_state * pdct, + const stream_DCT_state * defaults, + bool is_encode)); + +int s_DCT_byte_params(P5(gs_param_list * plist, gs_param_name key, int start, + int count, UINT8 * pvals)); +int s_DCT_put_params(P2(gs_param_list * plist, stream_DCT_state * pdct)); +int s_DCT_put_quantization_tables(P3(gs_param_list * plist, + stream_DCT_state * pdct, + bool is_encode)); +int s_DCT_put_huffman_tables(P3(gs_param_list * plist, stream_DCT_state * pdct, + bool is_encode)); + +#endif /* sdcparam_INCLUDED */ diff --git a/gs/src/sddparam.c b/gs/src/sddparam.c new file mode 100644 index 000000000..0d7ccf294 --- /dev/null +++ b/gs/src/sddparam.c @@ -0,0 +1,72 @@ +/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: sddparam.c */ +/* DCTDecode filter parameter setting and reading */ +#include "std.h" +#include "jpeglib.h" +#include "gserror.h" +#include "gserrors.h" +#include "gstypes.h" +#include "gsmemory.h" +#include "gsparam.h" +#include "strimpl.h" /* sdct.h requires this */ +#include "sdct.h" +#include "sdcparam.h" +#include "sjpeg.h" + +/* ================ Get parameters ================ */ + +stream_state_proc_get_params(s_DCTD_get_params, stream_DCT_state); /* check */ + +int +s_DCTD_get_params(gs_param_list * plist, const stream_DCT_state * ss, bool all) +{ + stream_DCT_state dcts_defaults; + const stream_DCT_state *defaults; + + if (all) + defaults = 0; + else { + (*s_DCTE_template.set_defaults) ((stream_state *) & dcts_defaults); + defaults = &dcts_defaults; + } +/****** NYI ******/ + return s_DCT_get_params(plist, ss, defaults); +} + +/* ================ Put parameters ================ */ + +stream_state_proc_put_params(s_DCTD_put_params, stream_DCT_state); /* check */ + +int +s_DCTD_put_params(gs_param_list * plist, stream_DCT_state * pdct) +{ + int code; + + if ((code = s_DCT_put_params(plist, pdct)) < 0 || + /* + * DCTDecode accepts quantization and huffman tables + * in case these tables have been omitted from the datastream. + */ + (code = s_DCT_put_huffman_tables(plist, pdct, false)) < 0 || + (code = s_DCT_put_quantization_tables(plist, pdct, false)) < 0 + ) + DO_NOTHING; + return code; +} diff --git a/gs/src/sdeparam.c b/gs/src/sdeparam.c new file mode 100644 index 000000000..fae6b1610 --- /dev/null +++ b/gs/src/sdeparam.c @@ -0,0 +1,317 @@ +/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: sdeparam.c */ +/* DCTEncode filter parameter setting and reading */ +#include "memory_.h" +#include "jpeglib.h" +#include "gserror.h" +#include "gserrors.h" +#include "gstypes.h" +#include "gsmemory.h" +#include "gsparam.h" +#include "strimpl.h" /* sdct.h requires this */ +#include "sdct.h" +#include "sdcparam.h" +#include "sjpeg.h" + +/* Define a structure for the DCTEncode scalar parameters. */ +typedef struct dcte_scalars_s { + int Columns; + int Rows; + int Colors; + gs_param_string Markers; + bool NoMarker; + int Resync; + int Blend; +} dcte_scalars_t; +private const dcte_scalars_t dcte_scalars_default = +{ + 0, 0, -1, + {0, 0}, 0 /*false */ , 0, 0 +}; +private const gs_param_item_t s_DCTE_param_items[] = +{ +#define dctp(key, type, memb) { key, type, offset_of(dcte_scalars_t, memb) } + dctp("Columns", gs_param_type_int, Columns), + dctp("Rows", gs_param_type_int, Rows), + dctp("Colors", gs_param_type_int, Colors), + dctp("Marker", gs_param_type_string, Markers), + dctp("NoMarker", gs_param_type_bool, NoMarker), + dctp("Resync", gs_param_type_int, Resync), + dctp("Blend", gs_param_type_int, Blend), +#undef dctp + gs_param_item_end +}; + +/* ================ Get parameters ================ */ + +stream_state_proc_get_params(s_DCTE_get_params, stream_DCT_state); /* check */ + +/* Get a set of sampling values. */ +private int +dcte_get_samples(gs_param_list * plist, gs_param_name key, int num_colors, + const jpeg_compress_data * jcdp, gs_memory_t * mem, bool is_vert, bool all) +{ + const jpeg_component_info *comp_info = jcdp->cinfo.comp_info; + int samples[4]; + bool write = all; + int i; + + for (i = 0; i < num_colors; ++i) + write |= (samples[i] = (is_vert ? comp_info[i].v_samp_factor : + comp_info[i].h_samp_factor)) != 1; + if (write) { + int *data = (int *)gs_alloc_byte_array(mem, num_colors, sizeof(int), + "dcte_get_samples"); + gs_param_int_array sa; + + if (data == 0) + return_error(gs_error_VMerror); + sa.data = data; + sa.size = num_colors; + sa.persistent = true; + memcpy(data, samples, num_colors * sizeof(samples[0])); + return param_write_int_array(plist, key, &sa); + } + return 0; +} + +int +s_DCTE_get_params(gs_param_list * plist, const stream_DCT_state * ss, bool all) +{ + gs_memory_t *mem = ss->memory; + stream_DCT_state dcts_defaults; + const stream_DCT_state *defaults = 0; + dcte_scalars_t params; + const jpeg_compress_data *jcdp = ss->data.compress; + int code; + + if (!all) { + jpeg_compress_data *jcdp_default = + (jpeg_compress_data *) + gs_alloc_bytes_immovable(mem, sizeof(*jcdp), "s_DCTE_get_params"); + + if (jcdp_default == 0) + return_error(gs_error_VMerror); + defaults = &dcts_defaults; + (*s_DCTE_template.set_defaults) ((stream_state *) & dcts_defaults); + dcts_defaults.data.compress = jcdp_default; + jcdp_default->memory = dcts_defaults.jpeg_memory = mem; + if ((code = gs_jpeg_create_compress(&dcts_defaults)) < 0) + goto fail; /* correct to do jpeg_destroy here */ +/****** SET DEFAULTS HERE ******/ + dcts_defaults.data.common->Picky = 0; + dcts_defaults.data.common->Relax = 0; + } + params.Columns = jcdp->cinfo.image_width; + params.Rows = jcdp->cinfo.image_height; + params.Colors = jcdp->cinfo.input_components; + params.Markers.data = ss->Markers.data; + params.Markers.size = ss->Markers.size; + params.Markers.persistent = false; + params.NoMarker = ss->NoMarker; + params.Resync = jcdp->cinfo.restart_interval; + /* What about Blend?? */ + if ((code = s_DCT_get_params(plist, ss, defaults)) < 0 || + (code = gs_param_write_items(plist, ¶ms, + &dcte_scalars_default, + s_DCTE_param_items)) < 0 || + (code = dcte_get_samples(plist, "HSamples", params.Colors, + jcdp, mem, false, all)) < 0 || + (code = dcte_get_samples(plist, "VSamples", params.Colors, + jcdp, mem, true, all)) < 0 || + (code = s_DCT_get_quantization_tables(plist, ss, defaults, true)) < 0 || + (code = s_DCT_get_huffman_tables(plist, ss, defaults, true)) < 0 + ) + DO_NOTHING; +/****** NYI ******/ + fail:if (defaults) { + gs_jpeg_destroy(&dcts_defaults); + gs_free_object(mem, dcts_defaults.data.compress, + "s_DCTE_get_params"); + } + return code; +} + +/* ================ Put parameters ================ */ + +stream_state_proc_put_params(s_DCTE_put_params, stream_DCT_state); /* check */ + +/* Put a set of sampling values. */ +private int +dcte_put_samples(gs_param_list * plist, gs_param_name key, int num_colors, + jpeg_compress_data * jcdp, bool is_vert) +{ + int code; + int i; + jpeg_component_info *comp_info = jcdp->cinfo.comp_info; + UINT8 samples[4]; + + /* + * Adobe default is all sampling factors = 1, + * which is NOT the IJG default, so we must always assign values. + */ + switch ((code = s_DCT_byte_params(plist, key, 0, num_colors, + samples)) + ) { + default: /* error */ + return code; + case 0: + break; + case 1: + samples[0] = samples[1] = samples[2] = samples[3] = 1; + } + for (i = 0; i < num_colors; i++) { + if (samples[i] < 1 || samples[i] > 4) + return_error(gs_error_rangecheck); + if (is_vert) + comp_info[i].v_samp_factor = samples[i]; + else + comp_info[i].h_samp_factor = samples[i]; + } + return 0; +} + +/* Main procedure */ +int +s_DCTE_put_params(gs_param_list * plist, stream_DCT_state * pdct) +{ + jpeg_compress_data *jcdp = pdct->data.compress; + dcte_scalars_t params; + int i; + int code; + + params = dcte_scalars_default; + /* + * Required parameters for DCTEncode. + * (DCTDecode gets the equivalent info from the SOF marker.) + */ + code = gs_param_read_items(plist, ¶ms, s_DCTE_param_items); + if (code < 0) + return code; + if (params.Columns <= 0 || params.Columns > 0xffff || + params.Rows <= 0 || params.Rows > 0xffff || + params.Colors <= 0 || params.Colors == 2 || params.Colors > 4 || + params.Resync < 0 || params.Resync > 0xffff || + params.Blend < 0 || params.Blend > 1 + ) + return_error(gs_error_rangecheck); +/****** HACK: SET DEFAULTS HERE ******/ + jcdp->Picky = 0; + jcdp->Relax = 0; + if ((code = s_DCT_put_params(plist, pdct)) < 0 || + (code = s_DCT_put_huffman_tables(plist, pdct, false)) < 0 + ) + return code; + switch ((code = s_DCT_put_quantization_tables(plist, pdct, false))) { + case 0: + break; + default: + return code; + case 1: + /* No QuantTables, but maybe a QFactor to apply to default. */ + if (pdct->QFactor != 1.0) { + code = gs_jpeg_set_linear_quality(pdct, + (int)(min(pdct->QFactor, 100.0) + * 100.0 + 0.5), + TRUE); + if (code < 0) + return code; + } + } + /* Set up minimal image description & call set_defaults */ + jcdp->cinfo.image_width = params.Columns; + jcdp->cinfo.image_height = params.Rows; + jcdp->cinfo.input_components = params.Colors; + switch (params.Colors) { + case 1: + jcdp->cinfo.in_color_space = JCS_GRAYSCALE; + break; + case 3: + jcdp->cinfo.in_color_space = JCS_RGB; + break; + case 4: + jcdp->cinfo.in_color_space = JCS_CMYK; + break; + default: + jcdp->cinfo.in_color_space = JCS_UNKNOWN; + } + if ((code = gs_jpeg_set_defaults(pdct)) < 0) + return code; + /* Change IJG colorspace defaults as needed; + * set ColorTransform to what will go in the Adobe marker. + */ + switch (params.Colors) { + case 3: + if (pdct->ColorTransform < 0) + pdct->ColorTransform = 1; /* default */ + if (pdct->ColorTransform == 0) { + if ((code = gs_jpeg_set_colorspace(pdct, JCS_RGB)) < 0) + return code; + } else + pdct->ColorTransform = 1; /* flag YCC xform */ + break; + case 4: + if (pdct->ColorTransform < 0) + pdct->ColorTransform = 0; /* default */ + if (pdct->ColorTransform != 0) { + if ((code = gs_jpeg_set_colorspace(pdct, JCS_YCCK)) < 0) + return code; + pdct->ColorTransform = 2; /* flag YCCK xform */ + } else { + if ((code = gs_jpeg_set_colorspace(pdct, JCS_CMYK)) < 0) + return code; + } + break; + default: + pdct->ColorTransform = 0; /* no transform otherwise */ + break; + } + /* Optional encoding-only parameters */ + pdct->Markers.data = params.Markers.data; + pdct->Markers.size = params.Markers.size; + pdct->NoMarker = params.NoMarker; + if ((code = dcte_put_samples(plist, "HSamples", params.Colors, + jcdp, false)) < 0 || + (code = dcte_put_samples(plist, "VSamples", params.Colors, + jcdp, true)) < 0 + ) + return code; + jcdp->cinfo.write_JFIF_header = FALSE; + jcdp->cinfo.write_Adobe_marker = FALSE; /* must do it myself */ + jcdp->cinfo.restart_interval = params.Resync; + /* What to do with Blend ??? */ + if (pdct->data.common->Relax == 0) { + jpeg_component_info *comp_info = jcdp->cinfo.comp_info; + int num_samples; + + for (i = 0, num_samples = 0; i < params.Colors; i++) + num_samples += comp_info[i].h_samp_factor * + comp_info[i].v_samp_factor; + if (num_samples > 10) + return_error(gs_error_rangecheck); + /* + * Note: by default the IJG software does not allow + * num_samples to exceed 10, Relax or no. For full + * compatibility with Adobe's non-JPEG-compliant + * software, set MAX_BLOCKS_IN_MCU to 64 in jpeglib.h. + */ + } + return 0; +} diff --git a/gs/src/unixinst.mak b/gs/src/unixinst.mak new file mode 100644 index 000000000..2ba26d1e7 --- /dev/null +++ b/gs/src/unixinst.mak @@ -0,0 +1,102 @@ +# Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved. +# +# This file is part of Aladdin Ghostscript. +# +# Aladdin 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 Ghostscript Free Public +# License (the "License") for full details. +# +# Every copy of Aladdin 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 Aladdin 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: unixinst.mak +# Partial makefile common to all Unix and Desqview/X configurations, +# containing the `install' targets. +# This is the very last part of the makefile for these configurations. + +install: install-exec install-scripts install-data + +# The sh -c in the rules below is required because Ultrix's implementation +# of sh -e terminates execution of a command if any error occurs, even if +# the command traps the error with ||. + +# We include mkdirs for datadir, gsdir, and gsdatadir in all 3 install +# rules, just in case bindir or scriptdir is a subdirectory of any of these. + +install-exec: $(GS_XE) + -mkdir $(datadir) + -mkdir $(gsdir) + -mkdir $(gsdatadir) + -mkdir $(bindir) + $(INSTALL_PROGRAM) $(GS_XE) $(bindir)/$(GS) + +install-scripts: gsnd + -mkdir $(datadir) + -mkdir $(gsdir) + -mkdir $(gsdatadir) + -mkdir $(scriptdir) + sh -c 'for f in \ +gsbj gsdj gsdj500 gslj gslp gsnd \ +bdftops dvipdf font2c \ +pdf2dsc pdf2ps pf2afm printafm ps2ascii ps2epsi ps2pdf ps2ps wftopfa ;\ + do if ( test -f $$f ); then $(INSTALL_PROGRAM) $$f $(scriptdir)/$$f; fi;\ + done' + +MAN1_PAGES=gs pdf2dsc pdf2ps ps2ascii ps2epsi ps2pdf ps2ps +install-data: gs.1 + -mkdir $(mandir) + -mkdir $(man1dir) + sh -c 'for f in $(MAN1_PAGES) ;\ + do if ( test -f $$f.1 ); then $(INSTALL_DATA) $$f.1 $(man1dir)/$$f.$(man1ext); fi;\ + done' + -mkdir $(datadir) + -mkdir $(gsdir) + -mkdir $(gsdatadir) + sh -c 'for f in \ +Fontmap \ +cbjc600.ppd cbjc800.ppd *.upp \ +gs_init.ps gs_btokn.ps gs_ccfnt.ps gs_cff.ps gs_cidfn.ps gs_cmap.ps \ +gs_diskf.ps gs_dpnxt.ps gs_dps.ps gs_dps1.ps gs_dps2.ps gs_epsf.ps \ +gs_fonts.ps gs_kanji.ps gs_lev2.ps gs_ll3.ps \ +gs_pfile.ps gs_res.ps gs_setpd.ps gs_statd.ps \ +gs_ttf.ps gs_typ32.ps gs_typ42.ps gs_type1.ps \ +gs_dbt_e.ps gs_iso_e.ps gs_ksb_e.ps gs_std_e.ps gs_sym_e.ps \ +acctest.ps align.ps bdftops.ps caption.ps decrypt.ps docie.ps \ +font2c.ps font2pcl.ps gslp.ps impath.ps landscap.ps level1.ps lines.ps \ +markhint.ps markpath.ps \ +packfile.ps pcharstr.ps pf2afm.ps ppath.ps prfont.ps printafm.ps \ +ps2ai.ps ps2ascii.ps ps2epsi.ps ps2image.ps \ +quit.ps showchar.ps showpage.ps stcinfo.ps stcolor.ps \ +traceimg.ps traceop.ps type1enc.ps type1ops.ps uninfo.ps unprot.ps \ +viewcmyk.ps viewgif.ps viewjpeg.ps viewpcx.ps viewpbm.ps viewps2a.ps \ +winmaps.ps wftopfa.ps wrfont.ps zeroline.ps \ +gs_l2img.ps \ +pdf2dsc.ps \ +pdf_base.ps pdf_draw.ps pdf_font.ps pdf_main.ps pdf_ops.ps pdf_sec.ps \ +gs_mex_e.ps gs_mro_e.ps gs_pdf_e.ps gs_wan_e.ps \ +gs_pdfwr.ps ;\ + do if ( test -f $$f ); then $(INSTALL_DATA) $$f $(gsdatadir)/$$f; fi;\ + done' + -mkdir $(docdir) + sh -c 'for f in \ +COPYING NEWS PUBLIC README \ +bug-form.txt c-style.txt current.txt devices.txt drivers.txt fonts.txt \ +helpers.txt hershey.txt history1.txt history2.txt history3.txt humor.txt \ +install.txt language.txt lib.txt make.txt new-user.txt \ +ps2epsi.txt ps2pdf.txt psfiles.txt public.txt \ +unix-lpr.txt use.txt xfonts.txt ;\ + do if ( test -f $$f ); then $(INSTALL_DATA) $$f $(docdir)/$$f; fi;\ + done' + -mkdir $(exdir) + for f in \ +alphabet.ps chess.ps cheq.ps colorcir.ps escher.ps golfer.ps \ +grayalph.ps snowflak.ps tiger.ps vasarely.ps waterfal.ps \ +ridt91.eps ;\ + do $(INSTALL_DATA) $$f $(exdir)/$$f ;\ + done diff --git a/gs/src/zcfont.c b/gs/src/zcfont.c new file mode 100644 index 000000000..1ccbca488 --- /dev/null +++ b/gs/src/zcfont.c @@ -0,0 +1,158 @@ +/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: zcfont.c */ +/* Composite font-related character operators */ +#include "ghost.h" +#include "oper.h" +#include "gschar.h" +#include "gsmatrix.h" /* for gxfont.h */ +#include "gxfixed.h" /* for gxfont.h */ +#include "gxfont.h" +#include "gxchar.h" +#include "estack.h" +#include "ichar.h" +#include "ifont.h" +#include "igstate.h" +#include "store.h" + +/* Forward references */ +private int cshow_continue(P1(os_ptr)); +private int cshow_restore_font(P1(os_ptr)); + +/* <proc> <string> cshow - */ +private int +zcshow(os_ptr op) +{ + os_ptr proc_op = op - 1; + os_ptr str_op = op; + gs_show_enum *penum; + int code; + + /* + * Even though this is not documented anywhere by Adobe, + * the Adobe interpreters apparently allow the string and + * the procedure to be provided in either order! + */ + if (r_is_proc(proc_op)) + ; + else if (r_is_proc(op)) { /* operands reversed */ + proc_op = op; + str_op = op - 1; + } else { + check_op(2); + return_error(e_typecheck); + } + if ((code = op_show_setup(str_op, &penum)) != 0) + return code; + if ((code = gs_cshow_n_init(penum, igs, (char *)str_op->value.bytes, + r_size(str_op))) < 0 + ) { + ifree_object(penum, "op_show_enum_setup"); + return code; + } + op_show_finish_setup(penum, 2, NULL); + sslot = *proc_op; /* save kerning proc */ + return cshow_continue(op - 2); +} +private int +cshow_continue(os_ptr op) +{ + gs_show_enum *penum = senum; + int code; + + check_estack(4); /* in case we call the procedure */ + code = gs_show_next(penum); + if (code != gs_show_move) { + code = op_show_continue_dispatch(op, code); + if (code == o_push_estack) /* must be gs_show_render */ + make_op_estack(esp - 1, cshow_continue); + return code; + } + /* Push the character code and width, and call the procedure. */ + { + ref *pslot = &sslot; + gs_point wpt; + gs_font *font = gs_show_current_font(penum); + gs_font *scaled_font; + + gs_show_current_width(penum, &wpt); +#if 0 + /**************** + * The following code is logically correct (or at least, + * probably more correct than the code it replaces), + * but because of the issues about creating the scaled + * font in the correct VM space that make_font (in zfont.c) + * has to deal with, it creates references pointing to + * the wrong spaces. Therefore, we've removed it. + *****************/ + { + int fdepth = penum->fstack.depth; + + if (fdepth <= 0) + scaled_font = font; /* not composite */ + else { + code = gs_makefont(font->dir, font, + &penum->fstack.items[fdepth - 1].font-> + FontMatrix, &scaled_font); + if (code < 0) + return code; + } + } +#else + scaled_font = font; +#endif + push(3); + make_int(op - 2, gs_show_current_char(penum)); + make_real(op - 1, wpt.x); + make_real(op, wpt.y); + push_op_estack(cshow_continue); + if (scaled_font != gs_rootfont(igs)) + push_op_estack(cshow_restore_font); + /* cshow does not change rootfont for user procedure */ + gs_set_currentfont(igs, scaled_font); + *++esp = *pslot; /* user procedure */ + } + return o_push_estack; +} +private int +cshow_restore_font(os_ptr op) +{ /* We have 1 more entry on the e-stack (cshow_continue). */ + return gs_show_restore_font(esenum(esp - 1)); +} + +/* - rootfont <font> */ +private int +zrootfont(os_ptr op) +{ + push(1); + *op = *pfont_dict(gs_rootfont(igs)); + return 0; +} + +/* ------ Initialization procedure ------ */ + +const op_def zcfont_op_defs[] = +{ + {"2cshow", zcshow}, + {"0rootfont", zrootfont}, + /* Internal operators */ + {"0%cshow_continue", cshow_continue}, + {"0%cshow_restore_font", cshow_restore_font}, + op_def_end(0) +}; diff --git a/gs/src/zchar32.c b/gs/src/zchar32.c new file mode 100644 index 000000000..39b310914 --- /dev/null +++ b/gs/src/zchar32.c @@ -0,0 +1,190 @@ +/* Copyright (C) 1997 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: zchar32.c */ +/* Type 32 font glyph operators */ +#include "ghost.h" +#include "oper.h" +#include "gsccode.h" /* for gxfont.h */ +#include "gscoord.h" +#include "gsutil.h" +#include "gxdevice.h" +#include "gxdevmem.h" +#include "gxchar.h" +#include "gxfont.h" +#include "gxfcache.h" +#include "ifont.h" +#include "igstate.h" + +/* ([wx wy llx lly urx ury] | [w0x w0y llx lly urx ury w1x w1y vx vy]) */ +/* <bitmap> <cid> <type32font> addglyph - */ +private floatp +coeff_sign(floatp v) +{ + return (v < 0 ? -1.0 : v > 0 ? 1.0 : 0.0); +} +private int +zaddglyph(os_ptr op) +{ + int num_wmodes = 1; + int int_mask = 0x3c; /* bits for llx .. ury */ + uint msize; + double metrics[10]; + int llx, lly, urx, ury; + int width, height, raster; + gs_font *pfont; + gs_matrix save_mat; + cached_fm_pair *pair; + int wmode; + int code; + + check_array(op[-3]); + msize = r_size(op - 3); + switch (msize) { + case 10: + num_wmodes = 2; + int_mask = 0x33c; /* add bits for vx, vy */ + case 6: + break; + default: + return_error(e_rangecheck); + } + code = num_params(op[-3].value.refs + msize - 1, msize, metrics); + if (code < 0) + return code; + if (~code & int_mask) /* check llx .. ury for integers */ + return_error(e_typecheck); + check_read_type(op[-2], t_string); + llx = (int)metrics[2]; + lly = (int)metrics[3]; + urx = (int)metrics[4]; + ury = (int)metrics[5]; + width = urx - llx; + height = ury - lly; + raster = (width + 7) >> 3; + if (width < 0 || height < 0 || r_size(op - 2) != raster * height) + return_error(e_rangecheck); + check_int_leu(op[-1], 65535); + code = font_param(op, &pfont); + if (code < 0) + return code; + if (pfont->FontType != ft_bitmap) + return_error(e_invalidfont); + /* Set the CTM to the (properly oriented) identity. */ + gs_currentmatrix(igs, &save_mat); + { + gs_matrix init_mat, font_mat; + + gs_defaultmatrix(igs, &init_mat); + font_mat.xx = coeff_sign(init_mat.xx); + font_mat.xy = coeff_sign(init_mat.xy); + font_mat.yx = coeff_sign(init_mat.yx); + font_mat.yy = coeff_sign(init_mat.yy); + font_mat.tx = font_mat.ty = 0; + gs_setmatrix(igs, &font_mat); + } + pair = gx_lookup_fm_pair(pfont, igs); + if (pair == 0) { + gs_setmatrix(igs, &save_mat); + return_error(e_VMerror); + } + /* The APIs for adding characters to the cache are not ideal.... */ + for (wmode = 0; wmode < num_wmodes; ++wmode) { + gx_device_memory mdev; + static const gs_log2_scale_point no_scale = + {0, 0}; + cached_char *cc; + gx_bitmap_id id = gs_next_ids(1); + + mdev.memory = 0; + mdev.target = 0; + cc = gx_alloc_char_bits(pfont->dir, &mdev, NULL, width, height, + &no_scale, 1); + if (cc == 0) { + code = gs_note_error(e_limitcheck); + break; + } + cc->wxy.x = float2fixed(metrics[0]); + cc->wxy.y = float2fixed(metrics[1]); + cc->offset.x = -llx; + cc->offset.y = -lly; + gx_copy_mono_unaligned((gx_device *) & mdev, + op[-2].value.const_bytes, 0, raster, id, + 0, 0, width, height, + (gx_color_index) 0, (gx_color_index) 1); + gx_add_cached_char(pfont->dir, &mdev, cc, pair, &no_scale); + cc->code = gs_min_cid_glyph + op[-1].value.intval; + cc->wmode = wmode; + gx_add_char_bits(pfont->dir, cc, &no_scale); + /* Adjust for WMode = 1. */ + if (num_wmodes > wmode + 1) { + metrics[0] = metrics[6]; + metrics[1] = metrics[7]; + llx -= (int)metrics[8]; + lly -= (int)metrics[9]; + } + } + gs_setmatrix(igs, &save_mat); + if (code >= 0) + pop(4); + return code; +} + +/* <cid_min> <cid_max> <type32font> removeglyphs - */ +typedef struct { + gs_glyph cid_min, cid_max; + gs_font *font; +} font_cid_range_t; +private bool +select_cid_range(cached_char * cc, void *range_ptr) +{ + const font_cid_range_t *range = range_ptr; + + return (cc->code >= range->cid_min && + cc->code <= range->cid_max && + cc->pair->font == range->font); +} +private int +zremoveglyphs(os_ptr op) +{ + int code; + font_cid_range_t range; + + check_int_leu(op[-2], 65535); + check_int_leu(op[-1], 65535); + code = font_param(op, &range.font); + if (code < 0) + return code; + if (range.font->FontType != ft_bitmap) + return_error(e_invalidfont); + range.cid_min = gs_min_cid_glyph + op[-2].value.intval; + range.cid_max = gs_min_cid_glyph + op[-1].value.intval; + gx_purge_selected_cached_chars(range.font->dir, select_cid_range, + &range); + pop(3); + return 0; +} + +/* ------ Initialization procedure ------ */ + +const op_def zchar32_op_defs[] = +{ + {"4addglyph", zaddglyph}, + {"3removeglyphs", zremoveglyphs}, + op_def_end(0) +}; diff --git a/gs/src/zcsdevn.c b/gs/src/zcsdevn.c new file mode 100644 index 000000000..92c406bd2 --- /dev/null +++ b/gs/src/zcsdevn.c @@ -0,0 +1,113 @@ +/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: zcsdevn.c */ +/* DeviceN color space support */ +#include "ghost.h" +#include "oper.h" +#include "gxcspace.h" /* must precede gscolor2.h */ +#include "gscolor2.h" +#include "igstate.h" +#include "ialloc.h" +#include "iname.h" + +/* Imported from gscdevn.c */ +extern const gs_color_space_type gs_color_space_type_DeviceN; + +/* <array> .setdevicepixelspace - */ +/* The current color space is the alternate space for the DeviceN space. */ +private int +zsetdevicenspace(register os_ptr op) +{ + const ref *pcsa; + gs_separation_name *names; + uint num_components; + gs_color_space cs; + ref_colorspace cspace_old; + int code; + + check_read_type(*op, t_array); + if (r_size(op) != 4) + return_error(e_rangecheck); + pcsa = op->value.const_refs + 1; + if (!r_is_array(pcsa)) + return_error(e_typecheck); + num_components = r_size(pcsa); + if (num_components == 0) + return_error(e_rangecheck); + check_proc(pcsa[2]); + cs = *gs_currentcolorspace(igs); + if (!cs.type->can_be_alt_space) + return_error(e_rangecheck); + names = (gs_separation_name *) + ialloc_byte_array(num_components, sizeof(gs_separation_name), + ".setdevicenspace"); + if (names == 0) + return_error(e_VMerror); + { + uint i; + ref sname; + + for (i = 0; i < num_components; ++i) { + array_get(pcsa, (long)i, &sname); + switch (r_type(&sname)) { + case t_string: + code = name_from_string(&sname, &sname); + if (code < 0) { + ifree_object(names, ".setdevicenspace"); + return code; + } + /* falls through */ + case t_name: + names[i] = name_index(&sname); + break; + default: + ifree_object(names, ".setdevicenspace"); + return_error(e_typecheck); + } + } + } + cs.params.device_n.alt_space = *(gs_base_color_space *) & cs; + cspace_old = istate->colorspace; + istate->colorspace.procs.special.device_n.layer_names = pcsa[0]; + istate->colorspace.procs.special.device_n.tint_transform = pcsa[2]; + cs.type = &gs_color_space_type_DeviceN; + cs.params.device_n.names = names; + cs.params.device_n.num_components = num_components; + cs.params.device_n.tint_transform = 0; +/****** ? ******/ + cs.params.device_n.tint_transform_data = 0; +/****** ? ******/ + code = gs_setcolorspace(igs, &cs); + if (code < 0) { + istate->colorspace = cspace_old; + ifree_object(names, ".setdevicenspace"); + return code; + } + pop(1); + return 0; +} + +/* ------ Initialization procedure ------ */ + +const op_def zcsdevn_op_defs[] = +{ + op_def_begin_ll3(), + {"1.setdevicenspace", zsetdevicenspace}, + op_def_end(0) +}; diff --git a/gs/src/zcspixel.c b/gs/src/zcspixel.c new file mode 100644 index 000000000..39ed38fd6 --- /dev/null +++ b/gs/src/zcspixel.c @@ -0,0 +1,67 @@ +/* Copyright (C) 1997 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: zcspixel.c */ +/* DevicePixel color space support */ +#include "ghost.h" +#include "oper.h" +#include "igstate.h" +#include "gscspace.h" +#include "gsmatrix.h" /* for gscolor2.h */ +#include "gscolor2.h" +#include "gscpixel.h" + +/* <array> .setdevicepixelspace - */ +private int +zsetdevicepixelspace(register os_ptr op) +{ + ref depth; + gs_color_space cs; + int code; + + check_read_type(*op, t_array); + if (r_size(op) != 2) + return_error(e_rangecheck); + array_get(op, 1L, &depth); + check_type_only(depth, t_integer); + switch (depth.value.intval) { + case 1: + case 2: + case 4: + case 8: + case 16: + case 24: + case 32: + break; + default: + return_error(e_rangecheck); + } + gs_cs_init_DevicePixel(&cs, (int)depth.value.intval); + code = gs_setcolorspace(igs, &cs); + if (code >= 0) + pop(1); + return code; +} + +/* ------ Initialization procedure ------ */ + +const op_def zcspixel_op_defs[] = +{ + {"1.setdevicepixelspace", zsetdevicepixelspace}, + op_def_end(0) +}; diff --git a/gs/src/zfont32.c b/gs/src/zfont32.c new file mode 100644 index 000000000..6185597d3 --- /dev/null +++ b/gs/src/zfont32.c @@ -0,0 +1,73 @@ +/* Copyright (C) 1997 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: zfont32.c */ +/* Type 32 font operators */ +#include "ghost.h" +#include "oper.h" +#include "gsccode.h" /* for gxfont.h */ +#include "gsmatrix.h" +#include "gsutil.h" +#include "gxfixed.h" /* for gxchar.h */ +#include "gxchar.h" +#include "gxfont.h" +#include "bfont.h" +#include "store.h" + +/* The encode_char procedure of a Type 32 font should never be called. */ +private gs_glyph +zfont_no_encode_char(gs_show_enum * penum, gs_font * pfont, gs_char * pchr) +{ + return gs_no_glyph; +} + +/* <string|name> <font_dict> .buildfont32 <string|name> <font> */ +/* Build a type 32 (bitmap) font. */ +private int +zbuildfont32(os_ptr op) +{ + int code; + build_proc_refs build; + gs_font_base *pfont; + + check_type(*op, t_dictionary); + make_null(&build.BuildChar); + make_null(&build.BuildGlyph); + code = build_gs_simple_font(op, &pfont, ft_bitmap, + &st_gs_font_base, &build, + bf_Encoding_optional); + if (code < 0) + return code; + /* Always transform cached bitmaps. */ + pfont->BitmapWidths = true; + pfont->ExactSize = fbit_transform_bitmaps; + pfont->InBetweenSize = fbit_transform_bitmaps; + pfont->TransformedChar = fbit_transform_bitmaps; + /* The encode_char procedure of a Type 32 font */ + /* should never be called. */ + pfont->procs.encode_char = zfont_no_encode_char; + return define_gs_font((gs_font *) pfont); +} + +/* ------ Initialization procedure ------ */ + +const op_def zfont32_op_defs[] = +{ + {"2.buildfont32", zbuildfont32}, + op_def_end(0) +}; diff --git a/gs/src/zfreuse.c b/gs/src/zfreuse.c new file mode 100644 index 000000000..041f15ae3 --- /dev/null +++ b/gs/src/zfreuse.c @@ -0,0 +1,200 @@ +/* Copyright (C) 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: zfreuse.c */ +/* ReusableStreamDecode filter support */ +#include "memory_.h" +#include "ghost.h" +#include "oper.h" +#include "stream.h" +#include "strimpl.h" +#include "sfilter.h" /* for SubFileDecode */ +#include "files.h" +#include "idict.h" +#include "idparam.h" +#include "iname.h" +#include "store.h" + +/* + * The actual work of constructing the filter is done in PostScript code. + * The operators in this file are internal ones that handle the dirty work. + */ + +/* <dict|null> .rsdparams <filters> <decodeparms|null> */ +/* filters is always an array, and decodeparms is always either an array */ +/* of the same length as filters, or null. */ +private int +zrsdparams(os_ptr op) +{ + ref *pFilter; + ref *pDecodeParms; + int Intent; + bool AsyncRead; + ref empty_array, filter1_array, parms1_array; + uint i; + int code; + + make_empty_array(&empty_array, a_readonly); + if (dict_find_string(op, "Filter", &pFilter) > 0) { + if (!r_is_array(pFilter)) { + if (!r_has_type(pFilter, t_name)) + return_error(e_typecheck); + make_array(&filter1_array, a_readonly, 1, pFilter); + pFilter = &filter1_array; + } + } else + pFilter = &empty_array; + /* If Filter is undefined, ignore DecodeParms. */ + if (pFilter != &empty_array && + dict_find_string(op, "DecodeParms", &pDecodeParms) > 0 + ) { + if (pFilter == &filter1_array) { + make_array(&parms1_array, a_readonly, 1, pDecodeParms); + pDecodeParms = &parms1_array; + } else if (!r_is_array(pDecodeParms)) + return_error(e_typecheck); + else if (r_size(pFilter) != r_size(pDecodeParms)) + return_error(e_rangecheck); + } else + pDecodeParms = 0; + for (i = 0; i < r_size(pFilter); ++i) { + ref f, fname, dp; + + array_get(pFilter, (long)i, &f); + if (!r_has_type(&f, t_name)) + return_error(e_typecheck); + name_string_ref(&f, &fname); + if (r_size(&fname) < 6 || + !memcmp(fname.value.bytes + r_size(&fname) - 6, "Decode", 6) + ) + return_error(e_rangecheck); + if (pDecodeParms) { + array_get(pDecodeParms, (long)i, &dp); + if (!(r_has_type(&dp, t_dictionary) || r_has_type(&dp, t_null))) + return_error(e_typecheck); + } + } + if ((code = dict_int_param(op, "Intent", 0, 3, 0, &Intent)) < 0 || + (code = dict_bool_param(op, "AsyncRead", false, &AsyncRead)) < 0 + ) + return code; + push(1); + op[-1] = *pFilter; + if (pDecodeParms) + *op = *pDecodeParms; + else + make_null(op); + return 0; +} + +/* <file|string> <length|null> <CloseSource> .reusablestream <filter> */ +/* + * The file|string operand must be a "reusable source", either: + * - A string; + * - A readable, positionable file stream; + * - A SubFileDecode filter with an empty EODString and a reusable + * source; + * - A reusable stream. + */ +private int make_rss(P6(os_ptr op, const byte * data, uint size, long offset, + long length, bool close_source)); +private int +zreusablestream(os_ptr op) +{ + os_ptr source_op = op - 2; + os_ptr length_op = op - 1; + long length; + bool close_source; + int code; + + if (r_has_type(length_op, t_integer)) { + length = length_op->value.intval; + if (length < 0) + return_error(e_rangecheck); + } else + length = -1; + check_type(*op, t_boolean); + close_source = op->value.boolval; + if (r_has_type(source_op, t_string)) { + check_read(*source_op); + code = make_rss(source_op, source_op->value.const_bytes, + r_size(source_op), 0L, length, close_source); + } else { + long offset = 0; + stream *source; + + check_read_file(source, source_op); +rs: + if (source->cbuf_string.data != 0) { + /* The data source is a string. */ + long avail; + + offset += stell(source); + savailable(source, &avail); + if (avail < 0) + avail = 0; + if (avail > length) + avail = length; + code = make_rss(source_op, source->cbuf_string.data, + source->cbuf_string.size, offset, avail, + close_source); + } else if (source->file != 0) { + /* The data source is a file. */ +/****** NYI ******/ + } else if (source->state->template == &s_SFD_template) { + /* The data source is a SubFileDecode filter. */ + const stream_SFD_state *const sfd_state = + (const stream_SFD_state *)source->state; + + if (sfd_state->eod.size != 0) + return_error(e_rangecheck); + if (sfd_state->count != 0) { + long left = sfd_state->count + sbufavailable(source); + + if (left < length) + length = left; + } + source = source->strm; + goto rs; + } +/****** REUSABLE CASE IS NYI ******/ + else + return_error(e_rangecheck); + } + if (code >= 0) + pop(2); + return code; +} + +/* Make a reusable string stream. */ +private int +make_rss(os_ptr op, const byte * data, uint size, long offset, + long length, bool close_source) +{ +/****** NYI ******/ + return_error(e_rangecheck); +} + +/* ---------------- Initialization procedure ---------------- */ + +const op_def zfreuse_op_defs[] = +{ + {"2.rsdparams", zrsdparams}, + {"2.reusablestream", zreusablestream}, + op_def_end(0) +}; diff --git a/gs/src/zfunc3.c b/gs/src/zfunc3.c new file mode 100644 index 000000000..383f5fa1d --- /dev/null +++ b/gs/src/zfunc3.c @@ -0,0 +1,130 @@ +/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: zfunc3.c */ +/* PostScript language interface to LL3 Functions */ +#include "memory_.h" +#include "ghost.h" +#include "oper.h" +#include "gsfunc3.h" +#include "gsstruct.h" +#include "stream.h" /* for files.h */ +#include "files.h" +#include "ialloc.h" +#include "idict.h" +#include "idparam.h" +#include "ifunc.h" +#include "store.h" + +/* Initialization */ +private build_function_proc(build_function_2); +private build_function_proc(build_function_3); +int +zfunc3_init(gs_memory_t * mem) +{ + build_function_procs[2] = build_function_2; + build_function_procs[3] = build_function_3; + return 0; +} + +const op_def zfunc3_op_defs[] = +{ + op_def_end(zfunc3_init) +}; + +/* Define the available Function types. */ + +/* Finish building a FunctionType 2 (ExponentialInterpolation) function. */ +private int +build_function_2(const_os_ptr op, const gs_function_params_t * mnDR, int depth, + gs_function_t ** ppfn) +{ + gs_function_ElIn_params_t params; + int code, n0, n1; + + *(gs_function_params_t *)¶ms = *mnDR; + params.C0 = 0; + params.C1 = 0; + if ((code = dict_float_param(op, "N", 0.0, ¶ms.N)) != 0 || + (code = n0 = fn_build_float_array(op, "C0", false, false, ¶ms.C0)) < 0 || + (code = n1 = fn_build_float_array(op, "C1", false, false, ¶ms.C1)) < 0 + ) + goto fail; + if (params.C0 == 0) + n0 = 1; /* C0 defaulted */ + if (params.C1 == 0) + n1 = 1; /* C1 defaulted */ + if (params.Range == 0) + params.n = n0; /* either one will do */ + if (n0 != n1 || n0 != params.n) + goto fail; + code = gs_function_ElIn_init(ppfn, ¶ms, imemory); + if (code >= 0) + return 0; +fail: + gs_function_ElIn_free_params(¶ms, imemory); + return (code < 0 ? code : gs_note_error(e_rangecheck)); +} + +/* Finish building a FunctionType 3 (1-Input Stitching) function. */ +private int +build_function_3(const_os_ptr op, const gs_function_params_t * mnDR, int depth, + gs_function_t ** ppfn) +{ + gs_function_1ItSg_params_t params; + int code; + + *(gs_function_params_t *) & params = *mnDR; + params.Functions = 0; + params.Bounds = 0; + params.Encode = 0; + { + ref *pFunctions; + gs_function_t **ptr; + int i; + + if ((code = dict_find_string(op, "Functions", &pFunctions)) <= 0) + return (code < 0 ? code : gs_note_error(e_rangecheck)); + check_array_only(*pFunctions); + params.k = r_size(pFunctions); + code = ialloc_function_array(params.k, &ptr); + if (code < 0) + return code; + params.Functions = (const gs_function_t * const *)ptr; + for (i = 0; i < params.k; ++i) { + ref subfn; + + array_get(pFunctions, (long)i, &subfn); + code = fn_build_sub_function(&subfn, &ptr[i], depth); + if (code < 0) + goto fail; + } + } + if ((code = fn_build_float_array(op, "Bounds", true, false, ¶ms.Bounds)) != params.k - 1 || + (code = fn_build_float_array(op, "Encode", true, true, ¶ms.Encode)) != 2 * params.k + ) + goto fail; + if (params.Range == 0) + params.n = params.Functions[0]->params.n; + code = gs_function_1ItSg_init(ppfn, ¶ms, imemory); + if (code >= 0) + return 0; +fail: + gs_function_1ItSg_free_params(¶ms, imemory); + return (code < 0 ? code : gs_note_error(e_rangecheck)); +} diff --git a/gs/src/zimage3.c b/gs/src/zimage3.c new file mode 100644 index 000000000..e9ae20a0d --- /dev/null +++ b/gs/src/zimage3.c @@ -0,0 +1,119 @@ +/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: zimage3.c */ +/* LanguageLevel 3 ImageTypes (3 & 4 - masked images) */ +#include "memory_.h" +#include "ghost.h" +#include "oper.h" +#include "gscspace.h" /* for gscolor2.h */ +#include "gscolor2.h" +#include "gsiparm3.h" +#include "gsiparm4.h" +#include "gxiparam.h" /* for image enumerator */ +#include "idict.h" +#include "idparam.h" +#include "igstate.h" +#include "iimage.h" +#include "iimage2.h" + +/* <dict> .image3 - */ +private int +zimage3(register os_ptr op) +{ + gs_image3_t image; + int interleave_type; + ref *pDataDict; + ref *pMaskDict; + image_params ip_data, ip_mask; + int ignored; + int code, mcode; + + check_type(*op, t_dictionary); + check_dict_read(*op); + if ((code = dict_int_param(op, "InterleaveType", 1, 3, -1, + &interleave_type)) < 0 + ) + return code; + gs_image3_t_init(&image, NULL, interleave_type); + if (dict_find_string(op, "DataDict", &pDataDict) <= 0 || + dict_find_string(op, "MaskDict", &pMaskDict) <= 0 + ) + return_error(e_rangecheck); + if ((code = pixel_image_params(pDataDict, (gs_pixel_image_t *)&image, + &ip_data, 12)) < 0 || + (mcode = code = data_image_params(pMaskDict, &image.MaskDict, &ip_mask, false, 1, 12)) < 0 || + (code = dict_int_param(pDataDict, "ImageType", 1, 1, 0, &ignored)) < 0 || + (code = dict_int_param(pMaskDict, "ImageType", 1, 1, 0, &ignored)) < 0 + ) + return code; + /* + * MaskDict must have a DataSource iff InterleaveType == 3. + */ + if ((ip_data.MultipleDataSources && interleave_type != 3) || + ip_mask.MultipleDataSources || + mcode != (image.InterleaveType != 3) + ) + return_error(e_rangecheck); + if (!mcode) { + /* Insert the mask DataSource before the data DataSources. */ + memmove(&ip_data.DataSource[1], &ip_data.DataSource[0], + (countof(ip_data.DataSource) - 1) * + sizeof(ip_data.DataSource[0])); + ip_data.DataSource[0] = ip_mask.DataSource[0]; + } + return zimage_setup((gs_pixel_image_t *)&image, + &ip_data.DataSource[0], + image.CombineWithColor, 1); +} + +/* <dict> .image4 - */ +private int +zimage4(register os_ptr op) +{ + gs_image4_t image; + image_params ip; + int num_components = + gs_color_space_num_components(gs_currentcolorspace(igs)); + int code; + + gs_image4_t_init(&image, NULL); + code = pixel_image_params(op, (gs_pixel_image_t *)&image, &ip, 12); + if (code < 0) + return code; + code = dict_int_array_param(op, "MaskColor", num_components * 2, + image.MaskColor); + if (code == num_components) + image.MaskColor_is_range = false; + else if (code == num_components * 2) + image.MaskColor_is_range = true; + else + return_error(code < 0 ? code : gs_note_error(e_rangecheck)); + return zimage_setup((gs_pixel_image_t *)&image, &ip.DataSource[0], + image.CombineWithColor, 1); +} + +/* ------ Initialization procedure ------ */ + +const op_def zimage3_op_defs[] = +{ + op_def_begin_ll3(), + {"1.image3", zimage3}, + {"1.image4", zimage4}, + op_def_end(0) +}; diff --git a/gs/src/zmisc3.c b/gs/src/zmisc3.c new file mode 100644 index 000000000..68e1420a4 --- /dev/null +++ b/gs/src/zmisc3.c @@ -0,0 +1,105 @@ +/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: zmisc3.c */ +/* Miscellaneous LanguageLevel 3 operators */ +#include "ghost.h" +#include "oper.h" +#include "store.h" + +/* <proc1> <proc2> .eqproc <bool> */ +/* + * Test whether two procedures are equal to depth 10. + * This is the equality test used by idiom recognition in 'bind'. + */ +#define MAX_DEPTH 10 /* depth is per Adobe specification */ +typedef struct ref2_s { + ref proc1, proc2; +} ref2_t; +private int +zeqproc(register os_ptr op) +{ + ref2_t stack[MAX_DEPTH + 1]; + ref2_t *top = stack; + + make_array(&stack[0].proc1, 0, 1, op - 1); + make_array(&stack[0].proc2, 0, 1, op); + for (;;) { + long i; + + if (r_size(&top->proc1) == 0) { + /* Finished these arrays, go up to next level. */ + if (top == stack) { + /* We're done matching: it succeeded. */ + make_true(op - 1); + pop(1); + return 0; + } + --top; + continue; + } + /* Look at the next elements of the arrays. */ + r_dec_size(&top->proc1, 1); + i = r_size(&top->proc1); + array_get(&top->proc1, i, &top[1].proc1); + array_get(&top->proc2, i, &top[1].proc2); + ++top; + /* + * Amazingly enough, the objects' executable attributes are not + * required to match. This means { x load } will match { /x load }, + * even though this is clearly wrong. + */ +#if 0 + if (r_has_attr(&top->proc1, a_executable) != + r_has_attr(&top->proc2, a_executable) + ) + break; +#endif + if (obj_eq(&top->proc1, &top->proc2)) { + /* Names don't match strings. */ + if (r_type(&top->proc1) != r_type(&top->proc2) && + (r_type(&top->proc1) == t_name || + r_type(&top->proc2) == t_name) + ) + break; + --top; /* no recursion */ + continue; + } + if (r_is_array(&top->proc1) && r_is_array(&top->proc2) && + r_size(&top->proc1) == r_size(&top->proc2) && + top < stack + (MAX_DEPTH - 1) + ) { + /* Descend into the arrays. */ + continue; + } + break; + } + /* An exit from the loop indicates that matching failed. */ + make_false(op - 1); + pop(1); + return 0; +} + +/* ------ Initialization procedure ------ */ + +const op_def zmisc3_op_defs[] = +{ + op_def_begin_ll3(), + {"2.eqproc", zeqproc}, + op_def_end(0) +}; diff --git a/gs/src/zshade.c b/gs/src/zshade.c new file mode 100644 index 000000000..d096e7404 --- /dev/null +++ b/gs/src/zshade.c @@ -0,0 +1,593 @@ +/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: zshade.c */ +/* PostScript language interface to shading */ +#include "memory_.h" +#include "ghost.h" +#include "oper.h" +#include "gscolor3.h" +#include "gscspace.h" +#include "gscolor2.h" /* requires gscspace.h */ +#include "gsfunc3.h" +#include "gsstruct.h" /* must precede gsshade.h */ +#include "gsshade.h" +#include "gsuid.h" +#include "stream.h" /* for files.h */ +#include "files.h" +#include "ialloc.h" +#include "idict.h" +#include "idparam.h" +#include "ifunc.h" +#include "igstate.h" +#include "store.h" + +/* Forward references */ +private int shading_param(P2(const_os_ptr op, const gs_shading_t ** ppsh)); + +/* ---------------- Standard operators ---------------- */ + +/* - currentsmoothness <smoothness> */ +private int +zcurrentsmoothness(register os_ptr op) +{ + push(1); + make_real(op, gs_currentsmoothness(igs)); + return 0; +} + +/* <smoothness> setsmoothness - */ +private int +zsetsmoothness(register os_ptr op) +{ + double smoothness; + int code; + + if (real_param(op, &smoothness) < 0) + return_op_typecheck(op); + if ((code = gs_setsmoothness(igs, smoothness)) < 0) + return code; + pop(1); + return 0; +} + +/* <shading> .shfill - */ +private int +zshfill(register os_ptr op) +{ + const gs_shading_t *psh; + int code = shading_param(op, &psh); + + if (code < 0 || (code = gs_shfill(igs, psh)) < 0) + return code; + pop(1); + return 0; +} + +/* ------ Non-standard operators ------ */ + +/* <pattern> <matrix> <shading> .buildshadingpattern <pattern> <instance> */ +private int +zbuildshadingpattern(os_ptr op) +{ + os_ptr op2 = op - 2; + const gs_shading_t *psh; + gs_matrix mat; + struct { + gs_uid uid; + } template; /****** WRONG ******/ + int code; + + check_type(*op2, t_dictionary); + check_dict_read(*op2); + + if ((code = shading_param(op, &psh)) < 0 || + (code = read_matrix(op - 1, &mat)) < 0 || + (code = dict_uid_param(op2, &template.uid, 1, imemory)) != 1 + ) + return_error((code < 0 ? code : e_rangecheck)); + /****** NYI ******/ + return_error(e_undefined); +} + +/* ------ Internal procedures ------ */ + +/* Get a shading parameter. */ +private int +shading_param(const_os_ptr op, const gs_shading_t ** ppsh) +{ /* + * Since shadings form a subclass hierarchy, we currently have + * no way to check whether a structure is actually a shading. + */ + if (!r_is_struct(op) || + r_has_masked_attrs(op, a_executable | a_execute, a_all) + ) + return_error(e_typecheck); + *ppsh = (gs_shading_t *) op->value.pstruct; + return 0; +} + +/* ---------------- Shading dictionaries ---------------- */ + +/* ------ Common code ------ */ + +extern_st(st_color_space); + +typedef int (*build_shading_proc_t)(P3(const ref *op, + const gs_shading_params_t *params, + gs_shading_t **ppsh)); + +/* Operators */ + +/* Common framework for building shadings. */ +private int +build_shading(ref * op, build_shading_proc_t proc) +{ + int code; + float box[4]; + gs_shading_params_t params; + gs_shading_t *psh; + ref *pvalue; + + check_type(*op, t_dictionary); + params.ColorSpace = 0; + params.Background = 0; + /* Collect parameters common to all shading types. */ + { + const gs_color_space *pcs_orig = gs_currentcolorspace(igs); + int num_comp = gs_color_space_num_components(pcs_orig); + gs_color_space *pcs; + + if (num_comp < 0) /* Pattern color space */ + return_error(e_rangecheck); + pcs = ialloc_struct(gs_color_space, &st_color_space, + "build_shading"); + if (pcs == 0) + return_error(e_VMerror); + gs_cspace_init_from(pcs, pcs_orig); + params.ColorSpace = pcs; + if (dict_find_string(op, "Background", &pvalue) > 0) { + gs_client_color *pcc = + ialloc_struct(gs_client_color, &st_client_color, + "build_shading"); + + if (pcc == 0) { + code = gs_note_error(e_VMerror); + goto fail; + } + pcc->pattern = 0; + params.Background = pcc; + code = dict_float_array_param(op, "Background", + countof(pcc->paint.values), + pcc->paint.values, NULL); + if (code != gs_color_space_num_components(pcs)) + goto fail; + } + } + if (dict_find_string(op, "BBox", &pvalue) <= 0) + params.have_BBox = false; + else if ((code = dict_float_array_param(op, "BBox", 4, box, NULL)) == 4) { + params.BBox.p.x = box[0]; + params.BBox.p.y = box[1]; + params.BBox.q.x = box[2]; + params.BBox.q.y = box[3]; + params.have_BBox = true; + } else + goto fail; + code = dict_bool_param(op, "AntiAlias", false, ¶ms.AntiAlias); + if (code < 0) + goto fail; + /* Finish building the shading. */ + code = (*proc) (op, ¶ms, &psh); + if (code < 0) + goto fail; + make_istruct_new(op, 0, psh); + return code; +fail: + ifree_object(params.Background, "Background"); + if (params.ColorSpace) { + gs_cspace_release(params.ColorSpace); + ifree_object(params.ColorSpace, "ColorSpace"); + } + return (code < 0 ? code : gs_note_error(e_rangecheck)); +} + +/* Collect a Function value. */ +private int +build_shading_function(const ref * op, gs_function_t ** ppfn, int num_inputs) +{ + ref *pFunction; + + *ppfn = 0; + if (dict_find_string(op, "Function", &pFunction) <= 0) + return 0; + if (r_is_array(pFunction)) { + uint size = r_size(pFunction); + gs_function_t **Functions; + uint i; + gs_function_AdOt_params_t params; + int code; + + check_read(*pFunction); + if (size == 0) + return_error(e_rangecheck); + code = ialloc_function_array(size, &Functions); + if (code < 0) + return code; + for (i = 0; i < size; ++i) { + ref rsubfn; + + array_get(op, (long)i, &rsubfn); + code = fn_build_function(&rsubfn, &Functions[i]); + if (code < 0) + break; + } + params.m = 1; + params.Domain = 0; + params.n = size; + params.Range = 0; + params.Functions = (const gs_function_t * const *)Functions; + if (code >= 0) + code = gs_function_AdOt_init(ppfn, ¶ms, imemory); + if (code < 0) + gs_function_AdOt_free_params(¶ms, imemory); + return code; + } else + return fn_build_function(pFunction, ppfn); +} + +/* ------ Build shadings ------ */ + +/* Build a ShadingType 1 (Function-based) shading. */ +private int +build_shading_1(const ref * op, const gs_shading_params_t * pcommon, + gs_shading_t ** ppsh) +{ + gs_shading_Fb_params_t params; + int code; + static const float default_Domain[4] = {0, 1, 0, 1}; + + *(gs_shading_params_t *)¶ms = *pcommon; + gs_make_identity(¶ms.Matrix); + params.Function = 0; + if ((code = dict_float_array_param(op, "Domain", 4, params.Domain, + default_Domain)) != 4 || + (code = dict_matrix_param(op, "Matrix", ¶ms.Matrix)) < 0 || + (code = build_shading_function(op, ¶ms.Function, 2)) < 0 || + (code = gs_shading_Fb_init(ppsh, ¶ms, imemory)) < 0 + ) { + ifree_object(params.Function, "Function"); + return (code < 0 ? code : gs_note_error(e_rangecheck)); + } + return 0; +} +/* <dict> .buildshading1 <shading_struct> */ +private int +zbuildshading1(os_ptr op) +{ + return build_shading(op, build_shading_1); +} + +/* Collect parameters for an Axial or Radial shading. */ +private int +build_directional_shading(const ref * op, float *Coords, int num_Coords, + float Domain[2], gs_function_t ** pFunction, bool Extend[2]) +{ + int code = + dict_float_array_param(op, "Coords", num_Coords, Coords, NULL); + static const float default_Domain[2] = {0, 1}; + ref *pExtend; + + *pFunction = 0; + if (code != num_Coords || + (code = dict_float_array_param(op, "Domain", 2, Domain, + default_Domain)) != 2 || + (code = build_shading_function(op, pFunction, 1)) < 0 + ) + return (code < 0 ? code : gs_note_error(e_rangecheck)); + if (dict_find_string(op, "Extend", &pExtend) <= 0) + Extend[0] = Extend[1] = false; + else { + ref E0, E1; + + if (!r_is_array(pExtend)) + return_error(e_typecheck); + else if (r_size(pExtend) != 2) + return_error(e_rangecheck); + else if ((array_get(pExtend, 0L, &E0), !r_has_type(&E0, t_boolean)) || + (array_get(pExtend, 1L, &E1), !r_has_type(&E1, t_boolean)) + ) + return_error(e_typecheck); + Extend[0] = E0.value.boolval, Extend[1] = E1.value.boolval; + } + return 0; +} + +/* Build a ShadingType 2 (Axial) shading. */ +private int +build_shading_2(const ref * op, const gs_shading_params_t * pcommon, + gs_shading_t ** ppsh) +{ + gs_shading_A_params_t params; + int code; + + *(gs_shading_params_t *)¶ms = *pcommon; + if ((code = build_directional_shading(op, params.Coords, 4, + params.Domain, ¶ms.Function, + params.Extend)) < 0 || + (code = gs_shading_A_init(ppsh, ¶ms, imemory)) < 0 + ) { + ifree_object(params.Function, "Function"); + } + return code; +} +/* <dict> .buildshading2 <shading_struct> */ +private int +zbuildshading2(os_ptr op) +{ + return build_shading(op, build_shading_2); +} + +/* Build a ShadingType 3 (Radial) shading. */ +private int +build_shading_3(const ref * op, const gs_shading_params_t * pcommon, + gs_shading_t ** ppsh) +{ + gs_shading_R_params_t params; + int code; + + *(gs_shading_params_t *)¶ms = *pcommon; + if ((code = build_directional_shading(op, params.Coords, 6, + params.Domain, ¶ms.Function, + params.Extend)) < 0 || + (code = gs_shading_R_init(ppsh, ¶ms, imemory)) < 0 + ) { + ifree_object(params.Function, "Function"); + } + return code; +} +/* <dict> .buildshading3 <shading_struct> */ +private int +zbuildshading3(os_ptr op) +{ + return build_shading(op, build_shading_3); +} + +/* Collect parameters for a mesh shading. */ +private int +build_mesh_shading(const ref * op, gs_shading_mesh_params_t * params, + float **pDecode, gs_function_t ** pFunction) +{ + int code; + ref *pDataSource; + + *pDecode = 0; + *pFunction = 0; + if (dict_find_string(op, "DataSource", &pDataSource) <= 0) + return_error(e_rangecheck); + if (r_is_array(pDataSource)) { + uint size = r_size(pDataSource); + float *data = + (float *)ialloc_byte_array(size, sizeof(float), + "build_mesh_shading"); + int code; + + if (data == 0) + return_error(e_VMerror); + code = float_params(pDataSource->value.refs + size - 1, size, data); + if (code < 0) { + ifree_object(data, "build_mesh_shading"); + return code; + } + data_source_init_floats(¶ms->DataSource, data, size); + } else + switch (r_type(pDataSource)) { + case t_file: { + stream *s; + + check_read_file(s, pDataSource); + data_source_init_stream(¶ms->DataSource, s); + break; + } + case t_string: + check_read(*pDataSource); + data_source_init_string2(¶ms->DataSource, + pDataSource->value.bytes, + r_size(pDataSource)); + break; + default: + return_error(e_typecheck); + } + if (data_source_is_array(params->DataSource)) { + params->BitsPerCoordinate = 0; + params->BitsPerComponent = 0; + } else { + int num_decode = + 4 + gs_color_space_num_components(params->ColorSpace) * 2; + +/****** FREE FLOAT ARRAY DATA ON ERROR ******/ + if ((code = dict_int_param(op, "BitsPerCoordinate", 1, 32, 0, + ¶ms->BitsPerCoordinate)) < 0 || + (code = dict_int_param(op, "BitsPerComponent", 1, 16, 0, + ¶ms->BitsPerComponent)) < 0 + ) + return code; + *pDecode = (float *)ialloc_byte_array(num_decode, sizeof(float), + "build_mesh_shading"); + + if (*pDecode == 0) + return_error(e_VMerror); + code = dict_float_array_param(op, "Decode", num_decode, *pDecode, + NULL); + if (code != num_decode) { + ifree_object(*pDecode, "build_mesh_shading"); + *pDecode = 0; + return (code < 0 ? code : gs_note_error(e_rangecheck)); + } + } + code = build_shading_function(op, pFunction, 1); + if (code < 0) { + ifree_object(*pDecode, "build_mesh_shading"); + *pDecode = 0; + } + return code; +} + +/* Collect the BitsPerFlag parameter, if relevant. */ +private int +flag_bits_param(const ref * op, const gs_shading_mesh_params_t * params, + int *pBitsPerFlag) +{ + if (data_source_is_array(params->DataSource)) { + *pBitsPerFlag = 0; + return 0; + } else { + return dict_int_param(op, "BitsPerFlag", 2, 8, 0, pBitsPerFlag); + } +} + +/* Build a ShadingType 4 (Free-form Gouraud triangle mesh) shading. */ +private int +build_shading_4(const ref * op, const gs_shading_params_t * pcommon, + gs_shading_t ** ppsh) +{ + gs_shading_FfGt_params_t params; + int code; + + *(gs_shading_params_t *)¶ms = *pcommon; + if ((code = + build_mesh_shading(op, (gs_shading_mesh_params_t *)¶ms, + ¶ms.Decode, ¶ms.Function)) < 0 || + (code = flag_bits_param(op, (gs_shading_mesh_params_t *)¶ms, + ¶ms.BitsPerFlag)) < 0 || + (code = gs_shading_FfGt_init(ppsh, ¶ms, imemory)) < 0 + ) { + ifree_object(params.Function, "Function"); + ifree_object(params.Decode, "Decode"); + } + return code; +} +/* <dict> .buildshading4 <shading_struct> */ +private int +zbuildshading4(os_ptr op) +{ + return build_shading(op, build_shading_4); +} + +/* Build a ShadingType 5 (Lattice-form Gouraud triangle mesh) shading. */ +private int +build_shading_5(const ref * op, const gs_shading_params_t * pcommon, + gs_shading_t ** ppsh) +{ + gs_shading_LfGt_params_t params; + int code; + + *(gs_shading_params_t *)¶ms = *pcommon; + if ((code = + build_mesh_shading(op, (gs_shading_mesh_params_t *)¶ms, + ¶ms.Decode, ¶ms.Function)) < 0 || + (code = dict_int_param(op, "VerticesPerRow", 2, max_int, 0, + ¶ms.VerticesPerRow)) < 0 || + (code = gs_shading_LfGt_init(ppsh, ¶ms, imemory)) < 0 + ) { + ifree_object(params.Function, "Function"); + ifree_object(params.Decode, "Decode"); + } + return code; +} +/* <dict> .buildshading5 <shading_struct> */ +private int +zbuildshading5(os_ptr op) +{ + return build_shading(op, build_shading_5); +} + +/* Build a ShadingType 6 (Coons patch mesh) shading. */ +private int +build_shading_6(const ref * op, const gs_shading_params_t * pcommon, + gs_shading_t ** ppsh) +{ + gs_shading_Cp_params_t params; + int code; + + *(gs_shading_params_t *)¶ms = *pcommon; + if ((code = + build_mesh_shading(op, (gs_shading_mesh_params_t *)¶ms, + ¶ms.Decode, ¶ms.Function)) < 0 || + (code = flag_bits_param(op, (gs_shading_mesh_params_t *)¶ms, + ¶ms.BitsPerFlag)) < 0 || + (code = gs_shading_Cp_init(ppsh, ¶ms, imemory)) < 0 + ) { + ifree_object(params.Function, "Function"); + ifree_object(params.Decode, "Decode"); + } + return code; +} +/* <dict> .buildshading6 <shading_struct> */ +private int +zbuildshading6(os_ptr op) +{ + return build_shading(op, build_shading_6); +} + +/* Build a ShadingType 7 (Tensor product patch mesh) shading. */ +private int +build_shading_7(const ref * op, const gs_shading_params_t * pcommon, + gs_shading_t ** ppsh) +{ + gs_shading_Tpp_params_t params; + int code; + + *(gs_shading_params_t *)¶ms = *pcommon; + if ((code = + build_mesh_shading(op, (gs_shading_mesh_params_t *)¶ms, + ¶ms.Decode, ¶ms.Function)) < 0 || + (code = flag_bits_param(op, (gs_shading_mesh_params_t *)¶ms, + ¶ms.BitsPerFlag)) < 0 || + (code = gs_shading_Tpp_init(ppsh, ¶ms, imemory)) < 0 + ) { + ifree_object(params.Function, "Function"); + ifree_object(params.Decode, "Decode"); + } + return code; +} +/* <dict> .buildshading7 <shading_struct> */ +private int +zbuildshading7(os_ptr op) +{ + return build_shading(op, build_shading_7); +} + +/* ------ Initialization procedure ------ */ + +const op_def zshade_op_defs[] = +{ + op_def_begin_ll3(), + {"0currentsmoothness", zcurrentsmoothness}, + {"1setsmoothness", zsetsmoothness}, + {"1.shfill", zshfill}, + {"1.buildshading1", zbuildshading1}, + {"1.buildshading2", zbuildshading2}, + {"1.buildshading3", zbuildshading3}, + {"1.buildshading4", zbuildshading4}, + {"1.buildshading5", zbuildshading5}, + {"1.buildshading6", zbuildshading6}, + {"1.buildshading7", zbuildshading7}, + {"3.buildshadingpattern", zbuildshadingpattern}, + op_def_end(0) +}; diff --git a/gs/src/ztrap.c b/gs/src/ztrap.c new file mode 100644 index 000000000..3270e56c9 --- /dev/null +++ b/gs/src/ztrap.c @@ -0,0 +1,66 @@ +/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved. + + This file is part of Aladdin Ghostscript. + + Aladdin 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 Ghostscript Free Public + License (the "License") for full details. + + Every copy of Aladdin 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 Aladdin 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: ztrap.c */ +/* Operators for setting trapping parameters and zones */ +#include "ghost.h" +#include "oper.h" +#include "ialloc.h" +#include "iparam.h" +#include "gstrap.h" + +/* Define the current trap parameters. */ +/****** THIS IS BOGUS ******/ +gs_trap_params_t i_trap_params; + +/* <dict> .settrapparams - */ +private int +zsettrapparams(os_ptr op) +{ + dict_param_list list; + int code; + + cheak_type(*op, t_dictionary); + code = dict_param_list_read(&list, op, NULL, false); + if (code < 0) + return code; + code = gs_settrapparams(&i_trap_params, (gs_param_list *) & list); + iparam_list_release(&list); + if (code < 0) + return code; + pop(1); + return 0; +} + +/* - settrapzone - */ +private int +zsettrapzone(os_ptr op) +{ +/****** NYI ******/ + return_error(e_undefined); +} + +/* ------ Initialization procedure ------ */ + +const op_def ztrap_op_defs[] = +{ + op_def_begin_ll3(), + {"1.settrapparams", zsettrapparams}, + {"0settrapzone", zsettrapzone}, + op_def_end(0) +}; |