diff options
Diffstat (limited to 'pxl/pxink.c')
-rw-r--r-- | pxl/pxink.c | 854 |
1 files changed, 854 insertions, 0 deletions
diff --git a/pxl/pxink.c b/pxl/pxink.c new file mode 100644 index 000000000..1d23beecb --- /dev/null +++ b/pxl/pxink.c @@ -0,0 +1,854 @@ +/* Portions Copyright (C) 2001 artofcode LLC. + Portions Copyright (C) 1996, 2001 Artifex Software Inc. + Portions Copyright (C) 1988, 2000 Aladdin Enterprises. + This software is based in part on the work of the Independent JPEG Group. + All Rights Reserved. + + This software is distributed under license and may not be copied, modified + or distributed except as expressly authorized under the terms of that + license. Refer to licensing information at http://www.artifex.com/ or + contact Artifex Software, Inc., 101 Lucas Valley Road #110, + San Rafael, CA 94903, (415)492-9861, for further information. */ +/*$Id$ */ + +/* pxink.c */ +/* PCL XL ink setting operators */ + +#include "math_.h" +#include "stdio_.h" /* for NULL */ +#include "memory_.h" +#include "gdebug.h" +#include "gstypes.h" +#include "gsmemory.h" +#include "pxoper.h" +#include "pxstate.h" +#include "gxarith.h" +#include "gsstate.h" +#include "gxcspace.h" /* must precede gscolor2.h */ +#include "gscolor2.h" +#include "gscoord.h" +#include "gsimage.h" +#include "gscie.h" +#include "gscrd.h" +#include "gspath.h" +#include "gxdevice.h" +#include "gxht.h" +#include "gxstate.h" +#include "plsrgb.h" +#include "plht.h" + +/* + * Contrary to the documentation, SetColorSpace apparently doesn't set the + * brush or pen to black. To produce this behavior, uncomment the + * following #define. + */ +#define SET_COLOR_SPACE_NO_SET_BLACK + +/* ---------------- Utilities ---------------- */ + +/* ------ Halftones ------ */ + +/* Define a transfer function without gamma correction. */ +static float +identity_transfer(floatp tint, const gx_transfer_map *ignore_map) +{ return tint; +} + +/* Set the default halftone screen. */ +static const byte order16x16[256] = { +#if 0 + /* + * The following is a standard 16x16 ordered dither, except that + * the very last pass goes in the order (0,1,2,3) rather than + * (0,3,1,2). This leads to excessive cancellation when the source + * and paint halftones interact, but it's better than the standard + * order, which has inadequate cancellation. + * + * This matrix is generated by the following call: + * [ <00 88 08 80> <00 44 04 40> <00 22 02 20> <00 01 10 11> ] + * { } makedither + */ + 0,64,32,96,8,72,40,104,2,66,34,98,10,74,42,106, + 128,192,160,224,136,200,168,232,130,194,162,226,138,202,170,234, + 48,112,16,80,56,120,24,88,50,114,18,82,58,122,26,90, + 176,240,144,208,184,248,152,216,178,242,146,210,186,250,154,218, + 12,76,44,108,4,68,36,100,14,78,46,110,6,70,38,102, + 140,204,172,236,132,196,164,228,142,206,174,238,134,198,166,230, + 60,124,28,92,52,116,20,84,62,126,30,94,54,118,22,86, + 188,252,156,220,180,244,148,212,190,254,158,222,182,246,150,214, + 3,67,35,99,11,75,43,107,1,65,33,97,9,73,41,105, + 131,195,163,227,139,203,171,235,129,193,161,225,137,201,169,233, + 51,115,19,83,59,123,27,91,49,113,17,81,57,121,25,89, + 179,243,147,211,187,251,155,219,177,241,145,209,185,249,153,217, + 15,79,47,111,7,71,39,103,13,77,45,109,5,69,37,101, + 143,207,175,239,135,199,167,231,141,205,173,237,133,197,165,229, + 63,127,31,95,55,119,23,87,61,125,29,93,53,117,21,85, + 191,255,159,223,183,247,151,215,189,253,157,221,181,245,149,213 +# define source_phase_x 1 +# define source_phase_y 1 +#else +/* + * The following is a 45 degree spot screen with the spots enumerated + * in a defined order. This matrix is generated by the following call: + +/gamma_transfer { + dup dup 0 le exch 1 ge or not { + dup 0.17 lt + { 3 mul } + { dup 0.35 lt { 0.78 mul 0.38 add } { 0.53 mul 0.47 add } ifelse } + ifelse + } if +} def + +[ [0 136 8 128 68 204 76 196] + [18 33 17 34 1 2 19 35 50 49 32 16 3 48 0 51 + -15 -14 20 36 66 65 47 31 -13 64 15 52 -30 81 30 37] +] /gamma_transfer load makedither + + */ + 38,11,14,32,165,105,90,171,38,12,14,33,161,101,88,167, + 30,6,0,16,61,225,231,125,30,6,1,17,63,222,227,122, + 27,3,8,19,71,242,205,110,28,4,9,20,74,246,208,106, + 35,24,22,40,182,46,56,144,36,25,22,41,186,48,58,148, + 152,91,81,174,39,12,15,34,156,95,84,178,40,13,16,34, + 69,212,235,129,31,7,2,18,66,216,239,133,32,8,2,18, + 79,254,203,114,28,4,10,20,76,250,199,118,29,5,10,21, + 193,44,54,142,36,26,23,42,189,43,52,139,37,26,24,42, + 39,12,15,33,159,99,87,169,38,11,14,33,163,103,89,172, + 31,7,1,17,65,220,229,123,30,6,1,17,62,223,233,127, + 28,4,9,20,75,248,210,108,27,3,9,19,72,244,206,112, + 36,25,23,41,188,49,60,150,35,25,22,41,184,47,57,146, + 157,97,85,180,40,13,16,35,154,93,83,176,39,13,15,34, + 67,218,240,135,32,8,3,19,70,214,237,131,31,7,2,18, + 78,252,197,120,29,5,11,21,80,255,201,116,29,5,10,21, + 191,43,51,137,37,27,24,43,195,44,53,140,37,26,23,42 +# define source_phase_x 4 +# define source_phase_y 0 +#endif +}; + +/* Set the size for a default halftone screen. */ +static void +px_set_default_screen_size(px_state_t *pxs, int method) +{ px_gstate_t *pxgs = pxs->pxgs; + + pxgs->halftone.width = pxgs->halftone.height = 16; +} + +/* If necessary, set the halftone in the graphics state. */ +int +px_set_halftone(px_state_t *pxs) +{ + px_gstate_t *pxgs = pxs->pxgs; + int code; + + if ( pxgs->halftone.set ) + return 0; + if ( pxgs->halftone.method != eDownloaded ) { + gs_string thresh; + thresh.data = (byte *)order16x16; + thresh.size = 256; + code = pl_set_pcl_halftone(pxs->pgs, + /* set transfer */ identity_transfer, + /* width */ 16, /*height */ 16, + /* dither data */ thresh, + /* x phase */ (int)pxgs->halftone.origin.x, + /* y phase */ (int)pxgs->halftone.origin.y); + } else { /* downloaded */ + int ht_width, ht_height; + switch ( pxs->orientation ) { + case ePortraitOrientation: + case eReversePortrait: + ht_width = pxgs->halftone.width; + ht_height = pxgs->halftone.height; + break; + case eLandscapeOrientation: + case eReverseLandscape: + ht_width = pxgs->halftone.height; + ht_height = pxgs->halftone.width; + break; + default: + return -1; + } + + code = pl_set_pcl_halftone(pxs->pgs, + /* set transfer */ identity_transfer, + /* width */ ht_width, /*height */ ht_height, + /* dither data */ pxgs->halftone.thresholds, + /* x phase */ (int)pxgs->halftone.origin.x, + /* y phase */ (int)pxgs->halftone.origin.y); + if ( code < 0 ) + gs_free_string(pxs->memory, pxgs->halftone.thresholds.data, + pxgs->halftone.thresholds.size, + "px_set_halftone(thresholds)"); + else { + gs_free_string(pxs->memory, (byte *)pxgs->dither_matrix.data, + pxgs->dither_matrix.size, + "px_set_halftone(dither_matrix)"); + pxgs->dither_matrix = pxgs->halftone.thresholds; + } + pxgs->halftone.thresholds.data = 0; + pxgs->halftone.thresholds.size = 0; + } + if ( code < 0 ) + return code; + pxgs->halftone.set = true; + /* Cached patterns have already been halftoned, so clear the cache. */ + px_purge_pattern_cache(pxs, eSessionPattern); + return 0; +} + +/* ------ Patterns ------ */ + +/* + * The library caches patterns in their fully rendered form, i.e., after + * halftoning. In order to avoid seams or anomalies, we have to replicate + * the pattern so that its size is an exact multiple of the halftone size. + */ + +static uint +ilcm(uint x, uint y) +{ return x * (y / igcd(x, y)); +} + +/* Render a pattern. */ +static int +px_paint_pattern(const gs_client_color *pcc, gs_state *pgs) +{ const gs_client_pattern *ppat = gs_getpattern(pcc); + const px_pattern_t *pattern = ppat->client_data; + const byte *dp = pattern->data; + gs_image_enum *penum; + gs_image_t image; + int code; + int num_components = + (pattern->params.indexed || pattern->params.color_space == eGray ? + 1 : 3); + uint rep_width = pattern->params.width; + uint rep_height = pattern->params.height; + uint full_width = (uint)ppat->XStep; + uint full_height = (uint)ppat->YStep; + uint bits_per_row, bytes_per_row; + int x; + + code = px_image_color_space(&image, &pattern->params, &pattern->palette, pgs); + if ( code < 0 ) + return code; + penum = gs_image_enum_alloc(gs_state_memory(pgs), "px_paint_pattern"); + if ( penum == 0 ) + return_error(errorInsufficientMemory); + bits_per_row = rep_width * image.BitsPerComponent * num_components; + bytes_per_row = (bits_per_row + 7) >> 3; + /* + * As noted above, in general, we have to replicate the original + * pattern to a multiple that avoids halftone seams. If the + * number of bits per row is a multiple of 8, we can do this with + * a single image; otherwise, we need one image per X replica. + * To simplify the code, we always use the (slightly) slower method. + */ + image.Width = rep_width; + image.Height = full_height; + image.CombineWithColor = true; + for ( x = 0; x < full_width; x += rep_width ) + { int y; + + image.ImageMatrix.tx = -x; + code = gs_image_init(penum, &image, false, pgs); + if ( code < 0 ) + break; + for ( y = 0; code >= 0 && y < full_height; ++y ) + { const byte *row = dp + (y % rep_height) * bytes_per_row; + uint used; + + code = gs_image_next(penum, row, bytes_per_row, &used); + } + gs_image_cleanup(penum, pgs); + } + gs_free_object(gs_state_memory(pgs), penum, "px_paint_pattern"); + return code; +} + +/* Create the rendering of a pattern. */ +static int +render_pattern(gs_client_color *pcc, const px_pattern_t *pattern, + const px_value_t *porigin, const px_value_t *pdsize, px_state_t *pxs) +{ px_gstate_t *pxgs = pxs->pxgs; + uint rep_width = pattern->params.width; + uint rep_height = pattern->params.height; + uint full_width, full_height; + gs_state *pgs = pxs->pgs; + gs_client_pattern template; + + /* + * If halftoning may occur, replicate the pattern so we don't get + * halftone seams. + */ + { gx_device *dev = gs_currentdevice(pgs); + if (!gx_device_must_halftone(dev)) + { /* No halftoning. */ + full_width = rep_width; + full_height = rep_height; + } + else + { full_width = ilcm(rep_width, pxgs->halftone.width); + full_height = ilcm(rep_height, pxgs->halftone.height); + /* + * If the pattern would be enormous, don't replicate it. + * This is a HACK. + */ + if ( full_width > 10000 ) + full_width = rep_width; + if ( full_height > 10000 ) + full_height = rep_height; + } + } + /* Construct a Pattern for the library, and render it. */ + gs_pattern1_init(&template); + uid_set_UniqueID(&template.uid, pattern->id); + template.PaintType = 1; + template.TilingType = 1; + template.BBox.p.x = 0; + template.BBox.p.y = 0; + template.BBox.q.x = full_width; + template.BBox.q.y = full_height; + template.XStep = full_width; + template.YStep = full_height; + template.PaintProc = px_paint_pattern; + template.client_data = (void *)pattern; + { gs_matrix mat; + gs_point dsize; + int code; + + if ( porigin ) + gs_make_translation(real_value(porigin, 0), + real_value(porigin, 1), &mat); + else + gs_make_identity(&mat); + + if ( pdsize ) + { dsize.x = real_value(pdsize, 0); + dsize.y = real_value(pdsize, 1); + } + else + { dsize.x = pattern->params.dest_width; + dsize.y = pattern->params.dest_height; + } + gs_matrix_scale(&mat, dsize.x / rep_width, dsize.y / rep_height, + &mat); + /* + * gs_makepattern will make a copy of the current gstate. + * We don't want this copy to contain any circular back pointers + * to px_pattern_ts: such pointers are unnecessary, because + * px_paint_pattern doesn't use the pen and brush (in fact, + * it doesn't even reference the px_gstate_t). We also want to + * reset the path and clip path. The easiest (although not by + * any means the most efficient) way to do this is to do a gsave, + * reset the necessary things, do the makepattern, and then do + * a grestore. + */ + code = gs_gsave(pgs); + if ( code < 0 ) + return code; + { px_gstate_t *pxgs = pxs->pxgs; + + px_gstate_rc_adjust(pxgs, -1, pxgs->memory); + pxgs->brush.type = pxgs->pen.type = pxpNull; + gs_newpath(pgs); + px_initclip(pxs); + } + { + gs_color_space *pcs; + /* set the color space */ + switch ( pxgs->color_space ) { + case eGray: + pcs = gs_cspace_new_DeviceGray(pxgs->memory); + if ( pcs == NULL ) + return_error(errorInsufficientMemory); + gs_setcolorspace(pgs, pcs); + break; + case eRGB: + pcs = gs_cspace_new_DeviceRGB(pxgs->memory); + if ( pcs == NULL ) + return_error(errorInsufficientMemory); + break; + case eSRGB: + case eCRGB: + if ( pl_cspace_init_SRGB(&pcs, pgs) < 0 ) + /* should not happen */ + return_error(errorInsufficientMemory); + break; + default: + return_error(errorIllegalAttributeValue); + } + gs_setcolorspace(pgs, pcs); + } + code = gs_makepattern(pcc, &template, &mat, pgs, NULL); + gs_grestore(pgs); + return code; + } +} + +/* ------ Brush/pen ------ */ + +/* Check parameters and execute SetBrushSource or SetPenSource: */ +/* pxaRGBColor, pxaGrayLevel, pxaNullBrush/Pen, pxaPatternSelectID, */ +/* pxaPatternOrigin, pxaNewDestinationSize */ +static ulong +int_type_max(px_data_type_t type) +{ return + (type & pxd_ubyte ? 255 : + type & pxd_uint16 ? 65535 : + type & pxd_sint16 ? 32767 : + type & pxd_uint32 ? (ulong)0xffffffff : + /* type & pxd_sint32 */ 0x7fffffff); +} +static real +fraction_value(const px_value_t *pv, int i) +{ px_data_type_t type = pv->type; + real v; + + if ( type & pxd_real32 ) + return pv->value.ra[i]; + v = pv->value.ia[i]; + return (v < 0 ? 0 : v / int_type_max(type)); +} + + +/* we use an enumeration instead of index numbers improve readability in this + "very busy" routine */ +typedef enum { + aRGBColor = 0, + aGrayLevel = 1, + aPrimaryArray = 2, + aPrimaryDepth = 3, + aNullBrushPen = 4, + aPatternSelectID = 5, + aPatternOrigin = 6, + aNewDestinationSize = 7 +} pxl_source_t; + +/* given a paint type decide if a halftone is necessary */ +static bool +px_needs_halftone(const gs_memory_t *mem, px_paint_t *ppt) +{ + bool needs_halftone; + if ( ppt->type == pxpPattern ) + needs_halftone = true; + else if ( ppt->type == pxpNull ) + needs_halftone = false; + else if ( ppt->type == pxpGray ) + needs_halftone = ppt->value.gray != 0 && ppt->value.gray != 1; + else if ( ppt->type == pxpRGB || ppt->type == pxpSRGB ) { /* rgb or srgb */ + int i; + needs_halftone = false; + for ( i = 0; i < 3; i++ ) { + if ( ppt->value.rgb[i] != 0 && ppt->value.rgb[i] != 1 ) { + needs_halftone = true; + break; + } + } + } else { + dprintf("unknown paint type\n"); + needs_halftone = true; + } + return needs_halftone; +} + +static int +set_source(const px_args_t *par, px_state_t *pxs, px_paint_t *ppt) +{ + px_gstate_t *pxgs = pxs->pxgs; + int code = 0; + /* pxaPatternSelectID */ + if ( par->pv[aPatternSelectID] ) { + px_value_t key; + void *value; + px_pattern_t *pattern; + gs_client_color ccolor; + int code; + if ( par->pv[aRGBColor] || par->pv[aGrayLevel] || par->pv[aNullBrushPen] ) + return_error(errorIllegalAttributeCombination); + key.type = pxd_array | pxd_ubyte; + key.value.array.data = (byte *)&par->pv[aPatternSelectID]->value.i; + key.value.array.size = sizeof(int32_t); + if ( !(px_dict_find(&pxgs->temp_pattern_dict, &key, &value) || + px_dict_find(&pxs->page_pattern_dict, &key, &value) || + px_dict_find(&pxs->session_pattern_dict, &key, &value)) + ) + return_error(errorRasterPatternUndefined); + pattern = value; + px_set_halftone(pxs); + code = render_pattern(&ccolor, pattern, par->pv[aPatternOrigin], + par->pv[aNewDestinationSize], pxs); + /* + * We don't use px_paint_rc_adjust(... 1 ...) here, because + * gs_makepattern creates pattern instances with a reference + * count already set to 1. + */ + rc_increment(pattern); + if ( code < 0 ) + return code; + px_paint_rc_adjust(ppt, -1, pxs->memory); + ppt->type = pxpPattern; + ppt->value.pattern.pattern = pattern; + ppt->value.pattern.color = ccolor; + /* not pxaPatternSelectID but we have a pattern origin or + newdestination size */ + } else if ( par->pv[aPatternOrigin] || par->pv[aNewDestinationSize] ) { + return_error(errorIllegalAttributeCombination); + } else if ( par->pv[aRGBColor] ) { + const px_value_t *prgb = par->pv[aRGBColor]; + int i; + if ( par->pv[aGrayLevel] || par->pv[aNullBrushPen] ) + return_error(errorIllegalAttributeCombination); + if ( pxgs->color_space != eRGB && pxgs->color_space != eSRGB ) + return_error(errorColorSpaceMismatch); + px_paint_rc_adjust(ppt, -1, pxs->memory); + if ( pxs->useciecolor ) + ppt->type = pxpSRGB; + else + ppt->type = pxpRGB; + for ( i = 0; i < 3; ++i ) + if ( prgb->type & pxd_any_real ) + ppt->value.rgb[i] = real_elt(prgb, i); + else { + int32_t v = integer_elt(prgb, i); + ppt->value.rgb[i] = (v < 0 ? 0 : (real)v / int_type_max(prgb->type)); + } + } else if ( par->pv[aGrayLevel] ) /* pxaGrayLevel */ { + if ( par->pv[aNullBrushPen] ) + return_error(errorIllegalAttributeCombination); + if ( pxgs->color_space != eGray ) + return_error(errorColorSpaceMismatch); + px_paint_rc_adjust(ppt, -1, pxs->memory); + ppt->type = pxpGray; + ppt->value.gray = fraction_value(par->pv[aGrayLevel], 0); + } else if ( par->pv[aNullBrushPen] ) /* pxaNullBrush/Pen */ { + px_paint_rc_adjust(ppt, -1, pxs->memory); + ppt->type = pxpNull; + } else if ( par->pv[aPrimaryDepth] && par->pv[aPrimaryArray] ) { + px_paint_rc_adjust(ppt, -1, pxs->memory); + if ( pxgs->color_space == eRGB ) + if ( pxs->useciecolor ) + ppt->type = pxpSRGB; + else + ppt->type = pxpRGB; + else if ( pxgs->color_space == eGray ) + ppt->type = pxpGray; + else if ( pxgs->color_space == eSRGB ) + ppt->type = pxpSRGB; + else { + dprintf1("Warning unknown color space %d\n", pxgs->color_space); + ppt->type = pxpGray; + } + /* NB depth?? - for range checking */ + if ( ppt->type == pxpRGB || ppt->type == pxpSRGB ) { + + ppt->value.rgb[0] = (float)par->pv[aPrimaryArray]->value.array.data[0] / 255.0; + ppt->value.rgb[1] = (float)par->pv[aPrimaryArray]->value.array.data[1] / 255.0; + ppt->value.rgb[2] = (float)par->pv[aPrimaryArray]->value.array.data[2] / 255.0; + } else + /* NB figure out reals and ints */ + ppt->value.gray = (float)par->pv[aPrimaryArray]->value.array.data[0] / 255.0; + } else + return_error(errorMissingAttribute); + /* + * Update the halftone to the most recently set one. + * This will do the wrong thing if we set the brush or pen source, + * set the halftone, and then set the other source, but we have + * no way to handle this properly with the current library. + */ + if ( code >= 0 && px_needs_halftone(pxs->memory, ppt) ) + code = px_set_halftone(pxs); + return code; +} + +/* Set up a brush or pen for drawing. */ +/* If it is a pattern, SetBrush/PenSource guaranteed that it is compatible */ +/* with the current color space. */ +int +px_set_paint(const px_paint_t *ppt, px_state_t *pxs) +{ + gs_state *pgs = pxs->pgs; + px_paint_type_t type; + + if ( pxs->useciecolor && ppt->type == pxpRGB ) + type = pxpSRGB; + else + type = ppt->type; + switch ( type ) { + case pxpNull: + gs_setnullcolor(pgs); + return 0; + case pxpRGB: + return gs_setrgbcolor(pgs, ppt->value.rgb[0], ppt->value.rgb[1], + ppt->value.rgb[2]); + case pxpGray: + return gs_setgray(pgs, ppt->value.gray); + case pxpPattern: + return gs_setpattern(pgs, &ppt->value.pattern.color); + case pxpSRGB: + return pl_setSRGBcolor(pgs, + ppt->value.rgb[0], + ppt->value.rgb[1], + ppt->value.rgb[2]); + default: /* can't happen */ + return_error(errorIllegalAttributeValue); + } +} + +/* ---------------- Operators ---------------- */ + +const byte apxSetBrushSource[] = { + 0, pxaRGBColor, pxaGrayLevel, pxaPrimaryArray, pxaPrimaryDepth, pxaNullBrush, pxaPatternSelectID, + pxaPatternOrigin, pxaNewDestinationSize, 0 +}; + +int +pxSetBrushSource(px_args_t *par, px_state_t *pxs) +{ return set_source(par, pxs, &pxs->pxgs->brush); +} + +const byte apxSetColorSpace[] = { + 0, pxaColorSpace, pxaColorimetricColorSpace, pxaXYChromaticities, pxaWhiteReferencePoint, + pxaCRGBMinMax, pxaGammaGain, pxaPaletteDepth, pxaPaletteData, 0 +}; + +/* it appears the 4600 does not support CRGB define this to enable support */ +/* #define SUPPORT_COLORIMETRIC */ + +int +pxSetColorSpace(px_args_t *par, px_state_t *pxs) +{ px_gstate_t *pxgs = pxs->pxgs; + pxeColorSpace_t cspace; + + if ( par->pv[0] ) + cspace = par->pv[0]->value.i; + else if ( par->pv[1] ) + cspace = par->pv[1]->value.i; + else + return_error(errorIllegalAttributeValue); +#ifndef SUPPORT_COLORIMETRIC + if ( cspace == eCRGB ) + /* oddly the 4600 reports this a missing attribute, not + the expected illegal attribute */ + return_error(errorMissingAttribute); +#endif + /* substitute srgb if cie color is in effect */ + if ( ( cspace == eRGB ) && pxs->useciecolor ) + cspace = eSRGB; + if ( par->pv[6] && par->pv[7] ) + { int ncomp = (( cspace == eRGB || cspace == eSRGB || cspace == eCRGB ) ? 3 : 1); + uint size = par->pv[7]->value.array.size; + if ( !(size == ncomp << 1 || size == ncomp << 4 || + size == ncomp << 8) + ) + return_error(errorIllegalAttributeValue); + /* The palette is in an array, but we want a string. */ + { + if ( pxgs->palette.data && !pxgs->palette_is_shared && + pxgs->palette.size != size + ) + { gs_free_string(pxs->memory, (byte *)pxgs->palette.data, + pxgs->palette.size, + "pxSetColorSpace(old palette)"); + pxgs->palette.data = 0; + pxgs->palette.size = 0; + } + if ( pxgs->palette.data == 0 || pxgs->palette_is_shared ) + { byte *pdata = + gs_alloc_string(pxs->memory, size, + "pxSetColorSpace(palette)"); + + if ( pdata == 0 ) + return_error(errorInsufficientMemory); + pxgs->palette.data = pdata; + pxgs->palette.size = size; + } + memcpy((void *)pxgs->palette.data, par->pv[7]->value.array.data, size); + } + } + else if ( par->pv[6] || par->pv[7] ) + return_error(errorMissingAttribute); + else if ( pxgs->palette.data ) + { if ( !pxgs->palette_is_shared ) + gs_free_string(pxs->memory, (byte *)pxgs->palette.data, + pxgs->palette.size, + "pxSetColorSpace(old palette)"); + pxgs->palette.data = 0; + pxgs->palette.size = 0; + } + pxgs->palette_is_shared = false; + pxgs->color_space = cspace; +#ifndef SET_COLOR_SPACE_NO_SET_BLACK + { px_paint_rc_adjust(&pxgs->brush, -1, pxs->memory); + pxgs->brush.type = pxpGray; + pxgs->brush.value.gray = 0; + } + { px_paint_rc_adjust(&pxgs->pen, -1, pxs->memory); + pxgs->pen.type = pxpGray; + pxgs->pen.value.gray = 0; + } +#endif + return 0; +} + +const byte apxSetHalftoneMethod[] = { + 0, pxaDitherOrigin, pxaDeviceMatrix, pxaDitherMatrixDataType, + pxaDitherMatrixSize, pxaDitherMatrixDepth, pxaAllObjectTypes, + pxaTextObjects, pxaVectorObjects, pxaRasterObjects, 0 +}; + +int +pxSetHalftoneMethod(px_args_t *par, px_state_t *pxs) +{ gs_state *pgs = pxs->pgs; + px_gstate_t *pxgs = pxs->pxgs; + pxeDitherMatrix_t method; + + if ( par->pv[6] || par->pv[7] || par->pv[8] || par->pv[9] ) + /* ignore object type arguments */ + return 0; + + if ( par->pv[1] ) + { /* Internal halftone */ + if ( par->pv[2] || par->pv[3] || par->pv[4] ) + return_error(errorIllegalAttributeCombination); + method = par->pv[1]->value.i; + px_set_default_screen_size(pxs, method); + pxs->download_string.data = 0; + pxs->download_string.size = 0; + } + else if ( par->pv[2] && par->pv[3] && par->pv[4] ) + { /* Dither matrix */ + uint width = par->pv[3]->value.ia[0]; + uint source_width = (width + 3) & ~3; + uint height = par->pv[3]->value.ia[1]; + uint size = width * height; + uint source_size = source_width * height; + + if ( par->source.position == 0 ) + { byte *data; + if ( par->source.available == 0 ) + return pxNeedData; + data = gs_alloc_string(pxs->memory, size, "dither matrix"); + if ( data == 0 ) + return_error(errorInsufficientMemory); + pxs->download_string.data = data; + pxs->download_string.size = size; + } + while ( par->source.position < source_size ) + { uint source_x = par->source.position % source_width; + uint source_y = par->source.position / source_width; + uint used; + + if ( par->source.available == 0 ) + return pxNeedData; + if ( source_x >= width ) + { /* Skip padding bytes at end of row. */ + used = min(par->source.available, source_width - source_x); + } + else + { /* Read data. */ + const byte *src = par->source.data; + byte *dest = pxs->download_string.data; + uint i; + int skip; + + used = min(par->source.available, width - source_x); + /* + * The documentation doesn't say this, but we have to + * rotate the dither matrix to match the orientation, + * remembering that we have a Y-inverted coordinate + * system. This is quite a nuisance! + */ + switch ( pxs->orientation ) + { + case ePortraitOrientation: + dest += source_y * width + source_x; + skip = 1; + break; + case eLandscapeOrientation: + dest += (width - 1 - source_x) * height + source_y; + skip = -height; + break; + case eReversePortrait: + dest += (height - 1 - source_y) * width + + width - 1 - source_x; + skip = -1; + break; + case eReverseLandscape: + dest += source_x * height + width - 1 - source_y; + skip = height; + break; + default: + return -1; + } + for ( i = 0; i < used; ++i, ++src, dest += skip ) + *dest = *src; + } + par->source.position += used; + par->source.available -= used; + par->source.data += used; + + } + pxgs->halftone.width = width; + pxgs->halftone.height = height; + method = eDownloaded; + } + else + return_error(errorMissingAttribute); + if ( par->pv[0] ) + gs_transform(pgs, real_value(par->pv[0], 0), + real_value(par->pv[0], 1), &pxgs->halftone.origin); + else + gs_transform(pgs, 0.0, 0.0, &pxgs->halftone.origin); + pxgs->halftone.thresholds = pxs->download_string; + pxgs->halftone.method = method; + pxgs->halftone.set = false; + return 0; +} + +const byte apxSetPenSource[] = { + 0, pxaRGBColor, pxaGrayLevel, pxaPrimaryArray, pxaPrimaryDepth, pxaNullPen, pxaPatternSelectID, + pxaPatternOrigin, pxaNewDestinationSize, 0 +}; + +int +pxSetPenSource(px_args_t *par, px_state_t *pxs) +{ return set_source(par, pxs, &pxs->pxgs->pen); +} + +const byte apxSetColorTreatment[] = + {0, pxaColorTreatment, pxaAllObjectTypes, pxaTextObjects, pxaVectorObjects, pxaRasterObjects, 0}; + +int +pxSetColorTreatment(px_args_t *par, px_state_t *pxs) +{ + return 0; +} + +const byte apxSetNeutralAxis[] = {0, pxaAllObjectTypes, pxaTextObjects, + pxaVectorObjects, pxaRasterObjects, 0}; + +int +pxSetNeutralAxis(px_args_t *par, px_state_t *pxs) +{ + return 0; +} + +const byte apxSetColorTrapping[] = {pxaAllObjectTypes, 0, 0}; + +int +pxSetColorTrapping(px_args_t *par, px_state_t *pxs) +{ + return 0; +} + +const byte apxSetAdaptiveHalftoning[] = + {0, pxaAllObjectTypes, pxaTextObjects, pxaVectorObjects, pxaRasterObjects, 0}; + +int +pxSetAdaptiveHalftoning(px_args_t *par, px_state_t *pxs) +{ + return 0; +} + |