Bug 690538 - Minor difference in output compared to Acrobat 9 Pro
Summary: Minor difference in output compared to Acrobat 9 Pro
Status: NOTIFIED FIXED
Alias: None
Product: Ghostscript
Classification: Unclassified
Component: Color (show other bugs)
Version: master
Hardware: Macintosh MacOS X
: P2 normal
Assignee: Robin Watts
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2009-06-12 11:08 UTC by Marcos H. Woehrmann
Modified: 2012-04-12 17:13 UTC (History)
1 user (show)

See Also:
Customer: 190
Word Size: ---


Attachments
ghostscript.Black.png (4.85 KB, application/octet-stream)
2009-06-12 11:10 UTC, Marcos H. Woehrmann
Details
acrobat.Black.png (5.32 KB, application/octet-stream)
2009-06-12 11:12 UTC, Marcos H. Woehrmann
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Marcos H. Woehrmann 2009-06-12 11:08:06 UTC
This effect is subtle, so please read the entire report before saying you can't see it.

The customer reports:

The background color (light blue) should be uniform, it is not. If you have a look around the disk 
(showing a kind map zoom), the color has K=35 (in decimal) near the dsk whereas everywhere else it's 
K=36.

Apparently a problem of rounding of truncation. Acrobat Pro renders it with a uniform color.

Of course on the screen you see nothing, but on a big print that's quite visible.

----------------------------------------------------------------------------

How to duplicate the problem:

  bin/gs -sDEVICE=tiffsep -o test.tif ./MontchatTRIP_d.pd

Open the test.Black.tif file in Photoshop and use the level controls with the parameters "0 0.01 222".  
This amplifies the effect and makes it visible on screen.

I've duplicated this problem with head (r9788), 8.64, and 8.63.  Adobe Acrobat 9 Pro renders the file 
with a uniform background.
Comment 1 Marcos H. Woehrmann 2009-06-12 11:08:40 UTC
Created attachment 5099 [details]
MontchatTRIP_d.pdf
Comment 2 Marcos H. Woehrmann 2009-06-12 11:10:23 UTC
Created attachment 5100 [details]
ghostscript.Black.png

Black channel of Ghostscript generated TIFF file, after adjusting levels with
Photoshop.
Comment 3 Marcos H. Woehrmann 2009-06-12 11:12:13 UTC
Created attachment 5101 [details]
acrobat.Black.png

Adobe Acrobat 9 Pro converted file, after adjusting levels with Photoshop.
Comment 4 Masaki Ushizaka 2009-08-14 01:23:00 UTC
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.
Comment 5 Masaki Ushizaka 2009-09-10 03:42:06 UTC
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.
Comment 6 Michael Vrhel 2009-09-10 12:14:01 UTC
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.   
Comment 7 Michael Vrhel 2010-02-16 11:16:59 UTC
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....
Comment 8 Michael Vrhel 2011-06-06 06:26:35 UTC
I will talk with Robin about how best to optimize this.
Comment 9 Michael Vrhel 2011-08-16 16:11:50 UTC
Handing off to Robin to see if he has a fast way to do this rounding operation.
Comment 10 Robin Watts 2011-08-17 10:26:46 UTC
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.
Comment 11 Robin Watts 2011-08-21 07:08:23 UTC
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).
Comment 12 Michael Vrhel 2011-08-21 18:39:04 UTC
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.
Comment 13 Robin Watts 2011-11-10 12:59:19 UTC
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!