[gs-devel] colour, colour spaces and Ghostscript
Igor V. Melichev
igor.melichev at artifex.com
Wed May 21 02:51:13 PDT 2008
Ken,
I recall Dan worked on the "pop false" issue in tint transfer,
so you should find and read related bugs and patches.
Did you ?
Besides that, there was some arguing in the tech achieve,
but I'm not sure whether it is useful.
IMO Adobe does violates PLRM with "pop false",
and we need to make gs be compatible to Adobe.
I recall that Dan tried to do that, and (as usual) I'm not sure
that he did in the right way.
So here we again come to Dan's patches and his motivation for them.
That's all for now, sorry.
Ask specific questions if any.
Igor.
----- Original Message -----
From: "Ken Sharp" <ken.sharp at artifex.com>
To: <gs-devel at ghostscript.com>
Sent: Saturday, May 17, 2008 2:43 PM
Subject: [gs-devel] colour, colour spaces and Ghostscript
> Morning all,
>
> For the last few weeks I've been looking into the way Ghostscript
> currently handles color and more particularly color spaces in PostScript.
> Ralph asked me to summarise what I've found so far, and also to document a
> problem I've run up against. The first part of this (how it works now) is
> probably of limited interest to anyone outside the color group, feel free
> to skip this (jump ahead to "DeviceN bug").
>
> However I'd welcome any opinions on the second part, which deals with what
> I think is a bug in Ghostscript relating to DeviceN and potentially other
> color spaces. If someone can tell me what I'm doing wrong I'd be grateful.
> Alex, there's a question about the PDF interpreter in there which I'd
> really like your input on please.
>
> How it works now
> =================
> I was asked to look into converting the existing complex PostScript
> structure into C, to dispense, at least as far as possible, with the
> PostScritp interpreter. Because PostScript spaces use tint transform
> procedures, which can be written in arbitrary PostScript, dispensing with
> the PostScript interpreter entirely isn't an option, at least for
> PostScript color spaces.
>
> If the PDF interpreter were to be re-implemented in C it would be possible
> (for PDF), since the tint transform procedure is already written as a
> Function, and we don't need to run a full PostScript interpreter (NB
> PostScript calculator functions would still need some kind of
> interpreter).
>
> More useful would be the ability to set ICC color spaces without relying
> on the PostScript interpreter, since these are the basis of color in XPS
> and likely to be so for any other page description languages.
>
> Anyway, following the usual Ghostscript design policy for the PostScript
> interpreter, much of the work involved in color is currently done in
> PostScript, with specific custom operators (eg .setdevicenspace) to
> perform the actual kernel work. Surprisingly perhaps, the kernel 'C'
> setcolorspace operator (zsetcolorspace in zcolor.c) does almost nothing,
> the actual work being done by the custom operators.
>
> The current implementation creates PostScript redefinitions of setcolor,
> setcolorspace, setgray, setrgbcolor, sethsbcolor, setcmykcolor,
> currentgray, curretnrgbcolor, currenthsbcolor and curretncmykcolor. In
> addition a dictionary 'colorspacedict' is defined in global VM, and used
> by these procedures.
>
> The code dealing with this is in /lib/gs_cspace.ps and is well documented.
> The general form is an object-oriented implementation, colorspacedict
> contains a key (the space name) for every supported color space and a
> dictionary for each space. The color space specific dictionary contains a
> number of procedures (methods) such as cs_validate, cs_install etc. Spaces
> are added to the dictionary by gs_ciecs2.ps, gs_ciecs3.ps, gs_devcs.ps,
> gs_devn.ps, gs_devpxl.ps, gs_icc.ps, gs_indxd, gs_patrn.ps, gs_sepr.ps.
>
> When a color space is set, the setcolorspace routine in gs_cspace.ps finds
> the name of the space either given directly as a base color name such as
> /DeviceGray or as the first entry from a color space array. The dictionary
> whose key in colorspacedict matches the color space name is extracted, and
> the methods in that dictionary used to actually install the color space
> via the custom operators.
>
> One interesting wrinkle is that complex colour spaces are installed
> 'backwards'. That is, each color space install method starts by setting
> the current color space to be the alternate for this space, and this is
> done recursively until we reach a color space which has no alternate. For
> example, the space:
>
> [/Indexed [/Separation (Pink) /DeviceCMYK {0 exch 0 0}] 256 <..elided
> lookup..>>
>
> Starts by setting the Indexed space, which sets its alternate space,
> Separation. The Separation space method then sets the base CMYK space. On
> return from setting the base space, the Separation method sets the current
> space to be /Separation. Finally, on return from the Separation space, we
> set the current space to be /Indexed. This recursion is all done in the
> PostScript code.
>
> Now, Ghostscript also converts PostScript tint transform procedures into
> Functions, either type 4 (PostScript calculator) or type 0 (sampled). If
> we create a sampled function, the more common case, then we will
> (obviously) execute code which samples the color space. (We do this even
> if we will never actually use the alternate space, which potentially costs
> performance)
>
> This is, at least partially, the reason why spaces are installed from the
> back forwards. In order to build a function dictionary the C code must
> know how many inputs and outputs are required. The number of inputs is
> given by the current space, but the number of outputs is given by the
> alternate space. By setting the current space to the alternate before we
> set the parent space, the current 'C' colorspace object gives us this
> information directly. Otherwise we would have to build a lot of common
> intelligence into each color space specific method in the kernel so that
> it could determine how many outputs it required.
>
> There may be other reasons for this, if anyone knows what they are, I
> would very much appreciate the information, the comments in the PostScript
> and C both say that the current design means that spaces need to be
> installed this way, but give few details on why.
>
>
> DeviceN Bug
> ============
> While working on converting the color space handling so that the use of
> PostScript is minimised, I ran into the well-known bug with DeviceN and
> Photoshop 5+ I'll recap the bug details below, if you already understand
> i, skip ahead to 'Problem'.
>
> Photoshop emits PostScript which, on level 3 devices, makes use of the
> DeviceN color space for multi-tone (duotone, tritone etc) images. To do
> this, it starts by setting a DeviceN space which contains all the required
> inks with an alternate of DeviceGray, the tint transform procedure is
> unusual in that it removes the ink tint percentages from the stack, and an
> additional extra operand. It then places a 'false' on the stack (replacing
> the extra operand) and a '0' for the tint. Eg:
>
> [ /DeviceN [ /Black (PANTONE 541) ] /DeviceGray {3 {pop} repeat false
> 0 } ] setcolorspace
>
> On the face of it this is odd behaviour. What it is actually intended to
> do is probe the interpreter to see whether the DeviceN space can be
> directly supported or not. After having set the space, the job executes:
>
> true 0 0 setcolor
>
> Now, if the DeviceN space is supported (ie all inks are present) then the
> tint transform procedure will not be executed during setcolor, and the
> stack will contain a 'true'. However if the space is not supported, then
> the tint transform will be executed and the stack will contain a 'false'.
>
> In itself this violates part of the specification which states that
> "Because the tint Transform procedure is called by the setcolor and image
> operators at unpredictable times, it must operate as a pure function
> without side effects." Manipulating an extra object on the stack is pretty
> clearly a side-effect.
>
> Arguably this is valid because the specification also states that if the
> space is supported then the alternative and tint transform are ignored.
>
> In any event, the resulting Boolean is then used by the job to decide
> whether to load a DeviceN space, or a different color space (always some
> flavour of CIE) :
>
> /PhotoshopDuotoneColorSpace [ /Indexed [ /DeviceN PhotoshopDuotoneList [
> /DeviceGray ] { /NeverReached 4 {pop} repeat 0 } ] 255
> <...
> > ] def
> /PhotoshopDuotoneAltColorSpace [ /Indexed [ /CIEBasedABC <<
> /MatrixLMN [0.9505 2E-5 0 0 1 0 0 2E-5 1.0890]
> /DecodeLMN [{2.2 exp} dup dup]
> /WhitePoint [0.9505 1 1.0890] >> ] 255
> <...
> > ] def { PhotoshopDuotoneColorSpace } { PhotoshopDuotoneAltColorSpace }
> ifelse
>
> It would, of course, be more sensible to simply set the CIE space as the
> alternate for the DeviceN space and allow the interpreter to make the
> decision.
>
>
> Problems
> ===========
> The first problem here is that the tint transform procedure for the
> DeviceN space has a bug. Although the space involves two inks, and the
> tint transform pushes a name, the procedure pops 4 values from the stack.
> There appears to be code in the sampled function routines to deal with
> this, but they do so by returning an 'undefinedresult' error. So if we
> were to sample this procedure, as required for the conversion to a
> PostScript function, we would have a problem.
>
> Notice that none of these execute 'bind' so Idiom Recognition isn't really
> an option.
>
>
> So why don't we have a problem ? This is the more serious issue, and I
> would welcome any extra information. At the moment I think we have a
> moderately serious bug which I'm surprised hasn't been reported by a
> customer.
>
> setcolor is defined in gs_cspace as follows:
>
> /setcolor
> {
> {
> currentcolorspace //.cs_prepare_color exec //setcolor
> currentcolorspace //.cs_complete_setcolor exec
> }
> stopped
> { //.cspace_util /setcolor get $error /errorname get signalerror }
> if
> }
> bind odef
>
> The '.cs_prepare_color' and '.cs_complete_setcolor' procedures extract the
> methods with the same name from the colorspacedict, using the current
> color space to select the correct instance, and then executes them.
>
> Notice that we first execute the 'C' level setcolor routine, and then
> execute an additional 'complete setcolor' method. The comments for
> cs_complete_setcolor say:
>
> % 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.
>
> So that looks good. If we are using the alternate space, then this will
> execute the tint transform for the benefit of this kind of Adobe code. The
> definition in gs_devn.ps also looks fine:
>
> /cs_complete_setcolor
> {
> .usealternate
> {
> pop
> currentcolor
> currentcolorspace 3 get exec
> currentcolorspace 2 get
> //clear_setcolor_operands exec
> }
> { pop }
> ifelse
> }
> bind
>
> So if '.usealternate' is true we will execute the tint transform. Good
> enough. Checking the definition of .usealternate to see how it 'knows'
> whether the alternate is being used or not (zcolor2.c):
>
> static int
> zusealternate(i_ctx_t * i_ctx_p)
> {
> os_ptr op = osp;
> const gs_color_space * pcs = gs_currentcolorspace(igs);
>
> push(1);
> make_bool(op, pcs->base_space != 0);
> return 0;
> }
>
> So it is true if the current color space has a non-zero 'alternate' Still
> sounds OK, but....
>
> In zcsdevn.c, zsetdevicenspace:
>
> /* The alternate color space has been selected as the current color
> space */
> pacs = gs_currentcolorspace(igs);
>
> code = gs_cspace_new_DeviceN(&pcs, num_components, pacs, imemory);
>
> and then in gscdevn.c, gs_cspace_new_DeviceN :
>
> pcs->base_space = palt_cspace;
>
>
> Bearing in mind that we set color space alternates backwards, by the time
> we reach zsetdevicenspace the current color space has already been set to
> the alternate, and the gs_color_space object base_space member will then
> be set to that. So when we execute .usealternate, base_space will always
> be non-zero.
>
> This is why we don't get an error sampling the broken tint transform, the
> Photoshop job thinks we can't support the spot color, and instead uses the
> CIE color space.
>
>
>
> To test this, I used two files, the first a simple hand crafted file:
>
> %!
> [/DeviceN [/Pink] /DeviceGray {}] setcolorspace
> 0.5 setcolor
> 10 10 100 100 rectfill
> .usealternate == %% Are we using the alternate space ?
> showpage
>
> This always echoes true to sdout.
>
> I also tried the file from bug 688584. Unfortunately this is rather a
> large file, but it does contain a genuine Adobe Photoshop duotone. I have
> reduced the file size, but it is still 11 Mb.
>
> I tested these two files using the following command lines:
>
> gswin32c -sDEVICE=tiffsep -sOutputFile=test.tif test.ps
> gswin32c -sDEVICE=tiffsep -sOutputFile=test.tif -c "<<
> /SeparationColorNames [/Pink] >> setpagedevice" -f test.ps
>
> gswin32c -sDEVICE=tiffsep -sOutputFile=test.tif Bug688584.ps
> gswin32c -sDEVICE=tiffsep -sOutputFile=test.tif -c "<<
> /SeparationColorNames [(PANTONE 541) cvn] >> setpagedevice" -f
> Bug688584.ps
>
> My own test file renders a rectangle to the spot plate as expected in both
> cases. The duotone renders the image to the Cyan, Magenta and Yellow
> plates in both cases, when it *should* be rendered to the Black and spot
> plates, both of which are blank.
>
>
> This seems like a bit of a hole to me, can anyone tell me if I'm doing
> something wrong ? It appears to me that Photoshop duotones will never
> render on the correct plates.
>
> Alex, I'm having some similar problems with PDF files, do you know if the
> PDF interpreter does any kind of similar operations to the Adobe Photoshop
> job ?
>
>
>
> Ken
>
> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
> TASK: Shoot yourself in the foot.
> HTML 1 You shoot yourself in the foot, only to find out that no matter how
> gory the result looks, your foot keeps working. Your foot finally stops
> working when you stub your toe kicking the box the gun came in.
> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
>
> _______________________________________________
> gs-devel mailing list
> gs-devel at ghostscript.com
> http://www.ghostscript.com/mailman/listinfo/gs-devel
>
More information about the gs-devel
mailing list