Summary: | Minor difference in output compared to Acrobat 9 Pro | ||
---|---|---|---|
Product: | Ghostscript | Reporter: | Marcos H. Woehrmann <marcos.woehrmann> |
Component: | Color | Assignee: | Robin Watts <robin.watts> |
Status: | NOTIFIED FIXED | ||
Severity: | normal | CC: | michael.vrhel |
Priority: | P2 | ||
Version: | master | ||
Hardware: | Macintosh | ||
OS: | MacOS X | ||
Customer: | 190 | Word Size: | --- |
Attachments: |
ghostscript.Black.png
acrobat.Black.png |
Description
Marcos H. Woehrmann
2009-06-12 11:08:06 UTC
Created attachment 5099 [details]
MontchatTRIP_d.pdf
Created attachment 5100 [details]
ghostscript.Black.png
Black channel of Ghostscript generated TIFF file, after adjusting levels with
Photoshop.
Created attachment 5101 [details]
acrobat.Black.png
Adobe Acrobat 9 Pro converted file, after adjusting levels with Photoshop.
Reproduced with r9980 on Mac OS X using tiffsep and tiff32nc. Did not happen on tiff24nc, tiffgary. I am not 100% sure but quick look by Illustrator makes me think those darker area is painted by image and the rest is fill. PDF information says it is version 1.6, Quark XPress 6.1, Mac OS X 10.5.7 Quarts PDF Context. I simplified this to 15 lines PS program.
---
%!
0.39 0.2 0.06 0.14 setcmykcolor
100 100 100 100 rectfill
200 100 translate
100 100 scale
<<
/ImageType 1
/Width 1
/Height 1
/ImageMatrix [1 0 0 1 0 0]
/DataSource <63 33 0f 24>
/BitsPerComponent 8
/Decode [0 1 0 1 0 1 0 1]
>> image
showpage
---
Save this and name the file 'p2.ps', run with command 'gs -sDEVICE=tiffsep -o p2 p2.ps', then open
p2.Black.tif. There are two squares, or you may see it a rectangle.
Left half is painted with 'fill' and has value of 220/255. Right half is painted with 'image' and has value
of 219/255.
Black component in 'fill' is 0.14, which is 35.7/255. Black component in 'image' has value of 0x24 =
36. Sounds like a rounding issue.
I'm not sure if I can call it a 'problem'. If an application really need to maintain color consistency
between fill color and image, it needs to specify color values in 255th unit, not in 100th unit. By using
'0.3882 0.2 0.05882 0.1411 setcmykcolor', I see no difference in output.
On the other hand, I would say converting 35.7 to 36 is better than just truncate it to 35.
Comparing AR and what we create by opening the PDF in photoshop, it is clear that we match the color of AR in the image but not in the solid fill of the CMYK value [0.39, 0.2, 0.06, 0.14]. I have found the issue. It is related to how we do the color encoding from the unsigned short form to the 8 bit value during dev_proc(dev, encode_color)(dev, cv). The tiffsep device uses the devn_encode_compressed_color operation which basically throws out the lower bits and keeps the upper bits. There is no 1/2 adjustment or rounding. Note that the tiff32nc device also has the same problem. It uses cmyk_8bit_map_cmyk_color where you can see the lower bits are thrown away. AR does a rounding operation and we end up off by 1 count in comparison. I don't want to make a change to fix this until we talk about it a bit, since a change to how these operations work will likely result in huge regressions when the clusters run. Handing to Dave to optimize final rounding to 8 bit in place of current truncation. An example where being off by one count is visible.... I will talk with Robin about how best to optimize this. Handing off to Robin to see if he has a fast way to do this rounding operation. I've had a quick look at the code. Firstly, Masaki notes in comment 4 that tiffsep and tiff32nc show the problem, where tiff24nc and tiffgray do not. This is in line with Michaels observations in comment 4 as they use different encode/decode colour pairings. tiffsep/tiff32nc roll their own, where tiff24nc and tiffgray use gx_default_map_{rgb_color,color_rgb}. The gx_default_map_ twins truncate when going from 16 -> 8 bits, and duplicate when going from 8 ->16 (i.e., 0xabcd => 0xab, and 0xab => 0xabab). The tiffsep specific ones do not duplicate (i.e. 0xabcd => 0xab, but 0xab => 0xab00). It would be worth testing to see if simply fixing this solved the problem. For a long time, I believed that this was the right thing to do (to simply drop bits, and then duplicate again when expanding) in order to minimise the average round trip error. Only a month or two ago, however, I noticed that lcms actually does something different. #define RGB_8_TO_16(rgb) (WORD) ((((WORD) (rgb)) << 8)|(rgb)) #define RGB_16_TO_8(rgb) (BYTE) ((((rgb) * 65281 + 8388608) >> 24) & 0xFF) i.e. it duplicates as expected (0xab => 0xabab), but conversion from 16 to 8 is NOT simple truncation. I started to write Marti an email explaining why this was wrong, and to support my argument wrote a test program to calculate the average round trip error, only to discover that his routines are actually better. Now, it's entirely possible that the problem we are seeing in this bug is due to lcms doing the colour conversion for images and gs doing it itself for line art. If this is the case then the only fix will be to pull one into line with the other. I suspect that we should be pulling gs into line with lcms, but that's a big change, I fear. The simple first test, I guess, is to fix the tiffsep functions to correctly duplicate when up converting. This may solve the bug, but isn't a true fix for the larger problems. The simple fix doesn't work. A larger fix to make gs follow lcms seems required. I'd like to have comments from Henry/Michael on this before I launch into it though (and anyone else with an opinion). lcms should be doing the conversion for the line art and for the images. However, lcms is typically handing back 8 bit data for images and 16 bit data for line art. From what I was seeing gs was doing a truncation as opposed to a rounding operation. AR also does a rounding operation. I am of the opinion that we should fix gs so that we do the rounding like what is done in lcms. I have just committed: commit 1da2a46ed9f6ae0b0afc5fd4417943a36e532171 Author: Robin Watts <Robin.Watts@artifex.com> Date: Wed Aug 31 18:39:24 2011 +0100 More work on bug 690538: introduce macros for color rounding. Introduce new macros to gxcvalue.h header file that defines helpful macros for colour depth changing. COLROUND macros do rounding (16->n bits), COLDUP macros do bit duplication (n->16 bits). Use these macros in various places throughout the code. Also tweak the gx_color_value_to_byte macro to round in the same way. Colors for devices that use these functions are now rounded in the same way that lcms does. Change as many encode_color routines as I can find to use this new code rather than simply truncating. That should make everything round like lcms and solve the problems. It also changes almost every image in our cluster regression suite - sorry Marcos! |