/* Copyright (C) 1997, 1998, 1999 artofcode LLC. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA, 02111-1307. */ /*$Id: zdpnext.c,v 1.3.6.1.2.1 2003/01/17 00:49:05 giles Exp $ */ /* NeXT Display PostScript extensions */ #include "math_.h" #include "ghost.h" #include "oper.h" #include "gscoord.h" #include "gscspace.h" /* for iimage.h */ #include "gsdpnext.h" #include "gsmatrix.h" #include "gsiparam.h" /* for iimage.h */ #include "gsiparm2.h" #include "gspath2.h" #include "gxcvalue.h" #include "gxdevice.h" #include "gxsample.h" #include "ialloc.h" #include "igstate.h" #include "iimage.h" #include "iimage2.h" #include "store.h" /* ------ alpha channel ------ */ /* - currentalpha */ private int zcurrentalpha(i_ctx_t *i_ctx_p) { os_ptr op = osp; push(1); make_real(op, gs_currentalpha(igs)); return 0; } /* setalpha - */ private int zsetalpha(i_ctx_t *i_ctx_p) { os_ptr op = osp; double alpha; int code; if (real_param(op, &alpha) < 0) return_op_typecheck(op); if ((code = gs_setalpha(igs, alpha)) < 0) return code; pop(1); return 0; } /* ------ Imaging/compositing ------ */ /* * Miscellaneous notes: * * composite / dissolve respect destination clipping (both clip & viewclip), * but ignore source clipping. * composite / dissolve must handle overlapping source/destination correctly. * compositing converts the source to the destination's color model * (including halftoning if needed). */ /* Imported procedures */ int zimage_multiple(P2(i_ctx_t *i_ctx_p, bool has_alpha)); /* in zcolor1.c */ /* * Define the operand and bookeeping structure for a compositing operation. */ typedef struct alpha_composite_state_s { /* Compositing parameters */ gs_composite_alpha_params_t params; /* Temporary structures */ gs_composite_t *pcte; gx_device *cdev; gx_device *orig_dev; } alpha_composite_state_t; /* Forward references */ private int begin_composite(P2(i_ctx_t *, alpha_composite_state_t *)); private void end_composite(P2(i_ctx_t *, alpha_composite_state_t *)); private int xywh_param(P2(os_ptr, double[4])); /* */ /* ... true alphaimage - */ /* false alphaimage - */ private int zalphaimage(i_ctx_t *i_ctx_p) { /* Essentially the whole implementation is shared with colorimage. */ return zimage_multiple(i_ctx_p, true); } /* compositerect - */ private int zcompositerect(i_ctx_t *i_ctx_p) { os_ptr op = osp; double dest_rect[4]; alpha_composite_state_t cstate; int code = xywh_param(op - 1, dest_rect); if (code < 0) return code; check_int_leu(*op, compositerect_last); cstate.params.op = (gs_composite_op_t) op->value.intval; code = begin_composite(i_ctx_p, &cstate); if (code < 0) return code; { gs_rect rect; rect.q.x = (rect.p.x = dest_rect[0]) + dest_rect[2]; rect.q.y = (rect.p.y = dest_rect[1]) + dest_rect[3]; code = gs_rectfill(igs, &rect, 1); } end_composite(i_ctx_p, &cstate); if (code >= 0) pop(5); return code; } /* Common code for composite and dissolve. */ private int composite_image(i_ctx_t *i_ctx_p, const gs_composite_alpha_params_t * params) { os_ptr op = osp; alpha_composite_state_t cstate; gs_image2_t image; double src_rect[4]; double dest_pt[2]; gs_matrix save_ctm; int code = xywh_param(op - 4, src_rect); cstate.params = *params; gs_image2_t_init(&image); if (code < 0 || (code = num_params(op - 1, 2, dest_pt)) < 0 ) return code; if (r_has_type(op - 3, t_null)) image.DataSource = igs; else { check_stype(op[-3], st_igstate_obj); check_read(op[-3]); image.DataSource = igstate_ptr(op - 3); } image.XOrigin = src_rect[0]; image.YOrigin = src_rect[1]; image.Width = src_rect[2]; image.Height = src_rect[3]; image.PixelCopy = true; /* Compute appropriate transformations. */ gs_currentmatrix(igs, &save_ctm); gs_translate(igs, dest_pt[0], dest_pt[1]); gs_make_identity(&image.ImageMatrix); if (image.DataSource == igs) { image.XOrigin -= dest_pt[0]; image.YOrigin -= dest_pt[1]; } code = begin_composite(i_ctx_p, &cstate); if (code >= 0) { code = process_non_source_image(i_ctx_p, (const gs_image_common_t *)&image, "composite_image"); end_composite(i_ctx_p, &cstate); if (code >= 0) pop(8); } gs_setmatrix(igs, &save_ctm); return code; } /* */ /* composite - */ private int zcomposite(i_ctx_t *i_ctx_p) { os_ptr op = osp; gs_composite_alpha_params_t params; check_int_leu(*op, composite_last); params.op = (gs_composite_op_t) op->value.intval; return composite_image(i_ctx_p, ¶ms); } /* */ /* dissolve - */ private int zdissolve(i_ctx_t *i_ctx_p) { os_ptr op = osp; gs_composite_alpha_params_t params; double delta; int code = real_param(op, &delta); if (code < 0) return code; if (delta < 0 || delta > 1) return_error(e_rangecheck); params.op = composite_Dissolve; params.delta = delta; return composite_image(i_ctx_p, ¶ms); } /* ------ Image reading ------ */ private int device_is_true_color(P1(gx_device * dev)); /* .sizeimagebox */ /* */ private void box_confine(P3(int *pp, int *pq, int wh)); private int zsizeimagebox(i_ctx_t *i_ctx_p) { os_ptr op = osp; const gx_device *dev = gs_currentdevice(igs); gs_rect srect, drect; gs_matrix mat; gs_int_rect rect; int w, h; int code; check_type(op[-4], t_integer); check_type(op[-3], t_integer); check_type(op[-2], t_integer); check_type(op[-1], t_integer); srect.p.x = op[-4].value.intval; srect.p.y = op[-3].value.intval; srect.q.x = srect.p.x + op[-2].value.intval; srect.q.y = srect.p.y + op[-1].value.intval; gs_currentmatrix(igs, &mat); gs_bbox_transform(&srect, &mat, &drect); /* * We want the dimensions of the image as a source, not a * destination, so we need to expand it rather than pixround. */ rect.p.x = (int)floor(drect.p.x); rect.p.y = (int)floor(drect.p.y); rect.q.x = (int)ceil(drect.q.x); rect.q.y = (int)ceil(drect.q.y); /* * Clip the rectangle to the device boundaries, since that's what * the NeXT implementation does. */ box_confine(&rect.p.x, &rect.q.x, dev->width); box_confine(&rect.p.y, &rect.q.y, dev->height); w = rect.q.x - rect.p.x; h = rect.q.y - rect.p.y; /* * The NeXT documentation doesn't specify very clearly what is * supposed to be in the matrix: the following produces results * that match testing on an actual NeXT system. */ mat.tx -= rect.p.x; mat.ty -= rect.p.y; code = write_matrix(op, &mat); if (code < 0) return code; make_int(op - 4, rect.p.x); make_int(op - 3, rect.p.y); make_int(op - 2, w); make_int(op - 1, h); return 0; } private void box_confine(int *pp, int *pq, int wh) { if ( *pq <= 0 ) *pp = *pq = 0; else if ( *pp >= wh ) *pp = *pq = wh; else { if ( *pp < 0 ) *pp = 0; if ( *pq > wh ) *pq = wh; } } /* - .sizeimageparams */ private int zsizeimageparams(i_ctx_t *i_ctx_p) { os_ptr op = osp; gx_device *dev = gs_currentdevice(igs); int ncomp = dev->color_info.num_components; int bps; push(3); if (device_is_true_color(dev)) bps = dev->color_info.depth / ncomp; else { /* * Set bps to the smallest allowable number of bits that is * sufficient to represent the number of different colors. */ gx_color_value max_value = (dev->color_info.num_components == 1 ? dev->color_info.max_gray : max(dev->color_info.max_gray, dev->color_info.max_color)); static const gx_color_value sizes[] = { 1, 2, 4, 8, 12, sizeof(gx_max_color_value) * 8 }; int i; for (i = 0;; ++i) if (max_value <= ((ulong) 1 << sizes[i]) - 1) break; bps = sizes[i]; } make_int(op - 2, bps); make_false(op - 1); make_int(op, ncomp); return 0; } /* ------ Initialization procedure ------ */ const op_def zdpnext_op_defs[] = { {"0currentalpha", zcurrentalpha}, {"1setalpha", zsetalpha}, {"7alphaimage", zalphaimage}, {"8composite", zcomposite}, {"5compositerect", zcompositerect}, {"8dissolve", zdissolve}, {"5.sizeimagebox", zsizeimagebox}, {"0.sizeimageparams", zsizeimageparams}, op_def_end(0) }; /* ------ Internal routines ------ */ /* Collect a rect operand. */ private int xywh_param(os_ptr op, double rect[4]) { int code = num_params(op, 4, rect); if (code < 0) return code; if (rect[2] < 0) rect[0] += rect[2], rect[2] = -rect[2]; if (rect[3] < 0) rect[1] += rect[3], rect[3] = -rect[3]; return code; } /* Begin a compositing operation. */ private int begin_composite(i_ctx_t *i_ctx_p, alpha_composite_state_t * pcp) { gx_device *dev = gs_currentdevice(igs); int code = gs_create_composite_alpha(&pcp->pcte, &pcp->params, imemory); if (code < 0) return code; pcp->orig_dev = pcp->cdev = dev; /* for end_composite */ code = (*dev_proc(dev, create_compositor)) (dev, &pcp->cdev, pcp->pcte, (const gs_imager_state *)igs, imemory); if (code < 0) { end_composite(i_ctx_p, pcp); return code; } gs_setdevice_no_init(igs, pcp->cdev); return 0; } /* End a compositing operation. */ private void end_composite(i_ctx_t *i_ctx_p, alpha_composite_state_t * pcp) { /* Close and free the compositor and the compositing object. */ if (pcp->cdev != pcp->orig_dev) { gs_closedevice(pcp->cdev); /* also frees the device */ gs_setdevice_no_init(igs, pcp->orig_dev); } ifree_object(pcp->pcte, "end_composite(gs_composite_t)"); } /* * Determine whether a device has decomposed pixels with the components * in the standard PostScript order, and a 1-for-1 color map * (possibly inverted). Return 0 if not true color, 1 if true color, * -1 if inverted true color. */ private int device_is_true_color(gx_device * dev) { int ncomp = dev->color_info.num_components; int depth = dev->color_info.depth; int i, max_v; #define CV(i) (gx_color_value)((ulong)gx_max_color_value * i / max_v) #define CV0 ((gx_color_value)0) /****** DOESN'T HANDLE INVERSION YET ******/ switch (ncomp) { case 1: /* gray-scale */ max_v = dev->color_info.max_gray; if (max_v != (1 << depth) - 1) return 0; for (i = 0; i <= max_v; ++i) { gx_color_value v = CV(i); if ((*dev_proc(dev, map_rgb_color)) (dev, v, v, v) != i) return 0; } return true; case 3: /* RGB */ max_v = dev->color_info.max_color; if (depth % 3 != 0 || max_v != (1 << (depth / 3)) - 1) return false; { const int gs = depth / 3, rs = gs * 2; for (i = 0; i <= max_v; ++i) { gx_color_value v = CV(i); if ((*dev_proc(dev, map_rgb_color)) (dev, v, CV0, CV0) != i << rs || (*dev_proc(dev, map_rgb_color)) (dev, CV0, v, CV0) != i << gs || (*dev_proc(dev, map_rgb_color)) (dev, CV0, CV0, v) != i /*<< bs */ ) return 0; } } return true; case 4: /* CMYK */ max_v = dev->color_info.max_color; if ((depth & 3) != 0 || max_v != (1 << (depth / 4)) - 1) return false; { const int ys = depth / 4, ms = ys * 2, cs = ys * 3; for (i = 0; i <= max_v; ++i) { gx_color_value v = CV(i); if ((*dev_proc(dev, map_cmyk_color)) (dev, v, CV0, CV0, CV0) != i << cs || (*dev_proc(dev, map_cmyk_color)) (dev, CV0, v, CV0, CV0) != i << ms || (*dev_proc(dev, map_cmyk_color)) (dev, CV0, CV0, v, CV0) != i << ys || (*dev_proc(dev, map_cmyk_color)) (dev, CV0, CV0, CV0, v) != i /*<< ks */ ) return 0; } } return 1; default: return 0; /* DeviceN */ } #undef CV #undef CV0 }