United StatesChange Country, Oracle Worldwide Web Sites Communities I am a... I want to...
JDK-6696292 : Printing transformed images accuracy problems

Details
Type:
Bug
Submit Date:
2008-04-30
Status:
Closed
Updated Date:
2011-03-07
Project Name:
JDK
Resolved Date:
2011-03-07
Component:
client-libs
OS:
windows_xp,windows_2000
Sub-Component:
2d
CPU:
x86
Priority:
P3
Resolution:
Fixed
Affected Versions:
6,6u10
Fixed Versions:

Related Reports
Backport:
Duplicate:

Sub Tasks

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

This is caused by fix for 6301927.
                                     
2008-05-07
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.
                                     
2008-05-13



Hardware and Software, Engineered to Work Together