JDK-5028259 : Setting of destination image type for jpeg image increase size of result.
  • Type: Bug
  • Component: client-libs
  • Sub-Component: javax.imageio
  • Affected Version: 6
  • Priority: P4
  • Status: Resolved
  • Resolution: Fixed
  • OS: generic
  • CPU: generic
  • Submitted: 2004-04-07
  • Updated: 2004-09-07
  • Resolved: 2004-09-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.
6 mustangFixed

Name: abR10136			Date: 04/07/2004

 If we explicitly use the write param with destination type to write image as
 jpeg then the size of the result file is bigger than if same
 destination type is used implicitly.

 Expected result: size of result should not depend on the way of
   selecting destination type. For identical image write params we must get
   identical sizes.

 Testcase from comments section could be used to reproduce the problem
  (note that created files sizes are different.
   For instance for test/javax/imageio/images/VancouverIsland200.jpg
   - sizes are 18156 bytes and 7460 bytes).


CONVERTED DATA BugTraq+ Release Management Values COMMIT TO FIX: mustang FIXED IN: mustang INTEGRATED IN: mustang

EVALUATION Name: abR10136 Date: 04/08/2004 The problem has place if destination type is specified but corresponding metadata object is not specified. By default ImageIO jpeg writer uses only one of Qtables (setting QtableSelectors elements to 0 (default value)). Default QtableSelectors values updated later using given metadata object. If metadata was not specified then it is created using information from buffered image (aka "full image" case). At the moment if the destination type is specified it means that we use only raster data or some subset of bands from the source image. In this case the metadata is not created and we have no way to change the default values of QtableSelectors. This causes worse level of compression (i.e. bigger result) comparing to case when metadata object is created ourselves. The idea of the proposed fix is to create the metadata object using given destination type in order to build SOF marker object, which will be used to change default values of the QtableSelectors (similar to how it is done for the "full image" case). ======================================================================

WORK AROUND Name: abR10136 Date: 04/13/2004 Usage of metadata object and explicitly specified destination type helps to avoid major increase of size. Result still will be bigger than optimal but this is less noticeable. =================== BEGIN OF WORKAROUND EXAMPLE ====================== import java.awt.image.BufferedImage; import java.awt.Graphics; import javax.imageio.ImageIO; import javax.imageio.IIOImage; import javax.imageio.ImageWriter; import javax.imageio.ImageWriteParam; import javax.imageio.ImageTypeSpecifier; import javax.imageio.event.IIOWriteWarningListener; import javax.imageio.stream.ImageOutputStream; import javax.imageio.metadata.IIOMetadata; import javax.imageio.metadata.IIOMetadataNode; import javax.imageio.metadata.IIOInvalidTreeException; import java.io.File; import java.io.IOException; import org.w3c.dom.Node; import org.w3c.dom.NodeList; public class Workaround implements IIOWriteWarningListener { public static void main(String[] args) throws IOException { BufferedImage bi = ImageIO.read(new File(args[0])); BufferedImage bi_argb = new BufferedImage(bi.getWidth(), bi.getHeight(), BufferedImage.TYPE_INT_ARGB); Graphics g = bi_argb.createGraphics(); g.drawImage(bi, 0, 0, null); ImageWriter iw = (ImageWriter)ImageIO.getImageWritersByFormatName("jpeg").next(); iw.addIIOWriteWarningListener(new Workaround()); ImageWriteParam p = iw.getDefaultWriteParam(); ImageTypeSpecifier type = ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_INT_RGB); p.setDestinationType(type); p.setSourceBands(new int[] {0, 1, 2}); ImageOutputStream ios = ImageIO.createImageOutputStream(new File("Workaround.jpeg")); iw.setOutput(ios); System.out.println("create metadata"); IIOMetadata meta = iw.getDefaultImageMetadata(type, null); meta = addAdobeTransform(meta); iw.write(null, new IIOImage(bi.getData(), null, meta), p); ios.close(); } public void warningOccurred(ImageWriter source, int imageIndex, String warning) { System.out.println("WRITING WARNING: " + warning); } private static IIOMetadata addAdobeTransform(IIOMetadata meta) throws IIOInvalidTreeException { // add the adobe transformation (transform 1 -> to YCbCr) Node root = meta.getAsTree("javax_imageio_jpeg_image_1.0"); NodeList nlist = root.getChildNodes(); Node markerSequence = getChildNode(root, "markerSequence"); if (markerSequence == null) { throw new RuntimeException("Invalid metadata!"); } IIOMetadataNode adobeTransform = getChildNode(markerSequence, "app14Adobe"); if (adobeTransform == null) { adobeTransform = new IIOMetadataNode("app14Adobe"); adobeTransform.setAttribute("transform" , "1"); // convert RGB to YCbCr adobeTransform.setAttribute("version", "101"); adobeTransform.setAttribute("flags0", "0"); adobeTransform.setAttribute("flags1", "0"); markerSequence.appendChild(adobeTransform); } else { adobeTransform.setAttribute("transform" , "1"); } meta.setFromTree("javax_imageio_jpeg_image_1.0", root); return meta; } private static IIOMetadataNode getChildNode(Node p, String name) { NodeList nlist = p.getChildNodes(); for (int i=0; i<nlist.getLength(); i++) { Node n = nlist.item(i); if (name.equals(n.getNodeName())) { return (IIOMetadataNode)n; } } return null; } } ==================== END OF WORKAROUND EXAMPLE =======================