0001: /*
0002: * $RCSfile: BMPMetadata.java,v $
0003: *
0004: *
0005: * Copyright (c) 2005 Sun Microsystems, Inc. All Rights Reserved.
0006: *
0007: * Redistribution and use in source and binary forms, with or without
0008: * modification, are permitted provided that the following conditions
0009: * are met:
0010: *
0011: * - Redistribution of source code must retain the above copyright
0012: * notice, this list of conditions and the following disclaimer.
0013: *
0014: * - Redistribution in binary form must reproduce the above copyright
0015: * notice, this list of conditions and the following disclaimer in
0016: * the documentation and/or other materials provided with the
0017: * distribution.
0018: *
0019: * Neither the name of Sun Microsystems, Inc. or the names of
0020: * contributors may be used to endorse or promote products derived
0021: * from this software without specific prior written permission.
0022: *
0023: * This software is provided "AS IS," without a warranty of any
0024: * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
0025: * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
0026: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
0027: * EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
0028: * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
0029: * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
0030: * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
0031: * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
0032: * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
0033: * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
0034: * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
0035: * POSSIBILITY OF SUCH DAMAGES.
0036: *
0037: * You acknowledge that this software is not designed or intended for
0038: * use in the design, construction, operation or maintenance of any
0039: * nuclear facility.
0040: *
0041: * $Revision: 1.2 $
0042: * $Date: 2006/04/21 23:14:37 $
0043: * $State: Exp $
0044: */
0045: package com.sun.media.imageioimpl.plugins.bmp;
0046:
0047: import java.io.UnsupportedEncodingException;
0048: import java.awt.image.ColorModel;
0049: import java.awt.image.DirectColorModel;
0050: import java.awt.image.IndexColorModel;
0051: import java.awt.image.SampleModel;
0052: import java.util.ArrayList;
0053: import java.util.Arrays;
0054: import java.util.Iterator;
0055: import java.util.List;
0056: import java.util.StringTokenizer;
0057: import javax.imageio.ImageTypeSpecifier;
0058: import javax.imageio.ImageWriteParam;
0059: import javax.imageio.metadata.IIOInvalidTreeException;
0060: import javax.imageio.metadata.IIOMetadata;
0061: import javax.imageio.metadata.IIOMetadataNode;
0062: import javax.imageio.metadata.IIOMetadataFormat;
0063: import javax.imageio.metadata.IIOMetadataFormatImpl;
0064: import org.w3c.dom.Node;
0065: import com.sun.media.imageio.plugins.bmp.BMPImageWriteParam;
0066: import com.sun.media.imageioimpl.common.ImageUtil;
0067:
0068: public class BMPMetadata extends IIOMetadata implements Cloneable,
0069: BMPConstants {
0070:
0071: public static final String nativeMetadataFormatName = "com_sun_media_imageio_plugins_bmp_image_1.0";
0072:
0073: // Fields for Image Descriptor
0074: public String bmpVersion;
0075: public int width;
0076: public int height;
0077: public short bitsPerPixel;
0078: public int compression;
0079: public int imageSize;
0080:
0081: // Fields for PixelsPerMeter
0082: public int xPixelsPerMeter;
0083: public int yPixelsPerMeter;
0084:
0085: public int colorsUsed;
0086: public int colorsImportant;
0087:
0088: // Fields for BI_BITFIELDS compression(Mask)
0089: public int redMask;
0090: public int greenMask;
0091: public int blueMask;
0092: public int alphaMask;
0093:
0094: public int colorSpace;
0095:
0096: // Fields for CIE XYZ for the LCS_CALIBRATED_RGB color space
0097: public double redX;
0098: public double redY;
0099: public double redZ;
0100: public double greenX;
0101: public double greenY;
0102: public double greenZ;
0103: public double blueX;
0104: public double blueY;
0105: public double blueZ;
0106:
0107: // Fields for Gamma values for the LCS_CALIBRATED_RGB color space
0108: public int gammaRed;
0109: public int gammaGreen;
0110: public int gammaBlue;
0111:
0112: public int intent;
0113:
0114: // Fields for the Palette and Entries
0115: public byte[] palette = null;
0116: public int paletteSize;
0117: public int red;
0118: public int green;
0119: public int blue;
0120:
0121: // Fields from CommentExtension
0122: // List of String
0123: public List comments = null; // new ArrayList();
0124:
0125: public BMPMetadata() {
0126: super (true, nativeMetadataFormatName,
0127: "com.sun.media.imageioimpl.bmp.BMPMetadataFormat",
0128: null, null);
0129: }
0130:
0131: public BMPMetadata(IIOMetadata metadata)
0132: throws IIOInvalidTreeException {
0133:
0134: this ();
0135:
0136: if (metadata != null) {
0137: List formats = Arrays.asList(metadata
0138: .getMetadataFormatNames());
0139:
0140: if (formats.contains(nativeMetadataFormatName)) {
0141: // Initialize from native image metadata format.
0142: setFromTree(nativeMetadataFormatName, metadata
0143: .getAsTree(nativeMetadataFormatName));
0144: } else if (metadata.isStandardMetadataFormatSupported()) {
0145: // Initialize from standard metadata form of the input tree.
0146: String format = IIOMetadataFormatImpl.standardMetadataFormatName;
0147: setFromTree(format, metadata.getAsTree(format));
0148: }
0149: }
0150: }
0151:
0152: public boolean isReadOnly() {
0153: return false;
0154: }
0155:
0156: public Object clone() {
0157: BMPMetadata metadata;
0158: try {
0159: metadata = (BMPMetadata) super .clone();
0160: } catch (CloneNotSupportedException e) {
0161: return null;
0162: }
0163:
0164: return metadata;
0165: }
0166:
0167: public Node getAsTree(String formatName) {
0168: if (formatName.equals(nativeMetadataFormatName)) {
0169: return getNativeTree();
0170: } else if (formatName
0171: .equals(IIOMetadataFormatImpl.standardMetadataFormatName)) {
0172: return getStandardTree();
0173: } else {
0174: throw new IllegalArgumentException(I18N
0175: .getString("BMPMetadata0"));
0176: }
0177: }
0178:
0179: private Node getNativeTree() {
0180: IIOMetadataNode root = new IIOMetadataNode(
0181: nativeMetadataFormatName);
0182:
0183: addChildNode(root, "BMPVersion", bmpVersion);
0184: addChildNode(root, "Width", new Integer(width));
0185: addChildNode(root, "Height", new Integer(height));
0186: addChildNode(root, "BitsPerPixel", new Short(bitsPerPixel));
0187: addChildNode(root, "Compression", new Integer(compression));
0188: addChildNode(root, "ImageSize", new Integer(imageSize));
0189:
0190: IIOMetadataNode node;
0191: if (xPixelsPerMeter > 0 && yPixelsPerMeter > 0) {
0192: node = addChildNode(root, "PixelsPerMeter", null);
0193: addChildNode(node, "X", new Integer(xPixelsPerMeter));
0194: addChildNode(node, "Y", new Integer(yPixelsPerMeter));
0195: }
0196:
0197: addChildNode(root, "ColorsUsed", new Integer(colorsUsed));
0198: addChildNode(root, "ColorsImportant", new Integer(
0199: colorsImportant));
0200:
0201: int version = 0;
0202: for (int i = 0; i < bmpVersion.length(); i++)
0203: if (Character.isDigit(bmpVersion.charAt(i)))
0204: version = bmpVersion.charAt(i) - '0';
0205:
0206: if (version >= 4) {
0207: node = addChildNode(root, "Mask", null);
0208: addChildNode(node, "Red", new Integer(redMask));
0209: addChildNode(node, "Green", new Integer(greenMask));
0210: addChildNode(node, "Blue", new Integer(blueMask));
0211: addChildNode(node, "Alpha", new Integer(alphaMask));
0212:
0213: addChildNode(root, "ColorSpaceType",
0214: new Integer(colorSpace));
0215:
0216: node = addChildNode(root, "CIEXYZEndpoints", null);
0217: addXYZPoints(node, "Red", redX, redY, redZ);
0218: addXYZPoints(node, "Green", greenX, greenY, greenZ);
0219: addXYZPoints(node, "Blue", blueX, blueY, blueZ);
0220:
0221: node = addChildNode(root, "Gamma", null);
0222: addChildNode(node, "Red", new Integer(gammaRed));
0223: addChildNode(node, "Green", new Integer(gammaGreen));
0224: addChildNode(node, "Blue", new Integer(gammaBlue));
0225:
0226: node = addChildNode(root, "Intent", new Integer(intent));
0227: }
0228:
0229: // Palette
0230: if ((palette != null) && (paletteSize > 0)) {
0231: node = addChildNode(root, "Palette", null);
0232: boolean isVersion2 = bmpVersion != null
0233: && bmpVersion.equals(VERSION_2);
0234:
0235: for (int i = 0, j = 0; i < paletteSize; i++) {
0236: IIOMetadataNode entry = addChildNode(node,
0237: "PaletteEntry", null);
0238: blue = palette[j++] & 0xff;
0239: green = palette[j++] & 0xff;
0240: red = palette[j++] & 0xff;
0241: addChildNode(entry, "Red", new Integer(red));
0242: addChildNode(entry, "Green", new Integer(green));
0243: addChildNode(entry, "Blue", new Integer(blue));
0244: if (!isVersion2)
0245: j++; // skip reserved entry
0246: }
0247: }
0248:
0249: return root;
0250: }
0251:
0252: // Standard tree node methods
0253: protected IIOMetadataNode getStandardChromaNode() {
0254:
0255: IIOMetadataNode node = new IIOMetadataNode("Chroma");
0256:
0257: IIOMetadataNode subNode = new IIOMetadataNode("ColorSpaceType");
0258: String colorSpaceType;
0259: if (((palette != null) && (paletteSize > 0))
0260: || (redMask != 0 || greenMask != 0 || blueMask != 0)
0261: || bitsPerPixel > 8) {
0262: colorSpaceType = "RGB";
0263: } else {
0264: colorSpaceType = "GRAY";
0265: }
0266: subNode.setAttribute("name", colorSpaceType);
0267: node.appendChild(subNode);
0268:
0269: subNode = new IIOMetadataNode("NumChannels");
0270: String numChannels;
0271: if (((palette != null) && (paletteSize > 0))
0272: || (redMask != 0 || greenMask != 0 || blueMask != 0)
0273: || bitsPerPixel > 8) {
0274: if (alphaMask != 0) {
0275: numChannels = "4";
0276: } else {
0277: numChannels = "3";
0278: }
0279: } else {
0280: numChannels = "1";
0281: }
0282: subNode.setAttribute("value", numChannels);
0283: node.appendChild(subNode);
0284:
0285: if (gammaRed != 0 && gammaGreen != 0 && gammaBlue != 0) {
0286: subNode = new IIOMetadataNode("Gamma");
0287: Double gamma = new Double(
0288: (gammaRed + gammaGreen + gammaBlue) / 3.0);
0289: subNode.setAttribute("value", gamma.toString());
0290: node.appendChild(subNode);
0291: }
0292:
0293: if (numChannels.equals("1")
0294: && (palette == null || paletteSize == 0)) {
0295: subNode = new IIOMetadataNode("BlackIsZero");
0296: subNode.setAttribute("value", "TRUE");
0297: node.appendChild(subNode);
0298: }
0299:
0300: if ((palette != null) && (paletteSize > 0)) {
0301: subNode = new IIOMetadataNode("Palette");
0302: boolean isVersion2 = bmpVersion != null
0303: && bmpVersion.equals(VERSION_2);
0304:
0305: for (int i = 0, j = 0; i < paletteSize; i++) {
0306: IIOMetadataNode subNode1 = new IIOMetadataNode(
0307: "PaletteEntry");
0308: subNode1.setAttribute("index", "" + i);
0309: subNode1.setAttribute("blue", ""
0310: + (palette[j++] & 0xff));
0311: subNode1.setAttribute("green", ""
0312: + (palette[j++] & 0xff));
0313: subNode1
0314: .setAttribute("red", "" + (palette[j++] & 0xff));
0315: if (!isVersion2)
0316: j++; // skip reserved entry
0317: subNode.appendChild(subNode1);
0318: }
0319: node.appendChild(subNode);
0320: }
0321:
0322: return node;
0323: }
0324:
0325: protected IIOMetadataNode getStandardCompressionNode() {
0326: IIOMetadataNode node = new IIOMetadataNode("Compression");
0327:
0328: // CompressionTypeName
0329: IIOMetadataNode subNode = new IIOMetadataNode(
0330: "CompressionTypeName");
0331: subNode
0332: .setAttribute("value",
0333: compressionTypeNames[compression]);
0334: node.appendChild(subNode);
0335:
0336: subNode = new IIOMetadataNode("Lossless");
0337: subNode.setAttribute("value", compression == BI_JPEG ? "FALSE"
0338: : "TRUE");
0339: node.appendChild(subNode);
0340:
0341: return node;
0342: }
0343:
0344: protected IIOMetadataNode getStandardDataNode() {
0345: IIOMetadataNode node = new IIOMetadataNode("Data");
0346:
0347: String sampleFormat = (palette != null) && (paletteSize > 0) ? "Index"
0348: : "UnsignedIntegral";
0349: IIOMetadataNode subNode = new IIOMetadataNode("SampleFormat");
0350: subNode.setAttribute("value", sampleFormat);
0351: node.appendChild(subNode);
0352:
0353: String bits = "";
0354: if (redMask != 0 || greenMask != 0 || blueMask != 0) {
0355: bits = countBits(redMask) + " " + countBits(greenMask)
0356: + " " + countBits(blueMask);
0357: if (alphaMask != 0) {
0358: bits += " " + countBits(alphaMask);
0359: }
0360: } else if (palette != null && paletteSize > 0) {
0361: for (int i = 1; i <= 3; i++) {
0362: bits += bitsPerPixel;
0363: if (i != 3) {
0364: bits += " ";
0365: }
0366: }
0367: } else {
0368: if (bitsPerPixel == 1) {
0369: bits = "1";
0370: } else if (bitsPerPixel == 4) {
0371: bits = "4";
0372: } else if (bitsPerPixel == 8) {
0373: bits = "8";
0374: } else if (bitsPerPixel == 16) {
0375: bits = "5 6 5";
0376: } else if (bitsPerPixel == 24) {
0377: bits = "8 8 8";
0378: } else if (bitsPerPixel == 32) {
0379: bits = "8 8 8 8";
0380: }
0381: }
0382:
0383: if (!bits.equals("")) {
0384: subNode = new IIOMetadataNode("BitsPerSample");
0385: subNode.setAttribute("value", bits);
0386: node.appendChild(subNode);
0387: }
0388:
0389: return node;
0390: }
0391:
0392: protected IIOMetadataNode getStandardDimensionNode() {
0393: if (yPixelsPerMeter > 0 && xPixelsPerMeter > 0) {
0394: IIOMetadataNode node = new IIOMetadataNode("Dimension");
0395: float ratio = (float) yPixelsPerMeter
0396: / (float) xPixelsPerMeter;
0397: IIOMetadataNode subNode = new IIOMetadataNode(
0398: "PixelAspectRatio");
0399: subNode.setAttribute("value", "" + ratio);
0400: node.appendChild(subNode);
0401:
0402: subNode = new IIOMetadataNode("HorizontalPixelSize");
0403: subNode.setAttribute("value", ""
0404: + (1000.0F / xPixelsPerMeter));
0405: node.appendChild(subNode);
0406:
0407: subNode = new IIOMetadataNode("VerticalPixelSize");
0408: subNode.setAttribute("value", ""
0409: + (1000.0F / yPixelsPerMeter));
0410: node.appendChild(subNode);
0411:
0412: // Emit HorizontalPhysicalPixelSpacing and
0413: // VerticalPhysicalPixelSpacing for historical reasonse:
0414: // HorizontalPixelSize and VerticalPixelSize should have
0415: // been used in the first place.
0416: subNode = new IIOMetadataNode(
0417: "HorizontalPhysicalPixelSpacing");
0418: subNode.setAttribute("value", ""
0419: + (1000.0F / xPixelsPerMeter));
0420: node.appendChild(subNode);
0421:
0422: subNode = new IIOMetadataNode(
0423: "VerticalPhysicalPixelSpacing");
0424: subNode.setAttribute("value", ""
0425: + (1000.0F / yPixelsPerMeter));
0426: node.appendChild(subNode);
0427:
0428: return node;
0429: }
0430: return null;
0431: }
0432:
0433: protected IIOMetadataNode getStandardDocumentNode() {
0434: if (bmpVersion != null) {
0435: IIOMetadataNode node = new IIOMetadataNode("Document");
0436: IIOMetadataNode subNode = new IIOMetadataNode(
0437: "FormatVersion");
0438: subNode.setAttribute("value", bmpVersion);
0439: node.appendChild(subNode);
0440: return node;
0441: }
0442: return null;
0443: }
0444:
0445: protected IIOMetadataNode getStandardTextNode() {
0446: if (comments != null) {
0447: IIOMetadataNode node = new IIOMetadataNode("Text");
0448: Iterator iter = comments.iterator();
0449: while (iter.hasNext()) {
0450: String comment = (String) iter.next();
0451: IIOMetadataNode subNode = new IIOMetadataNode(
0452: "TextEntry");
0453: subNode.setAttribute("keyword", "comment");
0454: subNode.setAttribute("value", comment);
0455: node.appendChild(subNode);
0456: }
0457: return node;
0458: }
0459: return null;
0460: }
0461:
0462: protected IIOMetadataNode getStandardTransparencyNode() {
0463: IIOMetadataNode node = new IIOMetadataNode("Transparency");
0464: IIOMetadataNode subNode = new IIOMetadataNode("Alpha");
0465: String alpha;
0466: if (alphaMask != 0) {
0467: alpha = "nonpremultiplied";
0468: } else {
0469: alpha = "none";
0470: }
0471:
0472: subNode.setAttribute("value", alpha);
0473: node.appendChild(subNode);
0474: return node;
0475: }
0476:
0477: // Shorthand for throwing an IIOInvalidTreeException
0478: private void fatal(Node node, String reason)
0479: throws IIOInvalidTreeException {
0480: throw new IIOInvalidTreeException(reason, node);
0481: }
0482:
0483: // Get an integer-valued attribute
0484: private int getIntAttribute(Node node, String name,
0485: int defaultValue, boolean required)
0486: throws IIOInvalidTreeException {
0487: String value = getAttribute(node, name, null, required);
0488: if (value == null) {
0489: return defaultValue;
0490: }
0491: return Integer.parseInt(value);
0492: }
0493:
0494: // Get a double-valued attribute
0495: private double getDoubleAttribute(Node node, String name,
0496: double defaultValue, boolean required)
0497: throws IIOInvalidTreeException {
0498: String value = getAttribute(node, name, null, required);
0499: if (value == null) {
0500: return defaultValue;
0501: }
0502: return Double.parseDouble(value);
0503: }
0504:
0505: // Get a required integer-valued attribute
0506: private int getIntAttribute(Node node, String name)
0507: throws IIOInvalidTreeException {
0508: return getIntAttribute(node, name, -1, true);
0509: }
0510:
0511: // Get a required double-valued attribute
0512: private double getDoubleAttribute(Node node, String name)
0513: throws IIOInvalidTreeException {
0514: return getDoubleAttribute(node, name, -1.0F, true);
0515: }
0516:
0517: // Get a String-valued attribute
0518: private String getAttribute(Node node, String name,
0519: String defaultValue, boolean required)
0520: throws IIOInvalidTreeException {
0521: Node attr = node.getAttributes().getNamedItem(name);
0522: if (attr == null) {
0523: if (!required) {
0524: return defaultValue;
0525: } else {
0526: fatal(node, "Required attribute " + name
0527: + " not present!");
0528: }
0529: }
0530: return attr.getNodeValue();
0531: }
0532:
0533: // Get a required String-valued attribute
0534: private String getAttribute(Node node, String name)
0535: throws IIOInvalidTreeException {
0536: return getAttribute(node, name, null, true);
0537: }
0538:
0539: void initialize(ColorModel cm, SampleModel sm, ImageWriteParam param) {
0540: // bmpVersion and compression.
0541: if (param != null) {
0542: bmpVersion = BMPConstants.VERSION_3;
0543:
0544: if (param.getCompressionMode() == ImageWriteParam.MODE_EXPLICIT) {
0545: String compressionType = param.getCompressionType();
0546: compression = BMPImageWriter
0547: .getCompressionType(compressionType);
0548: }
0549: } else {
0550: bmpVersion = BMPConstants.VERSION_3;
0551: compression = BMPImageWriter.getPreferredCompressionType(
0552: cm, sm);
0553: }
0554:
0555: // width and height
0556: width = sm.getWidth();
0557: height = sm.getHeight();
0558:
0559: // bitsPerPixel
0560: bitsPerPixel = (short) cm.getPixelSize();
0561:
0562: // mask
0563: if (cm instanceof DirectColorModel) {
0564: DirectColorModel dcm = (DirectColorModel) cm;
0565: redMask = dcm.getRedMask();
0566: greenMask = dcm.getGreenMask();
0567: blueMask = dcm.getBlueMask();
0568: alphaMask = dcm.getAlphaMask();
0569: }
0570:
0571: // palette and paletteSize
0572: if (cm instanceof IndexColorModel) {
0573: IndexColorModel icm = (IndexColorModel) cm;
0574: paletteSize = icm.getMapSize();
0575:
0576: byte[] r = new byte[paletteSize];
0577: byte[] g = new byte[paletteSize];
0578: byte[] b = new byte[paletteSize];
0579:
0580: icm.getReds(r);
0581: icm.getGreens(g);
0582: icm.getBlues(b);
0583:
0584: boolean isVersion2 = bmpVersion != null
0585: && bmpVersion.equals(VERSION_2);
0586:
0587: palette = new byte[(isVersion2 ? 3 : 4) * paletteSize];
0588: for (int i = 0, j = 0; i < paletteSize; i++) {
0589: palette[j++] = b[i];
0590: palette[j++] = g[i];
0591: palette[j++] = r[i];
0592: if (!isVersion2)
0593: j++; // skip reserved entry
0594: }
0595: }
0596: }
0597:
0598: public void mergeTree(String formatName, Node root)
0599: throws IIOInvalidTreeException {
0600: if (formatName.equals(nativeMetadataFormatName)) {
0601: if (root == null) {
0602: throw new IllegalArgumentException("root == null!");
0603: }
0604: mergeNativeTree(root);
0605: } else if (formatName
0606: .equals(IIOMetadataFormatImpl.standardMetadataFormatName)) {
0607: if (root == null) {
0608: throw new IllegalArgumentException("root == null!");
0609: }
0610: mergeStandardTree(root);
0611: } else {
0612: throw new IllegalArgumentException(
0613: "Not a recognized format!");
0614: }
0615: }
0616:
0617: private void mergeNativeTree(Node root)
0618: throws IIOInvalidTreeException {
0619: Node node = root;
0620: if (!node.getNodeName().equals(nativeMetadataFormatName)) {
0621: fatal(node, "Root must be " + nativeMetadataFormatName);
0622: }
0623:
0624: byte[] r = null, g = null, b = null;
0625: int maxIndex = -1;
0626:
0627: node = node.getFirstChild();
0628: while (node != null) {
0629: String name = node.getNodeName();
0630:
0631: if (name.equals("BMPVersion")) {
0632: String value = getStringValue(node);
0633: if (value != null)
0634: bmpVersion = value;
0635: } else if (name.equals("Width")) {
0636: Integer value = getIntegerValue(node);
0637: if (value != null)
0638: width = value.intValue();
0639: } else if (name.equals("Height")) {
0640: Integer value = getIntegerValue(node);
0641: if (value != null)
0642: height = value.intValue();
0643: } else if (name.equals("BitsPerPixel")) {
0644: Short value = getShortValue(node);
0645: if (value != null)
0646: bitsPerPixel = value.shortValue();
0647: } else if (name.equals("Compression")) {
0648: Integer value = getIntegerValue(node);
0649: if (value != null)
0650: compression = value.intValue();
0651: } else if (name.equals("ImageSize")) {
0652: Integer value = getIntegerValue(node);
0653: if (value != null)
0654: imageSize = value.intValue();
0655: } else if (name.equals("PixelsPerMeter")) {
0656: Node subNode = node.getFirstChild();
0657: while (subNode != null) {
0658: String subName = subNode.getNodeName();
0659: if (subName.equals("X")) {
0660: Integer value = getIntegerValue(subNode);
0661: if (value != null)
0662: xPixelsPerMeter = value.intValue();
0663: } else if (subName.equals("Y")) {
0664: Integer value = getIntegerValue(subNode);
0665: if (value != null)
0666: yPixelsPerMeter = value.intValue();
0667: }
0668: subNode = subNode.getNextSibling();
0669: }
0670: } else if (name.equals("ColorsUsed")) {
0671: Integer value = getIntegerValue(node);
0672: if (value != null)
0673: colorsUsed = value.intValue();
0674: } else if (name.equals("ColorsImportant")) {
0675: Integer value = getIntegerValue(node);
0676: if (value != null)
0677: colorsImportant = value.intValue();
0678: } else if (name.equals("Mask")) {
0679: Node subNode = node.getFirstChild();
0680: while (subNode != null) {
0681: String subName = subNode.getNodeName();
0682: if (subName.equals("Red")) {
0683: Integer value = getIntegerValue(subNode);
0684: if (value != null)
0685: redMask = value.intValue();
0686: } else if (subName.equals("Green")) {
0687: Integer value = getIntegerValue(subNode);
0688: if (value != null)
0689: greenMask = value.intValue();
0690: } else if (subName.equals("Blue")) {
0691: Integer value = getIntegerValue(subNode);
0692: if (value != null)
0693: blueMask = value.intValue();
0694: } else if (subName.equals("Alpha")) {
0695: Integer value = getIntegerValue(subNode);
0696: if (value != null)
0697: alphaMask = value.intValue();
0698: }
0699: subNode = subNode.getNextSibling();
0700: }
0701: } else if (name.equals("ColorSpace")) {
0702: Integer value = getIntegerValue(node);
0703: if (value != null)
0704: colorSpace = value.intValue();
0705: } else if (name.equals("CIEXYZEndpoints")) {
0706: Node subNode = node.getFirstChild();
0707: while (subNode != null) {
0708: String subName = subNode.getNodeName();
0709: if (subName.equals("Red")) {
0710: Node subNode1 = subNode.getFirstChild();
0711: while (subNode1 != null) {
0712: String subName1 = subNode1.getNodeName();
0713: if (subName1.equals("X")) {
0714: Double value = getDoubleValue(subNode1);
0715: if (value != null)
0716: redX = value.doubleValue();
0717: } else if (subName1.equals("Y")) {
0718: Double value = getDoubleValue(subNode1);
0719: if (value != null)
0720: redY = value.doubleValue();
0721: } else if (subName1.equals("Z")) {
0722: Double value = getDoubleValue(subNode1);
0723: if (value != null)
0724: redZ = value.doubleValue();
0725: }
0726: subNode1 = subNode1.getNextSibling();
0727: }
0728: } else if (subName.equals("Green")) {
0729: Node subNode1 = subNode.getFirstChild();
0730: while (subNode1 != null) {
0731: String subName1 = subNode1.getNodeName();
0732: if (subName1.equals("X")) {
0733: Double value = getDoubleValue(subNode1);
0734: if (value != null)
0735: greenX = value.doubleValue();
0736: } else if (subName1.equals("Y")) {
0737: Double value = getDoubleValue(subNode1);
0738: if (value != null)
0739: greenY = value.doubleValue();
0740: } else if (subName1.equals("Z")) {
0741: Double value = getDoubleValue(subNode1);
0742: if (value != null)
0743: greenZ = value.doubleValue();
0744: }
0745: subNode1 = subNode1.getNextSibling();
0746: }
0747: } else if (subName.equals("Blue")) {
0748: Node subNode1 = subNode.getFirstChild();
0749: while (subNode1 != null) {
0750: String subName1 = subNode1.getNodeName();
0751: if (subName1.equals("X")) {
0752: Double value = getDoubleValue(subNode1);
0753: if (value != null)
0754: blueX = value.doubleValue();
0755: } else if (subName1.equals("Y")) {
0756: Double value = getDoubleValue(subNode1);
0757: if (value != null)
0758: blueY = value.doubleValue();
0759: } else if (subName1.equals("Z")) {
0760: Double value = getDoubleValue(subNode1);
0761: if (value != null)
0762: blueZ = value.doubleValue();
0763: }
0764: subNode1 = subNode1.getNextSibling();
0765: }
0766: }
0767: subNode = subNode.getNextSibling();
0768: }
0769: } else if (name.equals("Gamma")) {
0770: Node subNode = node.getFirstChild();
0771: while (subNode != null) {
0772: String subName = subNode.getNodeName();
0773: if (subName.equals("Red")) {
0774: Integer value = getIntegerValue(subNode);
0775: if (value != null)
0776: gammaRed = value.intValue();
0777: } else if (subName.equals("Green")) {
0778: Integer value = getIntegerValue(subNode);
0779: if (value != null)
0780: gammaGreen = value.intValue();
0781: } else if (subName.equals("Blue")) {
0782: Integer value = getIntegerValue(subNode);
0783: if (value != null)
0784: gammaBlue = value.intValue();
0785: }
0786: subNode = subNode.getNextSibling();
0787: }
0788: } else if (name.equals("Intent")) {
0789: Integer value = getIntegerValue(node);
0790: if (value != null)
0791: intent = value.intValue();
0792: } else if (name.equals("Palette")) {
0793: paletteSize = getIntAttribute(node, "sizeOfPalette");
0794:
0795: r = new byte[paletteSize];
0796: g = new byte[paletteSize];
0797: b = new byte[paletteSize];
0798: maxIndex = -1;
0799:
0800: Node paletteEntry = node.getFirstChild();
0801: if (paletteEntry == null) {
0802: fatal(node, "Palette has no entries!");
0803: }
0804:
0805: int numPaletteEntries = 0;
0806: while (paletteEntry != null) {
0807: if (!paletteEntry.getNodeName().equals(
0808: "PaletteEntry")) {
0809: fatal(node,
0810: "Only a PaletteEntry may be a child of a Palette!");
0811: }
0812:
0813: int index = -1;
0814: Node subNode = paletteEntry.getFirstChild();
0815: while (subNode != null) {
0816: String subName = subNode.getNodeName();
0817: if (subName.equals("Index")) {
0818: Integer value = getIntegerValue(subNode);
0819: if (value != null)
0820: index = value.intValue();
0821: if (index < 0 || index > paletteSize - 1) {
0822: fatal(node,
0823: "Bad value for PaletteEntry attribute index!");
0824: }
0825: } else if (subName.equals("Red")) {
0826: Integer value = getIntegerValue(subNode);
0827: if (value != null)
0828: red = value.intValue();
0829: } else if (subName.equals("Green")) {
0830: Integer value = getIntegerValue(subNode);
0831: if (value != null)
0832: green = value.intValue();
0833: } else if (subName.equals("Blue")) {
0834: Integer value = getIntegerValue(subNode);
0835: if (value != null)
0836: blue = value.intValue();
0837: }
0838: subNode = subNode.getNextSibling();
0839: }
0840:
0841: if (index == -1) {
0842: index = numPaletteEntries;
0843: }
0844: if (index > maxIndex) {
0845: maxIndex = index;
0846: }
0847:
0848: r[index] = (byte) red;
0849: g[index] = (byte) green;
0850: b[index] = (byte) blue;
0851:
0852: numPaletteEntries++;
0853: paletteEntry = paletteEntry.getNextSibling();
0854: }
0855: } else if (name.equals("CommentExtensions")) {
0856: // CommentExtension
0857: Node commentExtension = node.getFirstChild();
0858: if (commentExtension == null) {
0859: fatal(node, "CommentExtensions has no entries!");
0860: }
0861:
0862: if (comments == null) {
0863: comments = new ArrayList();
0864: }
0865:
0866: while (commentExtension != null) {
0867: if (!commentExtension.getNodeName().equals(
0868: "CommentExtension")) {
0869: fatal(node,
0870: "Only a CommentExtension may be a child of a CommentExtensions!");
0871: }
0872:
0873: comments
0874: .add(getAttribute(commentExtension, "value"));
0875:
0876: commentExtension = commentExtension
0877: .getNextSibling();
0878: }
0879: } else {
0880: fatal(node, "Unknown child of root node!");
0881: }
0882:
0883: node = node.getNextSibling();
0884: }
0885:
0886: if (r != null && g != null && b != null) {
0887: boolean isVersion2 = bmpVersion != null
0888: && bmpVersion.equals(VERSION_2);
0889:
0890: int numEntries = maxIndex + 1;
0891: palette = new byte[(isVersion2 ? 3 : 4) * numEntries];
0892: for (int i = 0, j = 0; i < numEntries; i++) {
0893: palette[j++] = b[i];
0894: palette[j++] = g[i];
0895: palette[j++] = r[i];
0896: if (!isVersion2)
0897: j++; // skip reserved entry
0898: }
0899: }
0900: }
0901:
0902: private void mergeStandardTree(Node root)
0903: throws IIOInvalidTreeException {
0904: Node node = root;
0905: if (!node.getNodeName().equals(
0906: IIOMetadataFormatImpl.standardMetadataFormatName)) {
0907: fatal(node, "Root must be "
0908: + IIOMetadataFormatImpl.standardMetadataFormatName);
0909: }
0910:
0911: String colorSpaceType = null;
0912: int numChannels = 0;
0913: int[] bitsPerSample = null;
0914: boolean hasAlpha = false;
0915:
0916: byte[] r = null, g = null, b = null;
0917: int maxIndex = -1;
0918:
0919: node = node.getFirstChild();
0920: while (node != null) {
0921: String name = node.getNodeName();
0922:
0923: if (name.equals("Chroma")) {
0924: Node child = node.getFirstChild();
0925: while (child != null) {
0926: String childName = child.getNodeName();
0927: if (childName.equals("ColorSpaceType")) {
0928: colorSpaceType = getAttribute(child, "name");
0929: } else if (childName.equals("NumChannels")) {
0930: numChannels = getIntAttribute(child, "value");
0931: } else if (childName.equals("Gamma")) {
0932: gammaRed = gammaGreen = gammaBlue = (int) (getDoubleAttribute(
0933: child, "value") + 0.5);
0934: } else if (childName.equals("Palette")) {
0935: r = new byte[256];
0936: g = new byte[256];
0937: b = new byte[256];
0938: maxIndex = -1;
0939:
0940: Node paletteEntry = child.getFirstChild();
0941: if (paletteEntry == null) {
0942: fatal(node, "Palette has no entries!");
0943: }
0944:
0945: while (paletteEntry != null) {
0946: if (!paletteEntry.getNodeName().equals(
0947: "PaletteEntry")) {
0948: fatal(node,
0949: "Only a PaletteEntry may be a child of a Palette!");
0950: }
0951:
0952: int index = getIntAttribute(paletteEntry,
0953: "index");
0954: if (index < 0 || index > 255) {
0955: fatal(node,
0956: "Bad value for PaletteEntry attribute index!");
0957: }
0958: if (index > maxIndex) {
0959: maxIndex = index;
0960: }
0961: r[index] = (byte) getIntAttribute(
0962: paletteEntry, "red");
0963: g[index] = (byte) getIntAttribute(
0964: paletteEntry, "green");
0965: b[index] = (byte) getIntAttribute(
0966: paletteEntry, "blue");
0967:
0968: paletteEntry = paletteEntry
0969: .getNextSibling();
0970: }
0971: }
0972:
0973: child = child.getNextSibling();
0974: }
0975: } else if (name.equals("Compression")) {
0976: Node child = node.getFirstChild();
0977: while (child != null) {
0978: String childName = child.getNodeName();
0979: if (childName.equals("CompressionTypeName")) {
0980: String compressionName = getAttribute(child,
0981: "value");
0982: compression = BMPImageWriter
0983: .getCompressionType(compressionName);
0984: }
0985: child = child.getNextSibling();
0986: }
0987: } else if (name.equals("Data")) {
0988: Node child = node.getFirstChild();
0989: while (child != null) {
0990: String childName = child.getNodeName();
0991: if (childName.equals("BitsPerSample")) {
0992: List bps = new ArrayList(4);
0993: String s = getAttribute(child, "value");
0994: StringTokenizer t = new StringTokenizer(s);
0995: while (t.hasMoreTokens()) {
0996: bps.add(Integer.valueOf(t.nextToken()));
0997: }
0998: bitsPerSample = new int[bps.size()];
0999: for (int i = 0; i < bitsPerSample.length; i++) {
1000: bitsPerSample[i] = ((Integer) bps.get(i))
1001: .intValue();
1002: }
1003: break;
1004: }
1005: child = child.getNextSibling();
1006: }
1007: } else if (name.equals("Dimension")) {
1008: boolean gotWidth = false;
1009: boolean gotHeight = false;
1010: boolean gotAspectRatio = false;
1011: boolean gotSpaceX = false;
1012: boolean gotSpaceY = false;
1013:
1014: double width = -1.0F;
1015: double height = -1.0F;
1016: double aspectRatio = -1.0F;
1017: double spaceX = -1.0F;
1018: double spaceY = -1.0F;
1019:
1020: Node child = node.getFirstChild();
1021: while (child != null) {
1022: String childName = child.getNodeName();
1023: if (childName.equals("PixelAspectRatio")) {
1024: aspectRatio = getDoubleAttribute(child, "value");
1025: gotAspectRatio = true;
1026: } else if (childName.equals("HorizontalPixelSize")) {
1027: width = getDoubleAttribute(child, "value");
1028: gotWidth = true;
1029: } else if (childName.equals("VerticalPixelSize")) {
1030: height = getDoubleAttribute(child, "value");
1031: gotHeight = true;
1032: } else if (childName
1033: .equals("HorizontalPhysicalPixelSpacing")) {
1034: spaceX = getDoubleAttribute(child, "value");
1035: gotSpaceX = true;
1036: } else if (childName
1037: .equals("VerticalPhysicalPixelSpacing")) {
1038: spaceY = getDoubleAttribute(child, "value");
1039: gotSpaceY = true;
1040: // } else if (childName.equals("ImageOrientation")) {
1041: // } else if (childName.equals("HorizontalPosition")) {
1042: // } else if (childName.equals("VerticalPosition")) {
1043: // } else if (childName.equals("HorizontalPixelOffset")) {
1044: // } else if (childName.equals("VerticalPixelOffset")) {
1045: }
1046: child = child.getNextSibling();
1047: }
1048:
1049: // Use PhysicalPixelSpacing if PixelSize not available.
1050: if (!(gotWidth || gotHeight)
1051: && (gotSpaceX || gotSpaceY)) {
1052: width = spaceX;
1053: gotWidth = gotSpaceX;
1054: height = spaceY;
1055: gotHeight = gotSpaceY;
1056: }
1057:
1058: // Round floating point values to obtain int resolution.
1059: if (gotWidth && gotHeight) {
1060: xPixelsPerMeter = (int) (1000.0 / width + 0.5);
1061: yPixelsPerMeter = (int) (1000.0 / height + 0.5);
1062: } else if (gotAspectRatio && aspectRatio != 0.0) {
1063: if (gotWidth) {
1064: xPixelsPerMeter = (int) (1000.0 / width + 0.5);
1065: yPixelsPerMeter = (int) (aspectRatio
1066: * (1000.0 / width) + 0.5);
1067: } else if (gotHeight) {
1068: xPixelsPerMeter = (int) (1000.0 / height
1069: / aspectRatio + 0.5);
1070: yPixelsPerMeter = (int) (1000.0 / height + 0.5);
1071: }
1072: }
1073: } else if (name.equals("Document")) {
1074: Node child = node.getFirstChild();
1075: while (child != null) {
1076: String childName = child.getNodeName();
1077: if (childName.equals("FormatVersion")) {
1078: bmpVersion = getAttribute(child, "value");
1079: break;
1080: }
1081: child = child.getNextSibling();
1082: }
1083: } else if (name.equals("Text")) {
1084: Node child = node.getFirstChild();
1085: while (child != null) {
1086: String childName = child.getNodeName();
1087: if (childName.equals("TextEntry")) {
1088: if (comments == null) {
1089: comments = new ArrayList();
1090: }
1091: comments.add(getAttribute(child, "value"));
1092: }
1093: child = child.getNextSibling();
1094: }
1095: } else if (name.equals("Transparency")) {
1096: Node child = node.getFirstChild();
1097: while (child != null) {
1098: String childName = child.getNodeName();
1099: if (childName.equals("Alpha")) {
1100: hasAlpha = !getAttribute(child, "value")
1101: .equals("none");
1102: break;
1103: }
1104: child = child.getNextSibling();
1105: }
1106: } else {
1107: // XXX Ignore it.
1108: }
1109:
1110: node = node.getNextSibling();
1111: }
1112:
1113: // Set bitsPerPixel.
1114: if (bitsPerSample != null) {
1115: if (palette != null && paletteSize > 0) {
1116: bitsPerPixel = (short) bitsPerSample[0];
1117: } else {
1118: bitsPerPixel = 0;
1119: for (int i = 0; i < bitsPerSample.length; i++) {
1120: bitsPerPixel += bitsPerSample[i];
1121: }
1122: }
1123: } else if (palette != null) {
1124: bitsPerPixel = 8;
1125: } else if (numChannels == 1) {
1126: bitsPerPixel = 8;
1127: } else if (numChannels == 3) {
1128: bitsPerPixel = 24;
1129: } else if (numChannels == 4) {
1130: bitsPerPixel = 32;
1131: } else if (colorSpaceType.equals("GRAY")) {
1132: bitsPerPixel = 8;
1133: } else if (colorSpaceType.equals("RGB")) {
1134: bitsPerPixel = (short) (hasAlpha ? 32 : 24);
1135: }
1136:
1137: // Set RGB masks.
1138: if ((bitsPerSample != null && bitsPerSample.length == 4)
1139: || bitsPerPixel >= 24) {
1140: redMask = 0x00ff0000;
1141: greenMask = 0x0000ff00;
1142: blueMask = 0x000000ff;
1143: }
1144:
1145: // Set alpha mask.
1146: if ((bitsPerSample != null && bitsPerSample.length == 4)
1147: || bitsPerPixel > 24) {
1148: alphaMask = 0xff000000;
1149: }
1150:
1151: // Set palette
1152: if (r != null && g != null && b != null) {
1153: boolean isVersion2 = bmpVersion != null
1154: && bmpVersion.equals(VERSION_2);
1155:
1156: paletteSize = maxIndex + 1;
1157: palette = new byte[(isVersion2 ? 3 : 4) * paletteSize];
1158: for (int i = 0, j = 0; i < paletteSize; i++) {
1159: palette[j++] = b[i];
1160: palette[j++] = g[i];
1161: palette[j++] = r[i];
1162: if (!isVersion2)
1163: j++; // skip reserved entry
1164: }
1165: }
1166: }
1167:
1168: public void reset() {
1169: // Fields for Image Descriptor
1170: bmpVersion = null;
1171: width = 0;
1172: height = 0;
1173: bitsPerPixel = 0;
1174: compression = 0;
1175: imageSize = 0;
1176:
1177: // Fields for PixelsPerMeter
1178: xPixelsPerMeter = 0;
1179: yPixelsPerMeter = 0;
1180:
1181: colorsUsed = 0;
1182: colorsImportant = 0;
1183:
1184: // Fields for BI_BITFIELDS compression(Mask)
1185: redMask = 0;
1186: greenMask = 0;
1187: blueMask = 0;
1188: alphaMask = 0;
1189:
1190: colorSpace = 0;
1191:
1192: // Fields for CIE XYZ for the LCS_CALIBRATED_RGB color space
1193: redX = 0;
1194: redY = 0;
1195: redZ = 0;
1196: greenX = 0;
1197: greenY = 0;
1198: greenZ = 0;
1199: blueX = 0;
1200: blueY = 0;
1201: blueZ = 0;
1202:
1203: // Fields for Gamma values for the LCS_CALIBRATED_RGB color space
1204: gammaRed = 0;
1205: gammaGreen = 0;
1206: gammaBlue = 0;
1207:
1208: intent = 0;
1209:
1210: // Fields for the Palette and Entries
1211: palette = null;
1212: paletteSize = 0;
1213: red = 0;
1214: green = 0;
1215: blue = 0;
1216:
1217: // Fields from CommentExtension
1218: comments = null;
1219: }
1220:
1221: private String countBits(int num) {
1222: int count = 0;
1223: while (num != 0) {
1224: if ((num & 1) == 1)
1225: count++;
1226: num >>>= 1;
1227: }
1228:
1229: return count == 0 ? "0" : "" + count;
1230: }
1231:
1232: private void addXYZPoints(IIOMetadataNode root, String name,
1233: double x, double y, double z) {
1234: IIOMetadataNode node = addChildNode(root, name, null);
1235: addChildNode(node, "X", new Double(x));
1236: addChildNode(node, "Y", new Double(y));
1237: addChildNode(node, "Z", new Double(z));
1238: }
1239:
1240: private IIOMetadataNode addChildNode(IIOMetadataNode root,
1241: String name, Object object) {
1242: IIOMetadataNode child = new IIOMetadataNode(name);
1243: if (object != null) {
1244: child.setUserObject(object);
1245: child.setNodeValue(ImageUtil.convertObjectToString(object));
1246: }
1247: root.appendChild(child);
1248: return child;
1249: }
1250:
1251: private Object getObjectValue(Node node) {
1252: Object tmp = node.getNodeValue();
1253:
1254: if (tmp == null && node instanceof IIOMetadataNode) {
1255: tmp = ((IIOMetadataNode) node).getUserObject();
1256: }
1257:
1258: return tmp;
1259: }
1260:
1261: private String getStringValue(Node node) {
1262: Object tmp = getObjectValue(node);
1263: return tmp instanceof String ? (String) tmp : null;
1264: }
1265:
1266: private Byte getByteValue(Node node) {
1267: Object tmp = getObjectValue(node);
1268: Byte value = null;
1269: if (tmp instanceof String) {
1270: value = Byte.valueOf((String) tmp);
1271: } else if (tmp instanceof Byte) {
1272: value = (Byte) tmp;
1273: }
1274: return value;
1275: }
1276:
1277: private Short getShortValue(Node node) {
1278: Object tmp = getObjectValue(node);
1279: Short value = null;
1280: if (tmp instanceof String) {
1281: value = Short.valueOf((String) tmp);
1282: } else if (tmp instanceof Short) {
1283: value = (Short) tmp;
1284: }
1285: return value;
1286: }
1287:
1288: private Integer getIntegerValue(Node node) {
1289: Object tmp = getObjectValue(node);
1290: Integer value = null;
1291: if (tmp instanceof String) {
1292: value = Integer.valueOf((String) tmp);
1293: } else if (tmp instanceof Integer) {
1294: value = (Integer) tmp;
1295: } else if (tmp instanceof Byte) {
1296: value = new Integer(((Byte) tmp).byteValue() & 0xff);
1297: }
1298: return value;
1299: }
1300:
1301: private Double getDoubleValue(Node node) {
1302: Object tmp = getObjectValue(node);
1303: Double value = null;
1304: if (tmp instanceof String) {
1305: value = Double.valueOf((String) tmp);
1306: } else if (tmp instanceof Double) {
1307: value = (Double) tmp;
1308: }
1309: return value;
1310: }
1311: }
|