JDK-6800846 : REGRESSION: Printing quality degraded with Java 6 compared to 5.0
  • Type: Bug
  • Component: client-libs
  • Sub-Component: 2d
  • Affected Version: 6
  • Priority: P3
  • Status: Closed
  • Resolution: Fixed
  • OS: solaris_2.5.1
  • CPU: sparc
  • Submitted: 2009-02-03
  • Updated: 2011-03-07
  • Resolved: 2011-03-07
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
6u14Fixed 7 b54Fixed
Related Reports
Relates :  
Windows XP

Any Java 6 SDK after pre-GA build 1.6.0-b92

Direct printing quality with Java 6 is severely degraded, under certain circumstances, compared to Java 5.0. We have provided a testcase demonstrating this.

With the help of JLE (David Korbel) we have narrowed this problem down to a regression caused by the fix for CR 6444688 in 1.6.0-b92:

------- WPathGraphics.java -------
>                      * Since a subimage can be created by calling
>                      * BufferedImage.getSubImage() that condition needs to
>                      * be accounted for too. This implies inspecting the
>                      * data buffer. In the end too many cases are not able
>                      * to take advantage of this option until we can teach
>                      * the native code to properly navigate the data buffer.
>                      * Until all of this is resolved newImage is always true.
<                     int txType = rotTransform.getType();
<                     boolean newImage =
<                         (txType != AffineTransform.TYPE_IDENTITY &&
<                          txType != AffineTransform.TYPE_TRANSLATION) ||
<                         dibType != img.getType() ||
<                         icm != null && icm != img.getColorModel() ||
<                         srcX != 0 || srcY != 0 ||
<                         srcWidth != img.getWidth(null) ||
<                         srcHeight != img.getHeight(null);
>                     boolean newImage = true;

It seems that explicitly setting the boolean newImage to true is the root cause of the issue. If we change the code to always set the boolean to false, or reinstate the original code, the problem disappears. However, this will re-introduce the problem reported in 6444688. In summary, the fix for 6444688 needs to be reworked.

Release Regression From : 6
The above release value was the last known release where this 
bug was not reproducible. Since then there has been a regression.

SUGGESTED FIX http://sa.sfbay.sun.com/projects/java2d_data/7/6800846.0

EVALUATION The bug isn't in printing. Its in image code that is used by printing. The application prepares an image and draws it to the printer. The image is a 1 bit image in BYTE_BINARY BufferedImage, with an indexed colormodel where we have color index 0 == WHITE, and color index 1 = BLACK. The printing implementation re-draws that into its own copy of the image using the same size, image type and colormodel as the original. This should be a simple blit but it replicates many of the black pixels on the scan line. The same doesn't happen if the indexed color model reverses the order of the colours. The problem is demonstrated by this test program which simply creates a small image and then re-draws it. The results differ based on the colour table. I believe we are tripping over a > 10 year old bug in the color cube initialisation in dither.c that is used to look up a pixel given an RGB value. This is used for each pixel in the inner loop for this indexed color model. Because of the way the color cube is initialised, for RGB=0x0 it assigns pixel value 255. This is nonsense since there are only pixel values 0 and 1. The loop however takes that value and ors it into the output, and depending on where in the output byte we are corruption is variable. I think the cause of the problem is that the code pays no attention to the cmap_len parameter (in this case 2) and assumes 256. ie it grabs the rgb value from cmap[255-i] which could be garbage. When a color map is in increasing order of pixel and RGB value this bug isn't likely to be observed since entries in the cube that are already assigned are not overwritten but reverse order will hit this case. Here's a simple test case which creates two GIF files, in good.gif a single pixel is drawn. In bad.gif its replicated. import java.awt.*; import java.awt.color.*; import java.awt.image.*; import static java.awt.image.BufferedImage.*; import java.io.*; import javax.imageio.*; public class DrawBB { public static void main(String args[]) throws IOException { int w = 100, h = 30; byte[] arr = {(byte)0xff, (byte)0x0}; IndexColorModel newCM = new IndexColorModel(1, 2, arr, arr, arr); BufferedImage orig = new BufferedImage(w, h, TYPE_BYTE_BINARY, newCM); Graphics2D g2d = orig.createGraphics(); g2d.setColor(Color.white); g2d.fillRect(0, 0,w,h); g2d.setColor(Color.black); g2d.drawLine(10, 20, 10, 20); // draw a single pixel. g2d.dispose(); BufferedImage good = new BufferedImage(w, h, TYPE_BYTE_BINARY); g2d = good.createGraphics(); g2d.drawImage(orig, 0, 0, null); g2d.dispose(); ImageIO.write(good, "gif", new File("good.gif")); IndexColorModel origCM = (IndexColorModel)orig.getColorModel(); BufferedImage bad = new BufferedImage(w, h, TYPE_BYTE_BINARY,origCM); g2d = bad.createGraphics(); g2d.drawImage(orig, 0, 0, null); g2d.dispose(); ImageIO.write(bad, "gif", new File("bad.gif")); } }

WORK AROUND Print via a buffered image or using off-screen graphics.