/* Copyright (C) 2000 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: gstrans.c,v 1.13.2.1.2.1 2003/01/17 00:49:03 giles Exp $ */ /* Implementation of transparency, other than rendering */ #include "gx.h" #include "gserrors.h" #include "gstrans.h" #include "gsutil.h" #include "gzstate.h" #include "gxdevcli.h" /* ------ Transparency-related graphics state elements ------ */ int gs_setblendmode(gs_state *pgs, gs_blend_mode_t mode) { #ifdef DEBUG if (gs_debug_c('v')) { static const char *const bm_names[] = { GS_BLEND_MODE_NAMES }; dlprintf1("[v](0x%lx)blend_mode = ", (ulong)pgs); if (mode >= 0 && mode < countof(bm_names)) dprintf1("%s\n", bm_names[mode]); else dprintf1("%d??\n", (int)mode); } #endif if (mode < 0 || mode > MAX_BLEND_MODE) return_error(gs_error_rangecheck); pgs->blend_mode = mode; return 0; } gs_blend_mode_t gs_currentblendmode(const gs_state *pgs) { return pgs->blend_mode; } int gs_setopacityalpha(gs_state *pgs, floatp alpha) { if_debug2('v', "[v](0x%lx)opacity.alpha = %g\n", (ulong)pgs, alpha); pgs->opacity.alpha = (alpha < 0.0 ? 0.0 : alpha > 1.0 ? 1.0 : alpha); return 0; } float gs_currentopacityalpha(const gs_state *pgs) { return pgs->opacity.alpha; } int gs_setshapealpha(gs_state *pgs, floatp alpha) { if_debug2('v', "[v](0x%lx)shape.alpha = %g\n", (ulong)pgs, alpha); pgs->shape.alpha = (alpha < 0.0 ? 0.0 : alpha > 1.0 ? 1.0 : alpha); return 0; } float gs_currentshapealpha(const gs_state *pgs) { return pgs->shape.alpha; } int gs_settextknockout(gs_state *pgs, bool knockout) { if_debug2('v', "[v](0x%lx)text_knockout = %s\n", (ulong)pgs, (knockout ? "true" : "false")); pgs->text_knockout = knockout; return 0; } bool gs_currenttextknockout(const gs_state *pgs) { return pgs->text_knockout; } /* ------ Transparency rendering stack ------ */ /* This area of the transparency facilities is in flux. Here is a proposal for extending the driver interface. The material below will eventually go in gxdevcli.h. */ /* Push the current transparency state (*ppts) onto the associated stack, and set *ppts to a new transparency state of the given dimension. The transparency state may copy some or all of the imager state, such as the current alpha and/or transparency mask values, and definitely copies the parameters. */ #define dev_t_proc_begin_transparency_group(proc, dev_t)\ int proc(P6(gx_device *dev,\ const gs_transparency_group_params_t *ptgp,\ const gs_rect *pbbox,\ gs_imager_state *pis,\ gs_transparency_state_t **ppts,\ gs_memory_t *mem)) #define dev_proc_begin_transparency_group(proc)\ dev_t_proc_begin_transparency_group(proc, gx_device) /* End a transparency group: blend the top element of the transparency stack, which must be a group, into the next-to-top element, popping the stack. If the stack only had a single element, blend into the device output. Set *ppts to 0 iff the stack is now empty. If end_group fails, the stack is *not* popped. */ #define dev_t_proc_end_transparency_group(proc, dev_t)\ int proc(P3(gx_device *dev,\ gs_imager_state *pis,\ gs_transparency_state_t **ppts)) #define dev_proc_end_transparency_group(proc)\ dev_t_proc_end_transparency_group(proc, gx_device) /* Push the transparency state and prepare to render a transparency mask. This is similar to begin_transparency_group except that it only accumulates coverage values, not full pixel values. */ #define dev_t_proc_begin_transparency_mask(proc, dev_t)\ int proc(P6(gx_device *dev,\ const gs_transparency_mask_params_t *ptmp,\ const gs_rect *pbbox,\ gs_imager_state *pis,\ gs_transparency_state_t **ppts,\ gs_memory_t *mem)) #define dev_proc_begin_transparency_mask(proc)\ dev_t_proc_begin_transparency_mask(proc, gx_device) /* Store a pointer to the rendered transparency mask into *pptm, popping the stack like end_group. Normally, the client will follow this by using rc_assign to store the rendered mask into pis->{opacity,shape}.mask. If end_mask fails, the stack is *not* popped. */ #define dev_t_proc_end_transparency_mask(proc, dev_t)\ int proc(P2(gx_device *dev,\ gs_transparency_mask_t **pptm)) #define dev_proc_end_transparency_mask(proc)\ dev_t_proc_end_transparency_mask(proc, gx_device) /* Pop the transparency stack, discarding the top element, which may be either a group or a mask. Set *ppts to 0 iff the stack is now empty. */ #define dev_t_proc_discard_transparency_layer(proc, dev_t)\ int proc(P2(gx_device *dev,\ gs_transparency_state_t **ppts)) #define dev_proc_discard_transparency_layer(proc)\ dev_t_proc_discard_transparency_layer(proc, gx_device) /* (end of proposed driver interface extensions) */ gs_transparency_state_type_t gs_current_transparency_type(const gs_state *pgs) { return (pgs->transparency_stack == 0 ? 0 : pgs->transparency_stack->type); } /* Support for dummy implementation */ gs_private_st_ptrs1(st_transparency_state, gs_transparency_state_t, "gs_transparency_state_t", transparency_state_enum_ptrs, transparency_state_reloc_ptrs, saved); private int push_transparency_stack(gs_state *pgs, gs_transparency_state_type_t type, client_name_t cname) { gs_transparency_state_t *pts = gs_alloc_struct(pgs->memory, gs_transparency_state_t, &st_transparency_state, cname); if (pts == 0) return_error(gs_error_VMerror); pts->saved = pgs->transparency_stack; pts->type = type; pgs->transparency_stack = pts; return 0; } private void pop_transparency_stack(gs_state *pgs, client_name_t cname) { gs_transparency_state_t *pts = pgs->transparency_stack; /* known non-0 */ gs_transparency_state_t *saved = pts->saved; gs_free_object(pgs->memory, pts, cname); pgs->transparency_stack = saved; } void gs_trans_group_params_init(gs_transparency_group_params_t *ptgp) { ptgp->ColorSpace = 0; /* bogus, but can't do better */ ptgp->Isolated = false; ptgp->Knockout = false; } int gs_begin_transparency_group(gs_state *pgs, const gs_transparency_group_params_t *ptgp, const gs_rect *pbbox) { /****** NYI, DUMMY ******/ #ifdef DEBUG if (gs_debug_c('v')) { static const char *const cs_names[] = { GS_COLOR_SPACE_TYPE_NAMES }; dlprintf5("[v](0x%lx)begin_transparency_group [%g %g %g %g]\n", (ulong)pgs, pbbox->p.x, pbbox->p.y, pbbox->q.x, pbbox->q.y); if (ptgp->ColorSpace) dprintf1(" CS = %s", cs_names[(int)gs_color_space_get_index(ptgp->ColorSpace)]); else dputs(" (no CS)"); dprintf2(" Isolated = %d Knockout = %d\n", ptgp->Isolated, ptgp->Knockout); } #endif return (*dev_proc(pgs->device, begin_transparency_group)) (pgs->device, ptgp, pbbox, (gs_imager_state *)pgs, NULL, NULL); #if 0 return push_transparency_stack(pgs, TRANSPARENCY_STATE_Group, "gs_begin_transparency_group"); #endif } int gs_end_transparency_group(gs_state *pgs) { /****** NYI, DUMMY ******/ return (*dev_proc(pgs->device, end_transparency_group)) (pgs->device, (gs_imager_state *)pgs, NULL); #if 0 gs_transparency_state_t *pts = pgs->transparency_stack; if_debug1('v', "[v](0x%lx)end_transparency_group\n", (ulong)pgs); if (!pts || pts->type != TRANSPARENCY_STATE_Group) return_error(gs_error_rangecheck); pop_transparency_stack(pgs, "gs_end_transparency_group"); return 0; #endif } private int mask_transfer_identity(floatp in, float *out, void *proc_data) { *out = in; return 0; } void gs_trans_mask_params_init(gs_transparency_mask_params_t *ptmp, gs_transparency_mask_subtype_t subtype) { ptmp->subtype = subtype; ptmp->has_Background = false; ptmp->TransferFunction = mask_transfer_identity; ptmp->TransferFunction_data = 0; } int gs_begin_transparency_mask(gs_state *pgs, const gs_transparency_mask_params_t *ptmp, const gs_rect *pbbox) { /****** NYI, DUMMY ******/ if_debug8('v', "[v](0x%lx)begin_transparency_mask [%g %g %g %g]\n\ subtype = %d has_Background = %d %s\n", (ulong)pgs, pbbox->p.x, pbbox->p.y, pbbox->q.x, pbbox->q.y, (int)ptmp->subtype, ptmp->has_Background, (ptmp->TransferFunction == mask_transfer_identity ? "no TR" : "has TR")); return push_transparency_stack(pgs, TRANSPARENCY_STATE_Mask, "gs_begin_transparency_group"); } int gs_end_transparency_mask(gs_state *pgs, gs_transparency_channel_selector_t csel) { /****** NYI, DUMMY ******/ gs_transparency_state_t *pts = pgs->transparency_stack; if_debug2('v', "[v](0x%lx)end_transparency_mask(%d)\n", (ulong)pgs, (int)csel); if (!pts || pts->type != TRANSPARENCY_STATE_Mask) return_error(gs_error_rangecheck); pop_transparency_stack(pgs, "gs_end_transparency_mask"); return 0; } int gs_discard_transparency_layer(gs_state *pgs) { /****** NYI, DUMMY ******/ gs_transparency_state_t *pts = pgs->transparency_stack; if_debug1('v', "[v](0x%lx)discard_transparency_layer\n", (ulong)pgs); if (!pts) return_error(gs_error_rangecheck); pop_transparency_stack(pgs, "gs_discard_transparency_layer"); return 0; } int gs_init_transparency_mask(gs_state *pgs, gs_transparency_channel_selector_t csel) { /****** NYI, DUMMY ******/ gs_transparency_source_t *ptm; if_debug2('v', "[v](0x%lx)init_transparency_mask(%d)\n", (ulong)pgs, (int)csel); switch (csel) { case TRANSPARENCY_CHANNEL_Opacity: ptm = &pgs->opacity; break; case TRANSPARENCY_CHANNEL_Shape: ptm = &pgs->shape; break; default: return_error(gs_error_rangecheck); } rc_decrement_only(ptm->mask, "gs_init_transparency_mask"); ptm->mask = 0; return 0; }