/*
* Copyright (c) 2007, 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 6198111
* @summary Test verifies that PNG image reader correctly handles
* hIST chunk if length of image palette in not power of two.
*
* @run main ShortHistogramTest 15
*/
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
import java.awt.image.IndexColorModel;
import java.awt.image.Raster;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.Random;
import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
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.ImageOutputStream;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
public class ShortHistogramTest {
public static void main(String[] args) throws IOException {
int numColors = 15;
if (args.length > 0) {
try {
numColors = Integer.parseInt(args[0]);
} catch (NumberFormatException e) {
System.out.println("Invalid number of colors: " + args[0]);
}
}
System.out.println("Test number of colors: " + numColors);
ShortHistogramTest t = new ShortHistogramTest(numColors);
t.doTest();
}
int numColors;
public ShortHistogramTest(int numColors) {
this.numColors = numColors;
}
public void doTest() throws IOException {
BufferedImage bi = createTestImage(numColors);
File f = writeImageWithHist(bi);
System.out.println("Test file is " + f.getCanonicalPath());
try {
ImageIO.read(f);
} catch (IOException e) {
throw new RuntimeException("Test FAILED!", e);
}
System.out.println("Test PASSED!");
}
protected File writeImageWithHist(BufferedImage bi) throws IOException {
File f = File.createTempFile("hist_", ".png", new File("."));
ImageWriter writer = ImageIO.getImageWritersByFormatName("PNG").next();
ImageOutputStream ios = ImageIO.createImageOutputStream(f);
writer.setOutput(ios);
ImageWriteParam param = writer.getDefaultWriteParam();
ImageTypeSpecifier type = new ImageTypeSpecifier(bi);
IIOMetadata imgMetadata = writer.getDefaultImageMetadata(type, param);
/* add hIST node to image metadata */
imgMetadata = upgradeMetadata(imgMetadata, bi);
IIOImage iio_img = new IIOImage(bi,
null, // no thumbnails
imgMetadata);
writer.write(iio_img);
ios.flush();
ios.close();
return f;
}
private IIOMetadata upgradeMetadata(IIOMetadata src, BufferedImage bi) {
String format = src.getNativeMetadataFormatName();
System.out.println("Native format: " + format);
Node root = src.getAsTree(format);
// add hIST node
Node n = lookupChildNode(root, "hIST");
if (n == null) {
System.out.println("Appending new hIST node...");
Node hIST = gethISTNode(bi);
root.appendChild(hIST);
}
System.out.println("Upgraded metadata tree:");
dump(root, "");
System.out.println("Merging metadata...");
try {
src.mergeTree(format, root);
} catch (IIOInvalidTreeException e) {
throw new RuntimeException("Test FAILED!", e);
}
return src;
}
private IIOMetadataNode gethISTNode(BufferedImage bi) {
IndexColorModel icm = (IndexColorModel)bi.getColorModel();
int mapSize = icm.getMapSize();
int[] hist = new int[mapSize];
Arrays.fill(hist, 0);
Raster r = bi.getData();
for (int y = 0; y < bi.getHeight(); y++) {
for (int x = 0; x < bi.getWidth(); x++) {
int s = r.getSample(x, y, 0);
hist[s] ++;
}
}
IIOMetadataNode hIST = new IIOMetadataNode("hIST");
for (int i = 0; i < hist.length; i++) {
IIOMetadataNode n = new IIOMetadataNode("hISTEntry");
n.setAttribute("index", "" + i);
n.setAttribute("value", "" + hist[i]);
hIST.appendChild(n);
}
return hIST;
}
private static Node lookupChildNode(Node root, String name) {
Node n = root.getFirstChild();
while (n != null && !name.equals(n.getNodeName())) {
n = n.getNextSibling();
}
return n;
}
private static void dump(Node node, String ident) {
if (node == null) {
return;
}
System.out.printf("%s%s\n", ident, node.getNodeName());
// dump node attributes...
NamedNodeMap attribs = node.getAttributes();
if (attribs != null) {
for (int i = 0; i < attribs.getLength(); i++) {
Node a = attribs.item(i);
System.out.printf("%s %s: %s\n", ident,
a.getNodeName(), a.getNodeValue());
}
}
// dump node children...
dump(node.getFirstChild(), ident + " ");
dump(node.getNextSibling(), ident);
}
protected BufferedImage createTestImage(int numColors) {
IndexColorModel icm = createTestICM(numColors);
int w = numColors * 10;
int h = 20;
BufferedImage img = new BufferedImage(w, h,
BufferedImage.TYPE_BYTE_INDEXED, icm);
Graphics2D g = img.createGraphics();
for (int i = 0; i < numColors; i++) {
int rgb = icm.getRGB(i);
//System.out.printf("pixel %d, rgb %x\n", i, rgb);
g.setColor(new Color(rgb));
g.fillRect(i * 10, 0, w - i * 10, h);
}
g.dispose();
return img;
}
protected IndexColorModel createTestICM(int numColors) {
int[] palette = createTestPalette(numColors);
int numBits = getNumBits(numColors);
IndexColorModel icm = new IndexColorModel(numBits, numColors,
palette, 0, false, -1,
DataBuffer.TYPE_BYTE);
return icm;
}
protected static int getNumBits(int numColors) {
if (numColors < 0 || 256 < numColors) {
throw new RuntimeException("Unsupported number of colors: " +
numColors);
}
int numBits = 1;
int limit = 1 << numBits;
while (numColors > limit) {
numBits++;
limit = 1 << numBits;
}
return numBits;
}
private static Random rnd = new Random();
protected static int[] createTestPalette(int numColors) {
int[] palette = new int[numColors];
for (int i = 0; i < numColors; i++) {
int r = rnd.nextInt(256);
int g = rnd.nextInt(256);
int b = rnd.nextInt(256);
palette[i] = 0xff000000 | (r << 16) | (g << 8) | b;
}
return palette;
}
}