JDK-6696292 : Printing transformed images accuracy problems
  • Type: Bug
  • Component: client-libs
  • Sub-Component: 2d
  • Affected Version: 6,6u10
  • Priority: P3
  • Status: Closed
  • Resolution: Fixed
  • OS: windows_2000,windows_xp
  • CPU: x86
  • Submitted: 2008-04-30
  • 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.
JDK 6 JDK 7
6u10Fixed 7 b28Fixed
Related Reports
Duplicate :  
Description
FULL PRODUCT VERSION :
java version "1.6.0_06"
Java(TM) SE Runtime Environment (build 1.6.0_06-b02)
Java HotSpot(TM) Client VM (build 10.0-b22, mixed mode, sharing)

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows XP Home Edition Version 2002 Service Pack 2

EXTRA RELEVANT SYSTEM CONFIGURATION :
Problem shows on multiple printers

A DESCRIPTION OF THE PROBLEM :
When printing an image using a transform that has non-integer scale factors, the resizing algorithm in JDK 1.6 distorts the image so that the output is not usable when image accuracy is important.

This is especially evident when printing the image of a barcode.  After printing, the image of the barcode is not recognizable by a barcode reader.

  From indirect evidence, it seems that the image is being transformed first to 72 DPI and then to the printer's native resolution (300 and 600 DPI in our test cases).  When the image is downsampled and then upsampled, it will naturally cause unnecessary distortion in the output image.

The problem is not present in JDK 1.4.2 and JDK 1.5.0, it seems that the image is resized directly to the output resolution.

We tried using all permutations of rendering hints, with no success.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
In a class that implements Printable:

- Load a JPEG image of a barcode.
- Create an AffineTransform object that has non-integer scaling factors.  In our test case, we use "new AffineTransform (1.08, 0, 0, 0.648, 72, 72)"
- Send the image to the printer using graphics.drawImage (img, xform, null).

- We have a sample barcode image that can be used to test this.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The image should be transformed without loss of significant quality, results should be as good as JDK 1.4.2 and JDK 1.5.0.
ACTUAL -
The output image is distorted so that it is not recognized by a barcode reader.

ERROR MESSAGES/STACK TRACES THAT OCCUR :
No error message.

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
/*
 * Created on Apr 25, 2008
 *
 */
package printJPEG;

import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;
import java.io.File;

import javax.imageio.ImageIO;

public class PrintJPEG implements Printable
{
    public static void main (String [] args)
    {
        try
        {
            // Print Java Version
            System.out.println (System.getProperty("java.version"));

            // Create printer job
            PrinterJob pJob = PrinterJob.getPrinterJob();
            pJob.setPrintable (new PrintJPEG());
            pJob.print();
        }
        catch (Throwable t)
        {
            t.printStackTrace();
        }
    }
    
    public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException
    {
        if (pageIndex == 0)
        {
            Graphics2D g2d = (Graphics2D)graphics;
            g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
            g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
            
            try
            {
                // Load the JPEG image
                BufferedImage jpegImage = ImageIO.read(new File ("c:\\test.jpg"));

                AffineTransform xform = new AffineTransform (1.08, 0, 0, 0.648, 72, 72);
                g2d.drawImage (jpegImage, xform, null);
                
                return Printable.PAGE_EXISTS;
            }
            catch (Throwable t)
            {
                throw new PrinterException ("Error printing JPEG: " + t.getMessage());
            }
        }
        else
        {
            return Printable.NO_SUCH_PAGE;
        }
    }
}

---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Have not been able to find a workaround.

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

Comments
EVALUATION The fix for 6201927 was principally for translucent images, and this image is opaque. However one element of that change clamped the scale that would be performed by the device, and performed the rest in Java. This affects opaque and translucent images. The app scaled user space along the x-axis by a factor of 1.08. This was possibly crafted so that the scale to many typical printers will be integral, eg 600/72*1.08=9.0 But the above new JDK logic split this back into a scale of 1.08 and a scale of 16.667. Scaling the source image into an intermediate image by 1.08 lead to pixels shifting position in an observably non-uniform manner. Reverting the change for this case, where there is no rotation or shearing, means that we see a simple identity transform (and don't even need an intermediate image), and we scale only once, directly from image space to the device. But if the rotated and sheared cases operate on the lower resolution image (ie if we revert to using a smaller intermediate image, and a greater subsequent scale to the device), then the results for this case is noticeably worse. So the fix to this bug should keep the clamping for such transforms. The reason for the separation is that in rotated cases we have been unable to always rely on the platform to be able to apply that rotation. I think this limitation really applied only to windows 95, which is no longer supported, so one larger solution is to let GDI (and Postscript), handle that. But in the present code structure, its still necessary to separate out the simple scaling to device from the rest of such a complex transform. Another issue is that not all systems support translucency. So in those cases too we will need an intermediate image, at some intermediate resolution. GDI DIBs v4 and later do support 32 bit colors with alpha, although I am not sure if printer drivers are required to support this or how well they handle it. I'll be pleasantly surprised if it uniformly works without issues (testing needed). Postscript however does not (in any useful version) have such support. So the simplest solution is to allow simple scales and quadrant rotations to perform the entire scale directly from the image to the device. Using GDI and Postscript transforms for the general rotation and shear cases, and GDI for tranlucent images will require new code for each of those printing systems and should be deferred to a suitable release as its a significantly greater undertaking than fixing the regression in behaviour.
13-05-2008

EVALUATION This is caused by fix for 6301927.
07-05-2008