6243376: JPEGImageWriter corrupts color for non-JFIF images with differing sample factor
authorprr
Wed, 04 Feb 2015 10:28:40 -0800
changeset 29007 4dd0e891af6d
parent 29006 0d72c5ab72d8
child 29008 e368342daf04
6243376: JPEGImageWriter corrupts color for non-JFIF images with differing sample factor Reviewed-by: bae, serb
jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/jpeg/JPEGImageWriter.java
jdk/test/javax/imageio/plugins/jpeg/MagentaEXIFTest.java
--- a/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/jpeg/JPEGImageWriter.java	Tue Feb 03 09:28:21 2015 -0800
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/jpeg/JPEGImageWriter.java	Wed Feb 04 10:28:40 2015 -0800
@@ -1652,7 +1652,7 @@
         int vsamp0 = specs[0].VsamplingFactor;
         for (int i = 1; i < specs.length; i++) {
             if ((specs[i].HsamplingFactor != hsamp0) ||
-                (specs[i].HsamplingFactor != hsamp0))
+                (specs[i].VsamplingFactor != vsamp0))
                 return true;
         }
         return false;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/imageio/plugins/jpeg/MagentaEXIFTest.java	Wed Feb 04 10:28:40 2015 -0800
@@ -0,0 +1,205 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @bug     8071707 6243376
+ * @summary Test verifies that EXIF images with differing sampling factors
+ *          are written correctly
+ *
+ * @run     main MagentaEXIFTest
+ */
+
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.image.BufferedImage;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+
+import javax.imageio.IIOImage;
+import javax.imageio.ImageIO;
+import javax.imageio.ImageReader;
+import javax.imageio.ImageTypeSpecifier;
+import javax.imageio.ImageWriteParam;
+import javax.imageio.ImageWriter;
+import javax.imageio.metadata.IIOInvalidTreeException;
+import javax.imageio.metadata.IIOMetadata;
+import javax.imageio.metadata.IIOMetadataNode;
+import javax.imageio.stream.ImageInputStream;
+import javax.imageio.stream.ImageOutputStream;
+
+import org.w3c.dom.Attr;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+
+public class MagentaEXIFTest {
+
+    public static void main(final String[] argv) throws Exception {
+
+        IIOMetadata jpegmetadata = null;
+        ImageWriter jpgWriter = ImageIO.getImageWritersByFormatName("jpg").next();
+        try {
+        jpegmetadata = createJPEGMetadata(jpgWriter);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        ImageOutputStream output = ImageIO.createImageOutputStream(baos);
+        jpgWriter.setOutput(output);
+        int w=100, h=100;
+        BufferedImage bi = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB);
+        Graphics2D g2d = bi.createGraphics();
+        g2d.setColor(Color.white);
+        g2d.fillRect(0, 0, w, h);
+        IIOImage image = new IIOImage(bi, null, jpegmetadata);
+        jpgWriter.write(null, image, null);
+        jpgWriter.dispose();
+
+        baos.flush();
+        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
+        ImageInputStream iis = ImageIO.createImageInputStream(bais);
+        bi = ImageIO.read(iis);
+        for (int i=0; i<bi.getWidth(); i++) {
+            for(int j=0; j<bi.getHeight(); j++) {
+               if (bi.getRGB(i, j) != Color.white.getRGB()) {
+                   throw new RuntimeException("Wrong color : " + Integer.toHexString(bi.getRGB(i, j)));
+               }
+            }
+        }
+
+    }
+
+
+    static void displayMetadata(Node node, int level) {
+        for (int i = 0; i < level; i++) System.out.print("  ");
+        System.out.print("<" + node.getNodeName());
+        NamedNodeMap map = node.getAttributes();
+        if (map != null) { // print attribute values
+            int length = map.getLength();
+            for (int i = 0; i < length; i++) {
+                Node attr = map.item(i);
+                System.out.print(" " + attr.getNodeName() +
+                                 "=\"" + attr.getNodeValue() + "\"");
+            }
+        }
+
+        Node child = node.getFirstChild();
+        if (child != null) {
+            System.out.println(">"); // close current tag
+            while (child != null) { // emit child tags recursively
+                displayMetadata(child, level + 1);
+                child = child.getNextSibling();
+            }
+            for (int i = 0; i < level; i++) System.out.print("  ");
+            System.out.println("</" + node.getNodeName() + ">");
+        } else {
+            System.out.println("/>");
+        }
+    }
+
+    /*
+     * Construct a JPEG IIOMetadata that has had the JFIF marker removed and
+     * an APP1 EXIF marker added, and further massaged so that we have differing
+     * horizontal and vertical sampling factors for one channel.
+     */
+    static IIOMetadata createJPEGMetadata(ImageWriter iw) throws IIOInvalidTreeException {
+        String jpegMDName = "javax_imageio_jpeg_image_1.0";
+        ImageWriter imgWriter = ImageIO.getImageWritersByFormatName("jpg").next();
+        BufferedImage bi = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB);
+        ImageTypeSpecifier ist = new ImageTypeSpecifier(bi);
+        IIOMetadata metadata = imgWriter.getDefaultImageMetadata(ist, null);
+
+        IIOMetadataNode root = new IIOMetadataNode(jpegMDName);
+        IIOMetadataNode header = new IIOMetadataNode("JPEGvariety");
+        IIOMetadataNode sequence = new IIOMetadataNode("markerSequence");
+
+        root.appendChild(header);
+        root.appendChild(sequence);
+
+        IIOMetadataNode app1 = new IIOMetadataNode("unknown");
+        app1.setUserObject(new byte[255]);
+        app1.setAttribute("MarkerTag", "255");
+        sequence.appendChild(app1);
+
+        IIOMetadataNode sof = new IIOMetadataNode("sof");
+        sof.setAttribute("process", "0");
+        sof.setAttribute("samplePrecision", "8");
+        sof.setAttribute("numLines", "100");
+        sof.setAttribute("samplesPerLine", "100");
+        sof.setAttribute("numFrameComponents", "3");
+        IIOMetadataNode c1 = new IIOMetadataNode("componentSpec");
+        c1.setAttribute("componentId", "1");
+        c1.setAttribute("HsamplingFactor", "1");
+        c1.setAttribute("VsamplingFactor", "2");
+        c1.setAttribute("QtableSelector", "1");
+        sof.appendChild(c1);
+        IIOMetadataNode c2 = new IIOMetadataNode("componentSpec");
+        c2.setAttribute("componentId", "2");
+        c2.setAttribute("HsamplingFactor", "1");
+        c2.setAttribute("VsamplingFactor", "1");
+        c2.setAttribute("QtableSelector", "1");
+        sof.appendChild(c2);
+        IIOMetadataNode c3 = new IIOMetadataNode("componentSpec");
+        c3.setAttribute("componentId", "3");
+        c3.setAttribute("HsamplingFactor", "1");
+        c3.setAttribute("VsamplingFactor", "1");
+        c3.setAttribute("QtableSelector", "1");
+        sof.appendChild(c3);
+        sequence.appendChild(sof);
+        metadata.setFromTree(jpegMDName, root);
+        IIOMetadata def = imgWriter.getDefaultImageMetadata(ist, null);
+        metadata.mergeTree(jpegMDName, def.getAsTree(jpegMDName));
+        Node tree = metadata.getAsTree(jpegMDName);
+        Node variety = tree.getFirstChild();
+        Node jfif = variety.getFirstChild();
+        variety.removeChild(jfif);
+        sequence = (IIOMetadataNode)tree.getLastChild();
+        NodeList markers = sequence.getChildNodes();
+        IIOMetadataNode n, sofNode=null;
+        for (int i=0;i<markers.getLength();i++) {
+           n = (IIOMetadataNode)markers.item(i);
+           if (n.getNodeName().equals("sof")) {
+                sofNode = n;
+                break;
+           }
+        }
+        IIOMetadataNode componentSpec = (IIOMetadataNode)sofNode.getFirstChild();
+        Attr attr = componentSpec.getAttributeNode("HsamplingFactor");
+        attr.setValue("1");
+        attr = componentSpec.getAttributeNode("VsamplingFactor");
+        attr.setValue("2");
+        metadata.setFromTree(jpegMDName, tree);
+        String[] names = metadata.getMetadataFormatNames();
+        int length = names.length;
+        for (int i = 0; i < length; i++) {
+            System.out.println( "Format name: " + names[ i ] );
+            displayMetadata(metadata.getAsTree(names[i]), 0);
+        }
+
+        return metadata;
+    }
+}