0001: /*
0002: * $RCSfile: J2KMetadata.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.4 $
0042: * $Date: 2006/09/22 23:07:25 $
0043: * $State: Exp $
0044: */
0045: package com.sun.media.imageioimpl.plugins.jpeg2000;
0046:
0047: import java.io.InputStream;
0048:
0049: import javax.imageio.ImageTypeSpecifier;
0050: import javax.imageio.ImageWriteParam;
0051: import javax.imageio.ImageWriter;
0052: import javax.imageio.IIOException;
0053: import javax.imageio.stream.ImageInputStream;
0054: import javax.imageio.stream.ImageOutputStream;
0055: import javax.imageio.metadata.IIOMetadata;
0056: import javax.imageio.metadata.IIOMetadataNode;
0057: import javax.imageio.metadata.IIOMetadataFormat;
0058: import javax.imageio.metadata.IIOMetadataFormatImpl;
0059: import javax.imageio.metadata.IIOInvalidTreeException;
0060:
0061: import org.w3c.dom.Node;
0062: import org.w3c.dom.NodeList;
0063: import org.w3c.dom.NamedNodeMap;
0064:
0065: import java.util.List;
0066: import java.util.ArrayList;
0067: import java.util.Arrays;
0068: import java.util.Hashtable;
0069: import java.util.Iterator;
0070: import java.util.ListIterator;
0071: import java.io.IOException;
0072: import java.awt.color.ICC_Profile;
0073: import java.awt.color.ICC_ColorSpace;
0074: import java.awt.color.ColorSpace;
0075: import java.awt.image.ColorModel;
0076: import java.awt.image.DataBuffer;
0077: import java.awt.image.IndexColorModel;
0078: import java.awt.image.SampleModel;
0079: import java.awt.Point;
0080:
0081: import com.sun.media.imageio.plugins.jpeg2000.J2KImageReadParam;
0082: import com.sun.media.imageio.plugins.jpeg2000.J2KImageWriteParam;
0083:
0084: import jj2000.j2k.fileformat.FileFormatBoxes;
0085: import jj2000.j2k.fileformat.reader.FileFormatReader;
0086: import jj2000.j2k.io.RandomAccessIO;
0087:
0088: /**
0089: * Metadata for the J2K plug-in.
0090: */
0091: public class J2KMetadata extends IIOMetadata implements Cloneable {
0092: static final String nativeMetadataFormatName = "com_sun_media_imageio_plugins_jpeg2000_image_1.0";
0093:
0094: /** cache the metadata format */
0095: private J2KMetadataFormat format;
0096:
0097: /** The boxes of JP2 file used as meta data, i. e., all the boxes
0098: * except the data stream box
0099: */
0100: private ArrayList boxes = new ArrayList();
0101:
0102: /**
0103: * Constructor containing code shared by other constructors.
0104: */
0105: public J2KMetadata() {
0106: super (
0107: true, // Supports standard format
0108: nativeMetadataFormatName, // and a native format
0109: "com.sun.media.imageioimpl.plugins.jpeg2000.J2KMetadataFormat",
0110: null, null); // No other formats
0111:
0112: format = (J2KMetadataFormat) getMetadataFormat(nativeMetadataFormatName);
0113: }
0114:
0115: /*
0116: * Constructs a <code>J2KMetadata</code> object by reading the
0117: * contents of an <code>ImageInputStream</code>. Has package-only
0118: * access.
0119: *
0120: * @param iis An <code>ImageInputStream</code> from which to read
0121: * the metadata.
0122: * @param reader The <code>J2KImageReader</code> calling this
0123: * constructor, to which warnings should be sent.
0124: */
0125: public J2KMetadata(ImageInputStream iis, J2KImageReader reader)
0126: throws IOException {
0127: this ();
0128: RandomAccessIO in = new IISRandomAccessIO(iis);
0129:
0130: iis.mark();
0131: // **** File Format ****
0132: // If the codestream is wrapped in the jp2 fileformat, Read the
0133: // file format wrapper
0134: FileFormatReader ff = new FileFormatReader(in, this );
0135: ff.readFileFormat();
0136: iis.reset();
0137: }
0138:
0139: /**
0140: * Constructs a default stream <code>J2KMetadata</code> object appropriate
0141: * for the given write parameters.
0142: */
0143: public J2KMetadata(ImageWriteParam param, ImageWriter writer) {
0144: this (null, param, writer);
0145: }
0146:
0147: /**
0148: * Constructs a default image <code>J2KMetadata</code> object appropriate
0149: * for the given image type and write parameters.
0150: */
0151: public J2KMetadata(ImageTypeSpecifier imageType,
0152: ImageWriteParam param, ImageWriter writer) {
0153: this (imageType != null ? imageType.getColorModel() : null,
0154: imageType != null ? imageType.getSampleModel() : null,
0155: 0, 0, param, writer);
0156: }
0157:
0158: /**
0159: * Constructs a default image <code>J2KMetadata</code> object appropriate
0160: * for the given image type and write parameters.
0161: */
0162: public J2KMetadata(ColorModel colorModel, SampleModel sampleModel,
0163: int width, int height, ImageWriteParam param,
0164: ImageWriter writer) {
0165: this ();
0166: addNode(new SignatureBox());
0167: addNode(new FileTypeBox(0x6A703220, 0, new int[] { 0x6A703220 }));
0168:
0169: ImageTypeSpecifier destType = null;
0170:
0171: if (param != null) {
0172: destType = param.getDestinationType();
0173: if (colorModel == null && sampleModel == null) {
0174: colorModel = destType == null ? null : destType
0175: .getColorModel();
0176: sampleModel = destType == null ? null : destType
0177: .getSampleModel();
0178: }
0179: }
0180:
0181: int[] bitDepths = null;
0182: if (colorModel != null) {
0183: bitDepths = colorModel.getComponentSize();
0184: } else if (sampleModel != null) {
0185: bitDepths = sampleModel.getSampleSize();
0186: }
0187:
0188: int bitsPerComponent = 0xff;
0189: if (bitDepths != null) {
0190: bitsPerComponent = bitDepths[0];
0191: int numComponents = bitDepths.length;
0192: for (int i = 1; i < numComponents; i++) {
0193: /* XXX: This statement should be removed when BPC behavior
0194: is corrected as derscribed below. */
0195: if (bitDepths[i] > bitsPerComponent) {
0196: bitsPerComponent = bitDepths[i];
0197: }
0198: /* XXX: When the number of bits per component is not the
0199: same for all components the BPC parameter of the Image
0200: Header box should be set to 0xff and the actual number of
0201: bits per component written in the Bits Per Component box.
0202: if(bitDepths[i] != bitsPerComponent) {
0203: bitsPerComponent = 0xff;
0204: break;
0205: }
0206: */
0207: }
0208: }
0209:
0210: if (colorModel != null) {
0211: ColorSpace cs = colorModel.getColorSpace();
0212: boolean iccColor = (cs instanceof ICC_ColorSpace);
0213: int type = cs.getType();
0214:
0215: if (type == ColorSpace.TYPE_RGB) {
0216: addNode(new ColorSpecificationBox((byte) 1, (byte) 0,
0217: (byte) 0, ColorSpecificationBox.ECS_sRGB, null));
0218: } else if (type == ColorSpace.TYPE_GRAY)
0219: addNode(new ColorSpecificationBox((byte) 1, (byte) 0,
0220: (byte) 0, ColorSpecificationBox.ECS_GRAY, null));
0221: else if (cs instanceof ICC_ColorSpace)
0222: addNode(new ColorSpecificationBox((byte) 2, (byte) 0,
0223: (byte) 0, 0, ((ICC_ColorSpace) cs).getProfile()));
0224:
0225: if (colorModel.hasAlpha()) {
0226: addNode(new ChannelDefinitionBox(colorModel));
0227: }
0228:
0229: if (colorModel instanceof IndexColorModel) {
0230: addNode(new PaletteBox((IndexColorModel) colorModel));
0231: int numComp = colorModel.getComponentSize().length;
0232: short[] channels = new short[numComp];
0233: byte[] types = new byte[numComp];
0234: byte[] maps = new byte[numComp];
0235: for (int i = 0; i < numComp; i++) {
0236: channels[i] = 0;
0237: types[i] = 1;
0238: maps[i] = (byte) i;
0239: }
0240: addNode(new ComponentMappingBox(channels, types, maps));
0241: }
0242: }
0243:
0244: if (sampleModel != null) {
0245: if (width <= 0)
0246: width = sampleModel.getWidth();
0247: if (height <= 0)
0248: height = sampleModel.getHeight();
0249: int bpc = bitsPerComponent == 0xff ? 0xff
0250: : ((bitsPerComponent - 1) | (isOriginalSigned(sampleModel) ? 0x80
0251: : 0));
0252: addNode(new HeaderBox(
0253: height,
0254: width,
0255: sampleModel.getNumBands(),
0256: bpc,
0257: 7,
0258: colorModel == null ? 1 : 0,
0259: getElement("JPEG2000IntellectualPropertyRightsBox") == null ? 0
0260: : 1));
0261: }
0262: }
0263:
0264: public Object clone() {
0265: J2KMetadata theClone = null;
0266:
0267: try {
0268: theClone = (J2KMetadata) super .clone();
0269: } catch (CloneNotSupportedException e) {
0270: } // won't happen
0271:
0272: if (boxes != null) {
0273: int numBoxes = boxes.size();
0274: for (int i = 0; i < numBoxes; i++) {
0275: theClone.addNode((Box) boxes.get(i));
0276: }
0277: }
0278: return theClone;
0279: }
0280:
0281: public Node getAsTree(String formatName) {
0282: if (formatName == null) {
0283: throw new IllegalArgumentException(I18N
0284: .getString("J2KMetadata0"));
0285: }
0286:
0287: if (formatName.equals(nativeMetadataFormatName)) {
0288: return getNativeTree();
0289: }
0290:
0291: if (formatName
0292: .equals(IIOMetadataFormatImpl.standardMetadataFormatName)) {
0293: return getStandardTree();
0294: }
0295:
0296: throw new IllegalArgumentException(I18N
0297: .getString("J2KMetadata1")
0298: + " " + formatName);
0299: }
0300:
0301: IIOMetadataNode getNativeTree() {
0302: IIOMetadataNode root = new IIOMetadataNode(
0303: nativeMetadataFormatName);
0304:
0305: Box signatureBox = null, fileTypeBox = null, headerBox = null;
0306: int signatureIndex = -1, fileTypeIndex = -1, headerIndex = -1;
0307:
0308: int numBoxes = boxes.size();
0309:
0310: int found = 0;
0311: for (int i = 0; i < numBoxes && found < 3; i++) {
0312: Box box = (Box) boxes.get(i);
0313: if (Box.getName(box.getType()).equals(
0314: "JPEG2000SignatureBox")) {
0315: signatureBox = box;
0316: signatureIndex = i;
0317: found++;
0318: } else if (Box.getName(box.getType()).equals(
0319: "JPEG2000FileTypeBox")) {
0320: fileTypeBox = box;
0321: fileTypeIndex = i;
0322: found++;
0323: } else if (Box.getName(box.getType()).equals(
0324: "JPEG2000HeaderBox")) {
0325: headerBox = box;
0326: headerIndex = i;
0327: found++;
0328: }
0329: }
0330:
0331: if (signatureBox != null) {
0332: insertNodeIntoTree(root, signatureBox.getNativeNode());
0333: }
0334:
0335: if (fileTypeBox != null) {
0336: insertNodeIntoTree(root, fileTypeBox.getNativeNode());
0337: }
0338:
0339: if (headerBox != null) {
0340: insertNodeIntoTree(root, headerBox.getNativeNode());
0341: }
0342:
0343: for (int i = 0; i < numBoxes; i++) {
0344: if (i == signatureIndex || i == fileTypeIndex
0345: || i == headerIndex)
0346: continue;
0347: Box box = (Box) boxes.get(i);
0348: IIOMetadataNode node = box.getNativeNode();
0349: insertNodeIntoTree(root, node);
0350: }
0351: return root;
0352: }
0353:
0354: // Standard tree node methods
0355: protected IIOMetadataNode getStandardChromaNode() {
0356: HeaderBox header = (HeaderBox) getElement("JPEG2000HeaderBox");
0357: PaletteBox palette = (PaletteBox) getElement("JPEG2000PaletteBox");
0358: ColorSpecificationBox color = (ColorSpecificationBox) getElement("JPEG2000ColorSpecificationBox");
0359:
0360: IIOMetadataNode node = new IIOMetadataNode("Chroma");
0361: IIOMetadataNode subNode = null;
0362: if (header != null) {
0363: if (header.getUnknownColorspace() == 0) {
0364: if (color != null && color.getMethod() == 1) {
0365: subNode = new IIOMetadataNode("ColorSpaceType");
0366: int ecs = color.getEnumeratedColorSpace();
0367: if (ecs == FileFormatBoxes.CSB_ENUM_SRGB)
0368: subNode.setAttribute("name", "RGB");
0369: if (ecs == FileFormatBoxes.CSB_ENUM_GREY)
0370: subNode.setAttribute("name", "GRAY");
0371: node.appendChild(subNode);
0372: }
0373: }
0374:
0375: subNode = new IIOMetadataNode("NumChannels");
0376: subNode.setAttribute("value", ""
0377: + header.getNumComponents());
0378: node.appendChild(subNode);
0379:
0380: if (palette != null) {
0381: subNode
0382: .setAttribute("value", ""
0383: + palette.getNumComp());
0384: subNode = new IIOMetadataNode("Palette");
0385: byte[][] lut = palette.getLUT();
0386:
0387: int size = lut[0].length;
0388: int numComp = lut.length;
0389:
0390: for (int i = 0; i < size; i++) {
0391: IIOMetadataNode subNode1 = new IIOMetadataNode(
0392: "PaletteEntry");
0393: subNode1.setAttribute("index", "" + i);
0394: subNode1.setAttribute("red", ""
0395: + (lut[0][i] & 0xff));
0396: subNode1.setAttribute("green", ""
0397: + (lut[1][i] & 0xff));
0398: subNode1.setAttribute("blue", ""
0399: + (lut[2][i] & 0xff));
0400: if (numComp == 4)
0401: subNode1.setAttribute("alpha", ""
0402: + (lut[3][i] & 0xff));
0403: subNode.appendChild(subNode1);
0404: }
0405: node.appendChild(subNode);
0406: }
0407: }
0408: return node;
0409: }
0410:
0411: protected IIOMetadataNode getStandardCompressionNode() {
0412: IIOMetadataNode node = new IIOMetadataNode("Compression");
0413:
0414: // CompressionTypeName
0415: IIOMetadataNode subNode = new IIOMetadataNode(
0416: "CompressionTypeName");
0417: subNode.setAttribute("value", "JPEG2000");
0418: node.appendChild(subNode);
0419: return node;
0420: }
0421:
0422: protected IIOMetadataNode getStandardDataNode() {
0423: IIOMetadataNode node = new IIOMetadataNode("Data");
0424: PaletteBox palette = (PaletteBox) getElement("JPEG2000PaletteBox");
0425: boolean sampleFormat = false;
0426:
0427: if (palette != null) {
0428: IIOMetadataNode subNode = new IIOMetadataNode(
0429: "SampleFormat");
0430: subNode.setAttribute("value", "Index");
0431: node.appendChild(subNode);
0432: sampleFormat = true;
0433: }
0434:
0435: BitsPerComponentBox bitDepth = (BitsPerComponentBox) getElement("JPEG2000BitsPerComponentBox");
0436: String value = "";
0437: boolean signed = false;
0438: boolean gotSampleInfo = false;
0439:
0440: // JPEG 2000 "B" parameter represents "bitDepth - 1" in the
0441: // right 7 least significant bits with the most significant
0442: // bit indicating signed if set and unsigned if not.
0443: if (bitDepth != null) {
0444: byte[] bits = bitDepth.getBitDepth();
0445: if ((bits[0] & 0x80) == 0x80)
0446: signed = true;
0447:
0448: int numComp = bits.length;
0449: for (int i = 0; i < numComp; i++) {
0450: value += (bits[i] & 0x7f) + 1;
0451: if (i != numComp - 1)
0452: value += " ";
0453: }
0454:
0455: gotSampleInfo = true;
0456: } else {
0457: HeaderBox header = (HeaderBox) getElement("JPEG2000HeaderBox");
0458: if (header != null) {
0459: int bits = header.getBitDepth();
0460: if ((bits & 0x80) == 0x80)
0461: signed = true;
0462: bits = (bits & 0x7f) + 1;
0463: int numComp = header.getNumComponents();
0464: for (int i = 0; i < numComp; i++) {
0465: value += bits;
0466: if (i != numComp - 1)
0467: value += " ";
0468: }
0469:
0470: gotSampleInfo = true;
0471: }
0472: }
0473:
0474: IIOMetadataNode subNode = null;
0475:
0476: if (gotSampleInfo) {
0477: subNode = new IIOMetadataNode("BitsPerSample");
0478: subNode.setAttribute("value", value);
0479: node.appendChild(subNode);
0480: }
0481:
0482: subNode = new IIOMetadataNode("PlanarConfiguration");
0483: subNode.setAttribute("value", "TileInterleaved");
0484: node.appendChild(subNode);
0485:
0486: if (!sampleFormat && gotSampleInfo) {
0487: subNode = new IIOMetadataNode("SampleFormat");
0488: subNode.setAttribute("value", signed ? "SignedIntegral"
0489: : "UnsignedIntegral");
0490: node.appendChild(subNode);
0491: }
0492:
0493: return node;
0494: }
0495:
0496: protected IIOMetadataNode getStandardDimensionNode() {
0497: ResolutionBox box = (ResolutionBox) getElement("JPEG2000CaptureResolutionBox");
0498: if (box != null) {
0499: IIOMetadataNode node = new IIOMetadataNode("Dimension");
0500: float hRes = box.getHorizontalResolution();
0501: float vRes = box.getVerticalResolution();
0502: float ratio = vRes / hRes;
0503: IIOMetadataNode subNode = new IIOMetadataNode(
0504: "PixelAspectRatio");
0505: subNode.setAttribute("value", "" + ratio);
0506: node.appendChild(subNode);
0507:
0508: subNode = new IIOMetadataNode("HorizontalPixelSize");
0509: subNode.setAttribute("value", "" + (1000 / hRes));
0510: node.appendChild(subNode);
0511:
0512: subNode = new IIOMetadataNode("VerticalPixelSize");
0513: subNode.setAttribute("value", "" + (1000 / vRes));
0514: node.appendChild(subNode);
0515:
0516: return node;
0517: }
0518:
0519: return null;
0520: }
0521:
0522: protected IIOMetadataNode getStandardTransparencyNode() {
0523: ChannelDefinitionBox channel = (ChannelDefinitionBox) getElement("JPEG2000ChannelDefinitionBox");
0524: if (channel != null) {
0525: IIOMetadataNode node = new IIOMetadataNode("Transparency");
0526:
0527: boolean hasAlpha = false;
0528: boolean isPremultiplied = false;
0529: short[] type = channel.getTypes();
0530:
0531: for (int i = 0; i < type.length; i++) {
0532: if (type[i] == 1)
0533: hasAlpha = true;
0534: if (type[i] == 2)
0535: isPremultiplied = true;
0536: }
0537:
0538: String value = "none";
0539: if (isPremultiplied)
0540: value = "premultiplied";
0541: else if (hasAlpha)
0542: value = "nonpremultiplied";
0543:
0544: IIOMetadataNode subNode = new IIOMetadataNode("Alpha");
0545: subNode.setAttribute("value", value);
0546: node.appendChild(subNode);
0547:
0548: return node;
0549: }
0550:
0551: IIOMetadataNode node = new IIOMetadataNode("Transparency");
0552: IIOMetadataNode subNode = new IIOMetadataNode("Alpha");
0553: subNode.setAttribute("value", "none");
0554: node.appendChild(subNode);
0555:
0556: return null;
0557: }
0558:
0559: protected IIOMetadataNode getStandardTextNode() {
0560: if (boxes == null)
0561: return null;
0562: IIOMetadataNode text = null;
0563:
0564: Iterator iterator = boxes.iterator();
0565:
0566: while (iterator.hasNext()) {
0567: Box box = (Box) iterator.next();
0568: if (box instanceof XMLBox) {
0569: if (text == null)
0570: text = new IIOMetadataNode("Text");
0571: IIOMetadataNode subNode = new IIOMetadataNode(
0572: "TextEntry");
0573: String content = new String(box.getContent());
0574: subNode.setAttribute("value", content);
0575: text.appendChild(subNode);
0576: }
0577: }
0578: return text;
0579: }
0580:
0581: public boolean isReadOnly() {
0582: return false;
0583: }
0584:
0585: public void mergeTree(String formatName, Node root)
0586: throws IIOInvalidTreeException {
0587: if (formatName == null) {
0588: throw new IllegalArgumentException(I18N
0589: .getString("J2KMetadata0"));
0590: }
0591:
0592: if (root == null) {
0593: throw new IllegalArgumentException(I18N
0594: .getString("J2KMetadata2"));
0595: }
0596:
0597: if (formatName.equals(nativeMetadataFormatName)
0598: && root.getNodeName().equals(nativeMetadataFormatName)) {
0599: mergeNativeTree(root);
0600: } else if (formatName
0601: .equals(IIOMetadataFormatImpl.standardMetadataFormatName)) {
0602: mergeStandardTree(root);
0603: } else {
0604: throw new IllegalArgumentException(I18N
0605: .getString("J2KMetadata1")
0606: + " " + formatName);
0607: }
0608: }
0609:
0610: public void setFromTree(String formatName, Node root)
0611: throws IIOInvalidTreeException {
0612: if (formatName == null) {
0613: throw new IllegalArgumentException(I18N
0614: .getString("J2KMetadata0"));
0615: }
0616:
0617: if (root == null) {
0618: throw new IllegalArgumentException(I18N
0619: .getString("J2KMetadata2"));
0620: }
0621:
0622: if (formatName.equals(nativeMetadataFormatName)
0623: && root.getNodeName().equals(nativeMetadataFormatName)) {
0624: boxes = new ArrayList();
0625: mergeNativeTree(root);
0626: } else if (formatName
0627: .equals(IIOMetadataFormatImpl.standardMetadataFormatName)) {
0628: boxes = new ArrayList();
0629: mergeStandardTree(root);
0630: } else {
0631: throw new IllegalArgumentException(I18N
0632: .getString("J2KMetadata1")
0633: + " " + formatName);
0634: }
0635: }
0636:
0637: public void reset() {
0638: boxes.clear();
0639: }
0640:
0641: public void addNode(Box node) {
0642: if (boxes == null)
0643: boxes = new ArrayList();
0644: replace(Box.getName(node.getType()), node);
0645: }
0646:
0647: public Box getElement(String name) {
0648: for (int i = boxes.size() - 1; i >= 0; i--) {
0649: Box box = (Box) boxes.get(i);
0650: if (name.equals(Box.getName(box.getType())))
0651: return box;
0652: }
0653: return null;
0654: }
0655:
0656: private void mergeNativeTree(Node root)
0657: throws IIOInvalidTreeException {
0658: NodeList list = root.getChildNodes();
0659: for (int i = list.getLength() - 1; i >= 0; i--) {
0660: Node node = list.item(i);
0661: String name = node.getNodeName();
0662: if (format.getParent(name) != null) {
0663: if (format.isLeaf(name)) {
0664: String s = (String) Box.getAttribute(node, "Type");
0665: Box box = Box.createBox(Box.getTypeInt(s), node);
0666: if (format.singleInstance(name)
0667: && getElement(name) != null) {
0668: replace(name, box);
0669: } else
0670: boxes.add(box);
0671: } else {
0672: mergeNativeTree(node);
0673: }
0674: }
0675: }
0676: }
0677:
0678: private void mergeStandardTree(Node root)
0679: throws IIOInvalidTreeException {
0680: NodeList children = root.getChildNodes();
0681: int numComps = 0;
0682:
0683: for (int i = 0; i < children.getLength(); i++) {
0684: Node node = children.item(i);
0685: String name = node.getNodeName();
0686: if (name.equals("Chroma")) {
0687: NodeList children1 = node.getChildNodes();
0688: for (int j = 0; j < children1.getLength(); j++) {
0689: Node child = children1.item(j);
0690: String name1 = child.getNodeName();
0691:
0692: if (name1.equals("NumChannels")) {
0693: String s = (String) Box.getAttribute(child,
0694: "value");
0695: numComps = new Integer(s).intValue();
0696: }
0697:
0698: if (name1.equals("ColorSpaceType"))
0699: createColorSpecificationBoxFromStandardNode(child);
0700:
0701: if (name1.equals("Palette")) {
0702: createPaletteBoxFromStandardNode(child);
0703: }
0704: }
0705: } else if (name.equals("Compression")) {
0706: // Intentionally do nothing: just prevent entry into
0707: // the default "else" block and an ensuing
0708: // IIOInvalidTreeException; fixes 5110389.
0709: } else if (name.equals("Data")) {
0710: createBitsPerComponentBoxFromStandardNode(node);
0711: createHeaderBoxFromStandardNode(node, numComps);
0712: } else if (name.equals("Dimension")) {
0713: createResolutionBoxFromStandardNode(node);
0714: } else if (name.equals("Document")) {
0715: createXMLBoxFromStandardNode(node);
0716: } else if (name.equals("Text")) {
0717: createXMLBoxFromStandardNode(node);
0718: } else if (name.equals("Transparency")) {
0719: createChannelDefinitionFromStandardNode(node);
0720: } else {
0721: throw new IIOInvalidTreeException(I18N
0722: .getString("J2KMetadata3")
0723: + " " + name, node);
0724: }
0725: }
0726: }
0727:
0728: private void createColorSpecificationBoxFromStandardNode(Node node) {
0729: if (node.getNodeName() != "ColorSpaceType")
0730: throw new IllegalArgumentException(I18N
0731: .getString("J2KMetadata4"));
0732: String name = (String) Box.getAttribute(node, "name");
0733: int ecs = name.equals("RGB") ? ColorSpecificationBox.ECS_sRGB
0734: : (name.equals("Gray") ? ColorSpecificationBox.ECS_GRAY
0735: : 0);
0736:
0737: if (ecs == ColorSpecificationBox.ECS_sRGB
0738: || ecs == ColorSpecificationBox.ECS_GRAY) {
0739: replace("JPEG2000ColorSpecificationBox",
0740: new ColorSpecificationBox((byte) 1, (byte) 0,
0741: (byte) 0, ecs, null));
0742: }
0743: }
0744:
0745: private void createPaletteBoxFromStandardNode(Node node) {
0746: if (node.getNodeName() != "Palette")
0747: throw new IllegalArgumentException(I18N
0748: .getString("J2KMetadata5"));
0749: NodeList children = node.getChildNodes();
0750: int maxIndex = -1;
0751: boolean hasAlpha = false;
0752: for (int i = 0; i < children.getLength(); i++) {
0753: Node child = children.item(i);
0754: String name = child.getNodeName();
0755:
0756: if (name.equals("PaletteEntry")) {
0757: String s = (String) Box.getAttribute(child, "index");
0758: int index = new Integer(s).intValue();
0759: if (index > maxIndex) {
0760: maxIndex = index;
0761: }
0762: if (Box.getAttribute(child, "alpha") != null) {
0763: hasAlpha = true;
0764: }
0765: }
0766: }
0767:
0768: // Determine palette size.
0769: int numBits = 32;
0770: int mask = 0x80000000;
0771: while (mask != 0 && (maxIndex & mask) == 0) {
0772: numBits--;
0773: mask >>>= 1;
0774: }
0775: int size = 1 << numBits;
0776:
0777: byte[] red = new byte[size];
0778: byte[] green = new byte[size];
0779: byte[] blue = new byte[size];
0780: byte[] alpha = hasAlpha ? new byte[size] : null;
0781:
0782: for (int i = 0; i < children.getLength(); i++) {
0783: Node child = children.item(i);
0784: String name = child.getNodeName();
0785:
0786: if (name.equals("PaletteEntry")) {
0787: String s = (String) Box.getAttribute(child, "index");
0788: int index = new Integer(s).intValue();
0789: s = (String) Box.getAttribute(child, "red");
0790: red[index] = (byte) (new Integer(s).intValue());
0791: s = (String) Box.getAttribute(child, "green");
0792: green[index] = (byte) (new Integer(s).intValue());
0793: s = (String) Box.getAttribute(child, "blue");
0794: blue[index] = (byte) (new Integer(s).intValue());
0795:
0796: byte t = (byte) 255;
0797: s = (String) Box.getAttribute(child, "alpha");
0798: if (s != null) {
0799: t = (byte) (new Integer(s).intValue());
0800: }
0801:
0802: if (alpha != null) {
0803: alpha[index] = t;
0804: }
0805: }
0806: }
0807:
0808: IndexColorModel icm;
0809: if (alpha == null)
0810: icm = new IndexColorModel(numBits, size, red, green, blue);
0811: else
0812: icm = new IndexColorModel(numBits, size, red, green, blue,
0813: alpha);
0814:
0815: replace("JPEG2000PaletteBox", new PaletteBox(icm));
0816: }
0817:
0818: private void createBitsPerComponentBoxFromStandardNode(Node node) {
0819: if (node.getNodeName() != "Data")
0820: throw new IllegalArgumentException(I18N
0821: .getString("J2KMetadata6"));
0822:
0823: NodeList children = node.getChildNodes();
0824:
0825: byte[] bits = null;
0826: boolean isSigned = false;
0827: for (int i = 0; i < children.getLength(); i++) {
0828: Node child = children.item(i);
0829: String name = child.getNodeName();
0830:
0831: if (name.equals("BitsPerSample")) {
0832: String s = (String) Box.getAttribute(child, "value");
0833: bits = (byte[]) Box.parseByteArray(s).clone();
0834: } else if (name.equals("SampleFormat")) {
0835: String s = (String) Box.getAttribute(child, "value");
0836: isSigned = s.equals("SignedIntegral");
0837: }
0838: }
0839:
0840: if (bits != null) {
0841: // JPEG 2000 "B" parameter represents "bitDepth - 1" in the
0842: // right 7 least significant bits with the most significant
0843: // bit indicating signed if set and unsigned if not.
0844: for (int i = 0; i < bits.length; i++) {
0845: bits[i] = (byte) ((bits[i] & 0xff) - 1);
0846: if (isSigned) {
0847: bits[i] |= 0x80;
0848: }
0849: }
0850:
0851: replace("JPEG2000BitsPerComponent",
0852: new BitsPerComponentBox(bits));
0853: }
0854: }
0855:
0856: private void createResolutionBoxFromStandardNode(Node node) {
0857: if (node.getNodeName() != "Dimension")
0858: throw new IllegalArgumentException(I18N
0859: .getString("J2KMetadata7"));
0860: NodeList children = node.getChildNodes();
0861: float hRes = 0.0f;
0862: float vRes = 0.0f;
0863:
0864: boolean gotH = false, gotV = false;
0865:
0866: for (int i = 0; i < children.getLength(); i++) {
0867: Node child = children.item(i);
0868: String name = child.getNodeName();
0869:
0870: if (name.equals("HorizontalPixelSize")) {
0871: String s = (String) Box.getAttribute(child, "value");
0872: hRes = new Float(s).floatValue();
0873: hRes = 1000 / hRes;
0874: gotH = true;
0875: }
0876:
0877: if (name.equals("VerticalPixelSize")) {
0878: String s = (String) Box.getAttribute(child, "value");
0879: vRes = new Float(s).floatValue();
0880: vRes = 1000 / vRes;
0881: gotV = true;
0882: }
0883: }
0884:
0885: if (gotH && !gotV) {
0886: vRes = hRes;
0887: } else if (gotV && !gotH) {
0888: hRes = vRes;
0889: }
0890:
0891: if (gotH || gotV) {
0892: replace("JPEG2000CaptureResolutionBox", new ResolutionBox(
0893: 0x72657363, hRes, vRes));
0894: }
0895: }
0896:
0897: private void createXMLBoxFromStandardNode(Node node) {
0898: NodeList children = node.getChildNodes();
0899: String value = "<" + node.getNodeName() + ">";
0900:
0901: for (int i = 0; i < children.getLength(); i++) {
0902: Node child = children.item(i);
0903: String name = child.getNodeName();
0904: value += "<" + name + " ";
0905:
0906: NamedNodeMap map = child.getAttributes();
0907:
0908: for (int j = 0; j < map.getLength(); j++) {
0909: Node att = map.item(j);
0910: value += att.getNodeName() + "=\"" + att.getNodeValue()
0911: + "\" ";
0912: }
0913:
0914: value += " />";
0915: }
0916:
0917: value += "</" + node.getNodeName() + ">";
0918:
0919: boxes.add(new XMLBox(value.getBytes()));
0920: }
0921:
0922: private void createHeaderBoxFromStandardNode(Node node, int numComps) {
0923: HeaderBox header = (HeaderBox) getElement("JPEG2000HeaderBox");
0924: byte unknownColor = (byte) (getElement("JPEG2000ColorSpecificationBox") == null ? 1
0925: : 0);
0926: if (header != null) {
0927: if (numComps == 0)
0928: ;
0929: numComps = header.getNumComponents();
0930:
0931: header = new HeaderBox(header.getHeight(), header
0932: .getWidth(), numComps, header.getBitDepth(), header
0933: .getCompressionType(), unknownColor, header
0934: .getIntellectualProperty());
0935: } else {
0936: header = new HeaderBox(0, 0, numComps, 0, 0, unknownColor,
0937: 0);
0938: }
0939: replace("JPEG2000HeaderBox", header);
0940: }
0941:
0942: private void createChannelDefinitionFromStandardNode(Node node) {
0943: if (node.getNodeName() != "Transparency")
0944: throw new IllegalArgumentException(I18N
0945: .getString("J2KMetadata8"));
0946:
0947: HeaderBox header = (HeaderBox) getElement("JPEG2000HeaderBox");
0948: int numComps = 3;
0949:
0950: if (header != null) {
0951: numComps = header.getNumComponents();
0952: }
0953:
0954: NodeList children = node.getChildNodes();
0955: boolean hasAlpha = false;
0956: boolean isPremultiplied = false;
0957:
0958: for (int i = 0; i < children.getLength(); i++) {
0959: Node child = children.item(i);
0960: String name = child.getNodeName();
0961:
0962: if (name.equals("Alpha")) {
0963: String value = (String) Box
0964: .getAttribute(child, "value");
0965: if (value.equals("premultiplied"))
0966: isPremultiplied = true;
0967: if (value.equals("nonpremultiplied"))
0968: hasAlpha = true;
0969: }
0970: }
0971:
0972: if (!hasAlpha)
0973: return;
0974:
0975: int num = (short) (numComps * (isPremultiplied ? 3 : 2));
0976: short[] channels = new short[num];
0977: short[] types = new short[num];
0978: short[] associations = new short[num];
0979: ChannelDefinitionBox.fillBasedOnBands(numComps,
0980: isPremultiplied, channels, types, associations);
0981: replace("JPEG2000ChannelDefinitionBox",
0982: new ChannelDefinitionBox(channels, types, associations));
0983: }
0984:
0985: private void replace(String name, Box box) {
0986: for (int i = boxes.size() - 1; i >= 0; i--) {
0987: Box box1 = (Box) boxes.get(i);
0988: if (name.equals(Box.getName(box1.getType()))) {
0989: boxes.set(i, box);
0990: return;
0991: }
0992: }
0993:
0994: boxes.add(box);
0995: }
0996:
0997: private boolean insertNodeIntoTree(IIOMetadataNode root,
0998: IIOMetadataNode node) {
0999: String name = node.getNodeName();
1000: String parent = format.getParent(name);
1001: if (parent == null)
1002: return false;
1003:
1004: IIOMetadataNode parentNode = getNodeFromTree(root, parent, name);
1005: if (parentNode == null)
1006: parentNode = createNodeIntoTree(root, parent);
1007: parentNode.appendChild(node);
1008: return true;
1009: }
1010:
1011: private IIOMetadataNode getNodeFromTree(IIOMetadataNode root,
1012: String name, String childName) {
1013: if (name.equals(root.getNodeName()))
1014: return root;
1015:
1016: NodeList list = root.getChildNodes();
1017: for (int i = 0; i < list.getLength(); i++) {
1018: IIOMetadataNode node = (IIOMetadataNode) list.item(i);
1019: if (node.getNodeName().equals(name)) {
1020: if (name.equals("JPEG2000UUIDInfoBox")
1021: && checkUUIDInfoBox(node, childName))
1022: continue;
1023: else
1024: return node;
1025: }
1026: node = getNodeFromTree(node, name, childName);
1027: if (node != null)
1028: return node;
1029: }
1030:
1031: return null;
1032: }
1033:
1034: private IIOMetadataNode createNodeIntoTree(IIOMetadataNode root,
1035: String name) {
1036: IIOMetadataNode node = getNodeFromTree(root, name, null);
1037: if (node != null)
1038: return node;
1039:
1040: node = new IIOMetadataNode(name);
1041:
1042: String parent = format.getParent(name);
1043: IIOMetadataNode parentNode = createNodeIntoTree(root, parent);
1044: parentNode.appendChild(node);
1045:
1046: return node;
1047: }
1048:
1049: private boolean isOriginalSigned(SampleModel sampleModel) {
1050: int type = sampleModel.getDataType();
1051: if (type == DataBuffer.TYPE_BYTE
1052: || type == DataBuffer.TYPE_USHORT)
1053: return false;
1054: return true;
1055: }
1056:
1057: /** Check whether the child with a name <code>childName</code> exists.
1058: * This method is designed because UUID info box may have many instances.
1059: * So if one of its sub-box is inserted into the tree, an empty slut for
1060: * this sub-box has to be find or created to avoid one UUID info box
1061: * has duplicated sub-boxes. The users have to guarantee each UUID info
1062: * box has all the sub-boxes.
1063: */
1064: private boolean checkUUIDInfoBox(Node node, String childName) {
1065:
1066: NodeList list = node.getChildNodes();
1067: for (int i = 0; i < list.getLength(); i++) {
1068: IIOMetadataNode child = (IIOMetadataNode) list.item(i);
1069: String name = child.getNodeName();
1070:
1071: if (name.equals(childName))
1072: return true;
1073: }
1074:
1075: return false;
1076: }
1077: }
|