1 /*
   2  * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /**
  25  * @test
  26  * @bug     8071707 6243376
  27  * @summary Test verifies that EXIF images with differing sampling factors
  28  *          are written correctly
  29  *
  30  * @run     main MagentaEXIFTest
  31  */
  32 
  33 import java.awt.Color;
  34 import java.awt.Graphics2D;
  35 import java.awt.image.BufferedImage;
  36 
  37 import java.io.ByteArrayInputStream;
  38 import java.io.ByteArrayOutputStream;
  39 
  40 import javax.imageio.IIOImage;
  41 import javax.imageio.ImageIO;
  42 import javax.imageio.ImageReader;
  43 import javax.imageio.ImageTypeSpecifier;
  44 import javax.imageio.ImageWriteParam;
  45 import javax.imageio.ImageWriter;
  46 import javax.imageio.metadata.IIOInvalidTreeException;
  47 import javax.imageio.metadata.IIOMetadata;
  48 import javax.imageio.metadata.IIOMetadataNode;
  49 import javax.imageio.stream.ImageInputStream;
  50 import javax.imageio.stream.ImageOutputStream;
  51 
  52 import org.w3c.dom.Attr;
  53 import org.w3c.dom.NamedNodeMap;
  54 import org.w3c.dom.Node;
  55 import org.w3c.dom.NodeList;
  56 
  57 
  58 public class MagentaEXIFTest {
  59 
  60     public static void main(final String[] argv) throws Exception {
  61 
  62         IIOMetadata jpegmetadata = null;
  63         ImageWriter jpgWriter = ImageIO.getImageWritersByFormatName("jpg").next();
  64         try {
  65         jpegmetadata = createJPEGMetadata(jpgWriter);
  66         } catch (Exception e) {
  67             throw new RuntimeException(e);
  68         }
  69 
  70         ByteArrayOutputStream baos = new ByteArrayOutputStream();
  71         ImageOutputStream output = ImageIO.createImageOutputStream(baos);
  72         jpgWriter.setOutput(output);
  73         int w=100, h=100;
  74         BufferedImage bi = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB);
  75         Graphics2D g2d = bi.createGraphics();
  76         g2d.setColor(Color.white);
  77         g2d.fillRect(0, 0, w, h);
  78         IIOImage image = new IIOImage(bi, null, jpegmetadata);
  79         jpgWriter.write(null, image, null);
  80         jpgWriter.dispose();
  81 
  82         baos.flush();
  83         ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
  84         ImageInputStream iis = ImageIO.createImageInputStream(bais);
  85         bi = ImageIO.read(iis);
  86         for (int i=0; i<bi.getWidth(); i++) {
  87             for(int j=0; j<bi.getHeight(); j++) {
  88                if (bi.getRGB(i, j) != Color.white.getRGB()) {
  89                    throw new RuntimeException("Wrong color : " + Integer.toHexString(bi.getRGB(i, j)));
  90                }
  91             }
  92         }
  93 
  94     }
  95 
  96 
  97     static void displayMetadata(Node node, int level) {
  98         for (int i = 0; i < level; i++) System.out.print("  ");
  99         System.out.print("<" + node.getNodeName());
 100         NamedNodeMap map = node.getAttributes();
 101         if (map != null) { // print attribute values
 102             int length = map.getLength();
 103             for (int i = 0; i < length; i++) {
 104                 Node attr = map.item(i);
 105                 System.out.print(" " + attr.getNodeName() +
 106                                  "=\"" + attr.getNodeValue() + "\"");
 107             }
 108         }
 109 
 110         Node child = node.getFirstChild();
 111         if (child != null) {
 112             System.out.println(">"); // close current tag
 113             while (child != null) { // emit child tags recursively
 114                 displayMetadata(child, level + 1);
 115                 child = child.getNextSibling();
 116             }
 117             for (int i = 0; i < level; i++) System.out.print("  ");
 118             System.out.println("</" + node.getNodeName() + ">");
 119         } else {
 120             System.out.println("/>");
 121         }
 122     }
 123 
 124     /*
 125      * Construct a JPEG IIOMetadata that has had the JFIF marker removed and
 126      * an APP1 EXIF marker added, and further massaged so that we have differing
 127      * horizontal and vertical sampling factors for one channel.
 128      */
 129     static IIOMetadata createJPEGMetadata(ImageWriter iw) throws IIOInvalidTreeException {
 130         String jpegMDName = "javax_imageio_jpeg_image_1.0";
 131         ImageWriter imgWriter = ImageIO.getImageWritersByFormatName("jpg").next();
 132         BufferedImage bi = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB);
 133         ImageTypeSpecifier ist = new ImageTypeSpecifier(bi);
 134         IIOMetadata metadata = imgWriter.getDefaultImageMetadata(ist, null);
 135 
 136         IIOMetadataNode root = new IIOMetadataNode(jpegMDName);
 137         IIOMetadataNode header = new IIOMetadataNode("JPEGvariety");
 138         IIOMetadataNode sequence = new IIOMetadataNode("markerSequence");
 139 
 140         root.appendChild(header);
 141         root.appendChild(sequence);
 142 
 143         IIOMetadataNode app1 = new IIOMetadataNode("unknown");
 144         app1.setUserObject(new byte[255]);
 145         app1.setAttribute("MarkerTag", "255");
 146         sequence.appendChild(app1);
 147 
 148         IIOMetadataNode sof = new IIOMetadataNode("sof");
 149         sof.setAttribute("process", "0");
 150         sof.setAttribute("samplePrecision", "8");
 151         sof.setAttribute("numLines", "100");
 152         sof.setAttribute("samplesPerLine", "100");
 153         sof.setAttribute("numFrameComponents", "3");
 154         IIOMetadataNode c1 = new IIOMetadataNode("componentSpec");
 155         c1.setAttribute("componentId", "1");
 156         c1.setAttribute("HsamplingFactor", "1");
 157         c1.setAttribute("VsamplingFactor", "2");
 158         c1.setAttribute("QtableSelector", "1");
 159         sof.appendChild(c1);
 160         IIOMetadataNode c2 = new IIOMetadataNode("componentSpec");
 161         c2.setAttribute("componentId", "2");
 162         c2.setAttribute("HsamplingFactor", "1");
 163         c2.setAttribute("VsamplingFactor", "1");
 164         c2.setAttribute("QtableSelector", "1");
 165         sof.appendChild(c2);
 166         IIOMetadataNode c3 = new IIOMetadataNode("componentSpec");
 167         c3.setAttribute("componentId", "3");
 168         c3.setAttribute("HsamplingFactor", "1");
 169         c3.setAttribute("VsamplingFactor", "1");
 170         c3.setAttribute("QtableSelector", "1");
 171         sof.appendChild(c3);
 172         sequence.appendChild(sof);
 173         metadata.setFromTree(jpegMDName, root);
 174         IIOMetadata def = imgWriter.getDefaultImageMetadata(ist, null);
 175         metadata.mergeTree(jpegMDName, def.getAsTree(jpegMDName));
 176         Node tree = metadata.getAsTree(jpegMDName);
 177         Node variety = tree.getFirstChild();
 178         Node jfif = variety.getFirstChild();
 179         variety.removeChild(jfif);
 180         sequence = (IIOMetadataNode)tree.getLastChild();
 181         NodeList markers = sequence.getChildNodes();
 182         IIOMetadataNode n, sofNode=null;
 183         for (int i=0;i<markers.getLength();i++) {
 184            n = (IIOMetadataNode)markers.item(i);
 185            if (n.getNodeName().equals("sof")) {
 186                 sofNode = n;
 187                 break;
 188            }
 189         }
 190         IIOMetadataNode componentSpec = (IIOMetadataNode)sofNode.getFirstChild();
 191         Attr attr = componentSpec.getAttributeNode("HsamplingFactor");
 192         attr.setValue("1");
 193         attr = componentSpec.getAttributeNode("VsamplingFactor");
 194         attr.setValue("2");
 195         metadata.setFromTree(jpegMDName, tree);
 196         String[] names = metadata.getMetadataFormatNames();
 197         int length = names.length;
 198         for (int i = 0; i < length; i++) {
 199             System.out.println( "Format name: " + names[ i ] );
 200             displayMetadata(metadata.getAsTree(names[i]), 0);
 201         }
 202 
 203         return metadata;
 204     }
 205 }