% Copyright (C) 2002 Aladdin Enterprises. All rights reserved. % % This software is provided AS-IS with no warranty, either express or % implied. % % This software is distributed under license and may not be copied, % modified or distributed except as expressly authorized under the terms % of the license contained in the file LICENSE in this distribution. % % For more information about licensing, please refer to % http://www.ghostscript.com/licensing/. For information on % commercial licensing, go to http://www.artifex.com/licensing/ or % contact Artifex Software, Inc., 101 Lucas Valley Road #110, % San Rafael, CA 94903, U.S.A., +1(415)492-9861. % $Id: gs_cspace.ps,v 1.6 2003/06/26 22:42:33 dan Exp $ % basic colorspace mechanism % % This new implementation of color spaces extends the color space % formalism to all PostScript levels. Level specific features and % operators continue to be accessible only in the appropriate level, % but the colorspace concept and associated mechanisms are used % throughout. % % The color space mechanism is built around two dictionaries: % % .cspace_util % A dictionary in global VM that is accessible in userdict only % during initialization. This dictionary is intended for various % utility procedures that are used in implementing the individual % color spaces. % % colorspacedict % A dictionary of methods for each color space type. The keys % in this dictionary are color space type names (e.g.: /DeviceGray, % /Separation, etc.), and the values are dictionaries of methods. % The set of methods is the same for each color space type, and % provides a complete implementation for the corresponding color % space type. This dictionary is in global VM. % % The information specific to a color space type is created in a file % for that type or group of types (e.g.: gs_csdev.ps, gs_csindx.ps, % etc.). These files will generally adhere to the template: % % .currentglobal true .setglobal % begin % ... % .cspace_util begin % colorspacedict % / % mark % /cs_validate % { % ... % } % bind % ... % .dicttomark % put % end % .cspace_util % end ... % level-specific dictionary % .setglobal % % The methods associated with a color space are listed below (along with % their stack handling), followed by descriptions. % % - cs_potential_indexed_base % % - cs_potential_pattern_base % % - cs_potential_alternate % % - cs_potential_icc_alternate % % % cs_get_ncomps % % cs_get_range % % cs_get_default_color ... % % % ... cs_get_currentgray % % ... cs_get_currentrgb % % ... cs_get_currentcmyk % % % % cs_validate % % cs_substitute % % cs_prepare % % cs_install - % % % ... cs_verify_color ... % % cs_complete_color - % % % cs_potential_indexed_base, cs_potential_pattern_base, % cs_potential_alternate, cs_potential_icc_alternate % These are booleans rather than procedures. They indicate if the color % space can be a base space of an Indexed color space (anything except % Indexed and Pattern), a Pattern color space (anything except Pattern), % the alternative color space of a Separation or DeviceN color space, or % the alternative color space of an ICCBased color space. The two % parameters are distinct only because of a Ghostscript-specific % implementation problem; in principle, there is nothing special about % ICCBased color spaces in this regard. % % cs_get_ncomps % Return the number of color components for the color spaces. For Pattern % color spaces, the value is -1 if there is no base space, or -(n + 1) if % the base space has n components. % % cs_get_range % Return the input Range array appropriate for this color space. This is % defined for all color spaces, though it is of interest primarily for % CIEBased and ICCBased color spaces. For Indexed color spaces this is % [ 0 hival ], where hival is the maximum support index value. For all % other non-CIEBased, non-ICCBased color spaces, the range is an array % of ncomps elements, all of which are [ 0 1 ], where ncomps is the % number of color space components. % % cs_get_default_color % Generates the default color for the current color space. Under normal % circumstances this is done internally. It is provided in PostScript % only to support an optimization that doesn't change the current color % space more often than necessary. % % cs_get_currentgray, cs_get_currentrgb, cs_get_currentcmyk % These procedures are used to implement the currentgray, currentrgb, % and currentcmyk operators (which are pseudo-operators in the current % implementation). % % cs_validate % Validate the operand color space. Because color spaces are extensively % manipulated in PostScript in this implementation, error handling can % become burdensome. To make the code somewhat simpler, it is useful to % be able to validate a color space prior to manipulation, so as to % ensure that errors are not discovered in awkward places. % % cs_substitute % Substitute a device-independent color space for device specific color % space. This applies directly to the device-specific color spaces % (DeviceGray, DeviceRGB, DeviceCMYK), and indirectly when these color % spaces are used as base/alternative color spaces. The mechanism for % color substitution is included in all language levels, though it may % only be accessed for Language Level 3. % % The substituted color space is the topmost of the operands pushed. % this may or may not be the same as the original color space, which % is immediately below it on the operand stack. If the two differ, % the substituted space will always be in local VM (and will be % writable). % % Substitution is applied recursively to the base/alternate color % space of ICCBased, Indexed, Separation, DeviceN, or Pattern % color spaces. Because Ghostscript currently requires that any % base or alternative color space be the current color space when % the enclosing color space is set, this substitution effectively % occurs twice: once in the original color space, and once when the % base/alternative color space is made the current color space. % We retain the first substitution as we would eventually like to % remove the restriction on making the base/alternative color space % the current color space. % % cs_prepare % Perform any operations required on the color space for installation. % This method exists primarily to allow conversion of PostScript % procedures to functions for CIEBased color spaces. Two operands are % provided: the original and the substituted color space. If the two % differ and the latter is writable, required modifications can % be made "in place". Otherwise, a new instance of the second color % space must be built. % % Currently, cs_prepare is not explicitly recursive. Because % Ghostscript requires a base/alternate color space to be installed % as the current color space prior to installing the enclosing color % space, the cs_prepare method will implicitly be called recursively. % The reason for not making this explicit is that color space % preparation may involve a considerable amount of work, which could % be avoided if, for example, an alternative color space will not % be used because the enclosing Separation/DeviceN color space is % supported in native mode by the process color model. We would % eventually like to remove the need to prepare color spaces that % will not be used. % % cs_install % This method actually installs the color space in the graphic state. % Only the substituted/prepared space (which may be the same as the % original space) is passed as an operand; the original space is handled % directly by the .setcolorspace operator. % % The provision of a separate method for this tasks reflects the % historical implementation of color spaces in the Ghostscript % interpreter. This implementation provides a unique operator for each % color space type. % % cs_prepare_color % Modify a set of color operands as required by a color space. This % is used primarily to verify the color operands, as this is most % conveniently done in PostScript. % % cs_complete_setcolor % This method is invoked immediately after a (successful) invocation % of setcolor. Ii is provided as a separate method for compatibility % with Adobe implementations. These implementations invoke the lookup % (Indexed) or tint procedure each time setcolor is invoked (only if % the alternative color space is used in the case of the tint % transform). Because Ghostscript may convert these procedures to % functions (or pre-sample them), the procedures may not always be % called when expected. There are applications that depend on this % behavior (e.g.: Adobe PhotoShop 5+), so this method provides a way % to emulate it. % % In principle, a cs_complete_setcolor procedure for an Indexed color % space whose base space should invoke cs_complete_setcolor on its % base space. Currently we don't do this, because it has not been % shown to be necessary. It would be simple to add if it is every % needed. % % All of these methods are procedures. % % For each of these methods, there is a procedure in .cspace_util with % a dot ('.') prefix that will invoke the appropriate procedure for the % operand array. % .currentglobal true .setglobal userdict /.cspace_util 80 dict put .cspace_util begin % % Colorspacedict is initially in .cspace_util; it is copied to level2dict % in the Level 2 initialization code to retain compatibility with % earlier implementations. % /colorspacedict 20 dict def % % make_array1 % % procedure for conditionally converting a named color space to a % 1-element array. Since names are always global, the array will be % as well. % /make_array1 { dup type /nametype eq { currentglobal true setglobal exch 1 array astore exch setglobal } if } bind def % % .get_cspace_type name % % Provide generic routine for retrieving the color space type. % /.get_cspace_type { dup type dup /arraytype eq exch /packedarraytype eq or { 0 get } if } bind def % % .get_method_dict % % Get the method dictionary for a specific color space. Note that the % color space is left on the stack. % /.get_method_dict { //colorspacedict exch //.get_cspace_type exec get } bind def % % .get_method % % Get the named method for the operand color space. % /.get_method { exch //.get_method_dict exec exch get } bind def % % .cs_potential_indexed_base % .cs_potential_pattern_base % .cs_potential_alternate % .cs_potential_icc_alternate % .cs_get_ncomps % .cs_get_range % .cs_get_default_color ... % ... .cs_get_currentgray % ... .cs_get_currentrgb % ... .cs_get_currentcmyk % .cs_validate % .cs_substitute % .cs_prepare % .cs_install - % ... .cs_prepare_color ... % .cs_complete_setcolor - % % These procedures provide access to the corresponding methods of the % operand color space. % /.cs_potential_indexed_base { /cs_potential_indexed_base //.get_method exec } bind def /.cs_potential_pattern_base { /cs_potential_pattern_base //.get_method exec } bind def /.cs_potential_alternate { /cs_potential_alternate //.get_method exec } bind def /.cs_potential_icc_alternate { /cs_potential_icc_alternate //.get_method exec } bind def /.cs_get_ncomps { dup /cs_get_ncomps //.get_method exec exec } bind def /.cs_get_range { dup /cs_get_range //.get_method exec exec } bind def /.cs_get_default_color { dup /cs_get_default_color //.get_method exec exec } bind def /.cs_get_currentgray { dup /cs_get_currentgray //.get_method exec exec } bind def /.cs_get_currentrgb { dup /cs_get_currentrgb //.get_method exec exec } bind def /.cs_get_currentcmyk { dup /cs_get_currentcmyk //.get_method exec exec } bind def /.cs_validate { dup /cs_validate //.get_method exec exec } bind def /.cs_substitute { dup /cs_substitute //.get_method exec exec } bind def /.cs_prepare { dup /cs_prepare //.get_method exec exec } bind def /.cs_install { dup /cs_install //.get_method exec exec } bind def /.cs_prepare_color { dup /cs_prepare_color //.get_method exec exec } bind def /.cs_complete_setcolor { dup /cs_complete_setcolor //.get_method exec exec } bind def % % Make sure we have an interpreter color space before redefining % setcolorspace. The interpreter internal code only sets the effective % color space; the interpreters color spaces begins as a null object. % % NB: This should come prior to the redefinition of setcolorspace, and % must use an array operand. % [ /DeviceGray ] setcolorspace % % ... setcolor - % % As with setcolorspace, setcolor is initially placed in .cspace_util, % and is copied to level2dict by the Level 2 initialization code. The % internal definition of setcolor is removed from systemdict as soon % as this procedure is defined. % /setcolor { { currentcolorspace //.cs_prepare_color exec //setcolor currentcolorspace //.cs_complete_setcolor exec } stopped { //.cspace_util /setcolor get $error /errorname get signalerror } if } bind odef systemdict /setcolor .undef % % _setcolorspace - % _setcolorspace_nosub - % % setcolorspace - % forcesetcolorspace - % % setcolorspace is initially placed in .cspace_util. It is copied to % level2dict by the Level 2 initialization code. The internal % setcolorspace operator is removed from systemdict as soon as this % procedure is defined. % % Because some jobs, in particular PDF jobs, repeatedly set the same % color space, this procedure will check if the operand and current % color spaces are the same. The check is absolute for parameterless % color spaces, conservative for others. For PostScript, this % optimization can only be employed if color space substitution is % disabled, as otherwise there is no way to account for possible changes % in the /Default* instances of the ColorSpace resource category. For PDF % jobs, resource category instances can only be changed at very specific % times (typically page boundaries), so the "operand color space is the % same as current color space" optimization may be used even if color % space substitution is in effect. The optimization is also highly % desirable in such cases, as it greatly improves performance. % % In certain situations, it is critical that a color space be set, % even if it is the same as the current color space. This is the case % when a CIEBased color space is used as a base or alternative color % space, due to some long-standing problems with the graphics libraries % handling of sampled information from the procedures in CIE color % spaces and the color rendering dictionary. The forcesetcolorspace % operator is provided for those situations. % % Note also that, if the current color space is not reset, at least % the current color must be reset to its default value. % % Another problem arises in the case of ICCBased color spaces. These % color spaces may be used to substitute for a DeviceGray/DeviceRGB/ % DeviceCMYK color space, and may themselves require such a color % space as an alternate. Obviously, when this is the case the normal % setcolorspace mechanism would encounter and infinite loop if the % alternate colro space needed to be used. For this particular case, % the special _setcolorspace_nosub is provided, which suppresses % color space substitution. This routine does not bother to check if % the operand and current color space are the same. % /_setcolorspace { { % see if the operand space is the same as the current space currentcolorspace dup length 1 eq { 0 get 2 index dup type dup /arraytype eq exch /packedarraytype eq or { dup length 1 eq { 0 get } if } if } { 2 index } ifelse eq and dup { % % If PDFfile is defined on the dictionary stack, this is a % PDF job. No additional check is required in this case (see % comment above). % /PDFfile where { pop } { .getuseciecolor not and } % check that UseCIEColor is off ifelse } if { //.cs_get_default_color exec setcolor } { //.cs_validate exec //.cs_substitute exec //.cs_prepare exec //.cs_install exec //make_array1 exec //setcolorspace } ifelse } stopped { //.cspace_util /setcolorspace get $error /errorname get signalerror } if } bind def /_setcolorspace_nosub { { //.cs_validate exec dup //.cs_prepare exec //.cs_install exec //make_array1 exec //setcolorspace } stopped { //.cspace_util /setcolorspace get $error /errorname get signalerror } if } bind def /setcolorspace { //true //_setcolorspace exec } bind odef /forcesetcolorspace { //false //_setcolorspace exec } bind odef % % - initgraphics - % % The initgraphics operator must be redefined create a real color space. % Previously this was unnecessary, as .currentcolorspace could return % an integer. % % /initgraphics { initgraphics { /DeviceGray } cvlit forcesetcolorspace } .bind systemdict begin odef end systemdict /setcolorspace .undef % % setgray - % % setrgbcolor - % % setcmykcolor - % % The Level 1 color setting operators. setcmykcolor is created only if % setcolorscreen is present. These operators are always defined in % systemdict. % /setgray { { { /DeviceGray } cvlit //setcolorspace //setcolor } stopped { /setgray load $error /errorname get signalerror } if } bind systemdict begin odef end /setrgbcolor { { { /DeviceRGB } cvlit //setcolorspace //setcolor } stopped { /setrgbcolor load $error /errorname get signalerror } if } bind systemdict begin odef end /setcolorscreen where { pop /setcmykcolor { { { /DeviceCMYK } cvlit //setcolorspace //setcolor } stopped { /setcmykcolor load $error /errorname get signalerror } if } bind systemdict begin odef end } if % % - currentgray % % - currentrgbcolor % % - currentcmykcolor % % Return the current color, mapped to a DeviceGray, DeviceRGB, or % DeviceCMYK color space. The latter is only created if setcolorscreen % is present. /currentgray { currentcolor currentcolorspace //.cs_get_currentgray exec } bind systemdict begin odef end /currentrgbcolor { currentcolor currentcolorspace //.cs_get_currentrgb exec } bind systemdict begin odef end /setcolorscreen where { pop /currentcmykcolor { currentcolor currentcolorspace //.cs_get_currentcmyk exec } bind systemdict begin odef end } if % % Add some generically useful structures and procedures to .cspace_util. % % % Some common errors. The command for these errors will normally be % overwritten by the invoking operator. We cannot "load" the secolorspace % or setcolor operators, as they are not present in Level 1 systems. % /setcspace_typecheck { /setcolorspace cvx /typecheck signalerror } bind def /setcspace_rangecheck { /setcolorspace cvx /rangecheck signalerror } bind def /setcspace_invalidaccess { /setcolorspace cvx /invalidaccess signalerror } bind def /setcspace_undefined { /setcolorspace cvx /undefined signalerror } bind def /setcolor_typecheck { /setcolor cvx /typecheck signalerror } bind def /setcolor_invalidaccess { /setcolor cvx /invalidaccess signalerror } bind def % % check_array % % Check that an object is an array. Currently we don't check for % readability, as a failing get or length operator should generate % the appropriate invalidaccess error. /check_array { dup type dup /arraytype ne exch /packedarraytype ne and { /setcolorspace cvx /typecheck signalerror } if } bind def % pre-defined procedures for cs_ncomps and cs_get_range /ncomps_1 { pop 1 } bind def /ncomps_3 { pop 3 } bind def /ncomps_4 { pop 4 } bind def /dflt_range_4 [ 0 1 0 1 0 1 0 1 ] readonly def /dflt_range_3 dflt_range_4 0 6 getinterval def /dflt_range_1 dflt_range_4 0 2 getinterval def % get_range_[1|3|4] /get_range_1 { pop //dflt_range_1 } bind def /get_range_3 { pop //dflt_range_3 } bind def /get_range_4 { pop //dflt_range_4 } bind def % % ... % check_num_stack % ... % % validate_color_1 % validate_color_3 % validate_color_4 % % check_num_stack verifies that the stack consists of a color space array and % n numbers. This is used by most of the cs_prepare_color procedures. The % validate_color_[1|3|4] procedures can be used as the cs_prepare_color % procedure for Device specific, CIEBased, and Indexed color spaces. % % Note that the pseudo-operator that (indirectly) invokes this routine will % handle resetting the stacks. % /check_num_stack { dup 2 add copy exch pop { type dup /integertype ne exch /realtype ne and //setcolor_typecheck if } repeat pop % remove the extra op_count } bind def % validate_1 /validate_1 { 1 //check_num_stack exec pop } bind def % validate_3 /validate_3 { 3 //check_num_stack exec pop } bind def % validate_4 /validate_4 { 4 //check_num_stack exec pop } bind def % % pop_1 - % % This is a procedure form of pop. It may be used where a procedure is % expected, but the function of the procedure is the same as the pop % operator. /pop_1 { pop } bind def % % dup_1 % % An analog to pop_1, this one for dup. % /dup_1 { dup } bind def % % ... clear_n_objs - % % Clear n objects from the operand stack. % /clear_n_objs { //pop_1 repeat } bind def % % ... clear_setcolor_operands - % % Clear the setcolor operands for a color space. % /clear_setcolor_operands { //.cs_get_ncomps exec //clear_n_objs exec } bind def % % Return 1, 3, or 4 zeros. These routines are used primarily for the % CIEBased color spaces, for which currentgray and currentrgb % should return 0 for all components, and currentcmyk should return % 0 0 0 1.0 (this varies from Adobe's documentation but is consistent % with their impelementations). % /no_currentgray { //.cs_get_ncomps exec //clear_n_objs exec 0 } bind def /no_currentrgb { //.cs_get_ncomps exec //clear_n_objs exec 0 0 0 } bind def /no_currentcmyk { //.cs_get_ncomps exec //clear_n_objs exec 0 0 0 1.0 } bind def % % bound_0_1 % % Bound a number to the range [0, 1] % /bound_0_1 { dup 0 lt { pop 0 } { dup 1 gt { pop 1 } if } ifelse } bind def % % Provide pseudo-operators for sethsbcolor and currenthsbcolor. These are % alternate versions of the setrgbcolor and currentrgbcolor operators, which % make use of a hue/staturation/brightness color description. % % % ... n max_n % ... n min_n % % Find the maximum and minum of 3 color component intensities. % /max_n { 1 sub { 2 copy lt { exch } if pop } repeat } bind def /min_n { 1 sub { 2 copy gt { exch } if pop } repeat } bind def % % .rgb_2_hsb
%
.hsb_2_rgb % % Convert between RGB and HSB colors, using the hexcone approach (see % Rogers, David, "Procedureal Elements For Computer Graphics", % (McGraw-Hill, 1985), pp. 402 - 3). % % The rgb ==> hsb calculation is: % % br = max(r, g, b) % % if (br == 0) % h = 0, s = 0; % else { % v = min(r, g, b) % diff = br - v; % sat = diff / br; % if (r == br) % h = (g - b) / (6 * diff) + (b > g ? 1 : 0); % else if (g == br) % h = 1/3 + (b - r) / (6 * diff); % else /* b == br */ % h = 2/3 + (r - g) / (6 * diff); % } % % The hsb ==> rgb conversion is: % % mn = (1 - s) * br, md = 6 * s * br; % % switch ((int)floor(6 * h)) { % case 0: /* r >= g >= b */ % r = br; % g = mn + h * md; % b = mn; % break; % % case 1: /* g >= r >= b */ % r = mn + md * (1/3 - h); % g = br; % b = mn; % break; % % case 2: /* g >= b >= r */ % r = mn; % g = br; % b = mn + (h - 1/3) * md; % break; % % case 3: /* b >= g >= r */ % r = mn; % g = mn + (2/3 - h) * md; % b = br; % break; % % case 4: /* b >= r >= g */ % r = mn + (h - 2/3) * md; % g = mn; % b = br; % break; % % case 5: /* r >= b >= g */ % r = br; % g = mn; % b = mn + (1 - h) * md; % break; % % case 6: /* We have wrapped around the hexcone. Thus this case is % the same as case 0 with h = 0 */ % h = 0; % r = br; % g = mn + h * md = mn; % b = mn; % break; % } % /.rgb_2_hsb { % find the largest and smallest components 3 copy 3 //max_n exec dup 5 1 roll dup 0 eq { pop pop pop pop 0 0 } { 4 copy pop 3 //min_n exec 1 index exch sub dup 2 index div 7 1 roll dup 0 eq { 5 { pop } repeat 0 3 1 roll } { 6 mul 5 1 roll 2 copy eq % blue == brightness { pop pop sub exch div .666667 add } { 2 index eq % green == brightness { exch pop exch sub exch div .3333333 add } { % red == brightness sub exch pop exch div dup 0 lt { 1 add } if } ifelse } ifelse 3 1 roll } ifelse } ifelse } bind def /.hsb_2_rgb { 3 { 0 max 1 min 3 1 roll } repeat 1 2 index sub 1 index mul % (1 - s) * br 3 -1 roll 2 index mul 6 mul % 6 * s * br 4 -1 roll % stack:
<(1 - s) * br> <6 * s * br> % array of procedures for the 7 hue cases { % 0 ==> r >= g >= b { mul 1 index add exch } % 1 ==> g >= r >= b { 0.333333 exch sub mul 1 index add 3 1 roll } % 2 ==> g >= b >= r { 0.333333 sub mul 1 index add 3 1 roll exch 3 -1 roll } % 3 ==> b >= g >= r { 0.666667 exch sub mul 1 index add 3 -1 roll } % 4 ==> b >= r >= g { 0.666667 sub mul 1 index add 3 1 roll exch } % 5 ==> r >= b >= g { 1 exch sub mul 1 index add } % 6 ==> r = br, g = b = mn % Case 6 is the same as case 0 with h = 0. This also simplifies % the calculations. { pop pop dup } } 1 index 6 mul cvi % (int)(6 * h) get exec } bind def % % % /sethsbcolor { { //.hsb_2_rgb exec setrgbcolor } stopped { /sethsbcolor load $error /errorname get signalerror } if } bind systemdict begin odef end /currenthsbcolor { { currentrgbcolor //.rgb_2_hsb exec } stopped { /currenthsbcolor load $error /errorname get signalerror } if } bind systemdict begin odef end end % .cspace_util .setglobal