001/* 002 * $RCSfile: J2KMetadata.java,v $ 003 * 004 * 005 * Copyright (c) 2005 Sun Microsystems, Inc. All Rights Reserved. 006 * 007 * Redistribution and use in source and binary forms, with or without 008 * modification, are permitted provided that the following conditions 009 * are met: 010 * 011 * - Redistribution of source code must retain the above copyright 012 * notice, this list of conditions and the following disclaimer. 013 * 014 * - Redistribution in binary form must reproduce the above copyright 015 * notice, this list of conditions and the following disclaimer in 016 * the documentation and/or other materials provided with the 017 * distribution. 018 * 019 * Neither the name of Sun Microsystems, Inc. or the names of 020 * contributors may be used to endorse or promote products derived 021 * from this software without specific prior written permission. 022 * 023 * This software is provided "AS IS," without a warranty of any 024 * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND 025 * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, 026 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY 027 * EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL 028 * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF 029 * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS 030 * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR 031 * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, 032 * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND 033 * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR 034 * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE 035 * POSSIBILITY OF SUCH DAMAGES. 036 * 037 * You acknowledge that this software is not designed or intended for 038 * use in the design, construction, operation or maintenance of any 039 * nuclear facility. 040 * 041 * $Revision: 1.4 $ 042 * $Date: 2006/09/22 23:07:25 $ 043 * $State: Exp $ 044 */ 045package com.sun.media.imageioimpl.plugins.jpeg2000; 046 047import java.io.InputStream; 048 049import javax.imageio.ImageTypeSpecifier; 050import javax.imageio.ImageWriteParam; 051import javax.imageio.ImageWriter; 052import javax.imageio.IIOException; 053import javax.imageio.stream.ImageInputStream; 054import javax.imageio.stream.ImageOutputStream; 055import javax.imageio.metadata.IIOMetadata; 056import javax.imageio.metadata.IIOMetadataNode; 057import javax.imageio.metadata.IIOMetadataFormat; 058import javax.imageio.metadata.IIOMetadataFormatImpl; 059import javax.imageio.metadata.IIOInvalidTreeException; 060 061import org.w3c.dom.Node; 062import org.w3c.dom.NodeList; 063import org.w3c.dom.NamedNodeMap; 064 065import java.util.List; 066import java.util.ArrayList; 067import java.util.Arrays; 068import java.util.Hashtable; 069import java.util.Iterator; 070import java.util.ListIterator; 071import java.io.IOException; 072import java.awt.color.ICC_Profile; 073import java.awt.color.ICC_ColorSpace; 074import java.awt.color.ColorSpace; 075import java.awt.image.ColorModel; 076import java.awt.image.DataBuffer; 077import java.awt.image.IndexColorModel; 078import java.awt.image.SampleModel; 079import java.awt.Point; 080 081import com.sun.media.imageio.plugins.jpeg2000.J2KImageReadParam; 082import com.sun.media.imageio.plugins.jpeg2000.J2KImageWriteParam; 083 084import jj2000.j2k.fileformat.FileFormatBoxes; 085import jj2000.j2k.fileformat.reader.FileFormatReader; 086import jj2000.j2k.io.RandomAccessIO; 087 088/** 089 * Metadata for the J2K plug-in. 090 */ 091public class J2KMetadata extends IIOMetadata implements Cloneable { 092 static final String nativeMetadataFormatName = 093 "com_sun_media_imageio_plugins_jpeg2000_image_1.0"; 094 095 /** cache the metadata format */ 096 private J2KMetadataFormat format; 097 098 /** The boxes of JP2 file used as meta data, i. e., all the boxes 099 * except the data stream box 100 */ 101 private ArrayList boxes = new ArrayList(); 102 103 /** 104 * Constructor containing code shared by other constructors. 105 */ 106 public J2KMetadata() { 107 super(true, // Supports standard format 108 nativeMetadataFormatName, // and a native format 109 "com.sun.media.imageioimpl.plugins.jpeg2000.J2KMetadataFormat", 110 null, null); // No other formats 111 112 format = (J2KMetadataFormat)getMetadataFormat(nativeMetadataFormatName); 113 } 114 115 /* 116 * Constructs a <code>J2KMetadata</code> object by reading the 117 * contents of an <code>ImageInputStream</code>. Has package-only 118 * access. 119 * 120 * @param iis An <code>ImageInputStream</code> from which to read 121 * the metadata. 122 * @param reader The <code>J2KImageReader</code> calling this 123 * constructor, to which warnings should be sent. 124 */ 125 public J2KMetadata(ImageInputStream iis, 126 J2KImageReader reader) throws IOException { 127 this(); 128 RandomAccessIO in = new IISRandomAccessIO(iis); 129 130 iis.mark(); 131 // **** File Format **** 132 // If the codestream is wrapped in the jp2 fileformat, Read the 133 // file format wrapper 134 FileFormatReader ff = new FileFormatReader(in, this); 135 ff.readFileFormat(); 136 iis.reset(); 137 } 138 139 /** 140 * Constructs a default stream <code>J2KMetadata</code> object appropriate 141 * for the given write parameters. 142 */ 143 public J2KMetadata(ImageWriteParam param, ImageWriter writer) { 144 this(null, param, writer); 145 } 146 147 /** 148 * Constructs a default image <code>J2KMetadata</code> object appropriate 149 * for the given image type and write parameters. 150 */ 151 public J2KMetadata(ImageTypeSpecifier imageType, 152 ImageWriteParam param, 153 ImageWriter writer) { 154 this(imageType != null ? imageType.getColorModel() : null, 155 imageType != null ? imageType.getSampleModel() : null, 156 0, 0, 157 param, writer); 158 } 159 160 /** 161 * Constructs a default image <code>J2KMetadata</code> object appropriate 162 * for the given image type and write parameters. 163 */ 164 public J2KMetadata(ColorModel colorModel, 165 SampleModel sampleModel, 166 int width, 167 int height, 168 ImageWriteParam param, 169 ImageWriter writer) { 170 this(); 171 addNode(new SignatureBox()); 172 addNode(new FileTypeBox(0x6A703220, 0, new int[]{0x6A703220})); 173 174 ImageTypeSpecifier destType = null; 175 176 if (param != null) { 177 destType = param.getDestinationType(); 178 if (colorModel == null && sampleModel == null) { 179 colorModel = destType == null ? null : destType.getColorModel(); 180 sampleModel = 181 destType == null ? null : destType.getSampleModel(); 182 } 183 } 184 185 int[] bitDepths = null; 186 if(colorModel != null) { 187 bitDepths = colorModel.getComponentSize(); 188 } else if(sampleModel != null) { 189 bitDepths = sampleModel.getSampleSize(); 190 } 191 192 int bitsPerComponent = 0xff; 193 if(bitDepths != null) { 194 bitsPerComponent = bitDepths[0]; 195 int numComponents = bitDepths.length; 196 for(int i = 1; i < numComponents; i++) { 197 /* XXX: This statement should be removed when BPC behavior 198 is corrected as derscribed below. */ 199 if(bitDepths[i] > bitsPerComponent) { 200 bitsPerComponent = bitDepths[i]; 201 } 202 /* XXX: When the number of bits per component is not the 203 same for all components the BPC parameter of the Image 204 Header box should be set to 0xff and the actual number of 205 bits per component written in the Bits Per Component box. 206 if(bitDepths[i] != bitsPerComponent) { 207 bitsPerComponent = 0xff; 208 break; 209 } 210 */ 211 } 212 } 213 214 if (colorModel != null) { 215 ColorSpace cs = colorModel.getColorSpace(); 216 boolean iccColor = (cs instanceof ICC_ColorSpace); 217 int type = cs.getType(); 218 219 if (type == ColorSpace.TYPE_RGB) { 220 addNode(new ColorSpecificationBox((byte)1, 221 (byte)0, (byte)0, 222 ColorSpecificationBox.ECS_sRGB, 223 null)); 224 } else if (type == ColorSpace.TYPE_GRAY) 225 addNode(new ColorSpecificationBox((byte)1, 226 (byte)0, (byte)0, 227 ColorSpecificationBox.ECS_GRAY, 228 null)); 229 else if (cs instanceof ICC_ColorSpace) 230 addNode(new ColorSpecificationBox((byte)2, 231 (byte)0, (byte)0, 232 0, 233 ((ICC_ColorSpace)cs).getProfile())); 234 235 if (colorModel.hasAlpha()) { 236 addNode(new ChannelDefinitionBox(colorModel)); 237 } 238 239 if (colorModel instanceof IndexColorModel) { 240 addNode(new PaletteBox((IndexColorModel)colorModel)); 241 int numComp = colorModel.getComponentSize().length; 242 short[] channels = new short[numComp]; 243 byte[] types = new byte[numComp]; 244 byte[] maps = new byte[numComp]; 245 for (int i = 0; i < numComp; i++) { 246 channels[i] = 0; 247 types[i] = 1; 248 maps[i] = (byte)i; 249 } 250 addNode(new ComponentMappingBox(channels, types, maps)); 251 } 252 } 253 254 if (sampleModel != null) { 255 if (width <= 0) 256 width = sampleModel.getWidth(); 257 if (height <= 0) 258 height = sampleModel.getHeight(); 259 int bpc = bitsPerComponent == 0xff ? 260 0xff : ((bitsPerComponent - 1) | 261 (isOriginalSigned(sampleModel) ? 0x80 : 0)); 262 addNode(new HeaderBox(height, 263 width, 264 sampleModel.getNumBands(), 265 bpc, 266 7, 267 colorModel == null ? 1 : 0, 268 getElement("JPEG2000IntellectualPropertyRightsBox")==null ? 0 : 1)); 269 } 270 } 271 272 public Object clone() { 273 J2KMetadata theClone = null; 274 275 try { 276 theClone = (J2KMetadata) super.clone(); 277 } catch (CloneNotSupportedException e) {} // won't happen 278 279 if (boxes != null) { 280 int numBoxes = boxes.size(); 281 for(int i = 0; i < numBoxes; i++) { 282 theClone.addNode((Box)boxes.get(i)); 283 } 284 } 285 return theClone; 286 } 287 288 public Node getAsTree(String formatName) { 289 if (formatName == null) { 290 throw new IllegalArgumentException(I18N.getString("J2KMetadata0")); 291 } 292 293 if (formatName.equals(nativeMetadataFormatName)) { 294 return getNativeTree(); 295 } 296 297 if (formatName.equals 298 (IIOMetadataFormatImpl.standardMetadataFormatName)) { 299 return getStandardTree(); 300 } 301 302 throw new IllegalArgumentException(I18N.getString("J2KMetadata1") 303 + " " + formatName); 304 } 305 306 IIOMetadataNode getNativeTree() { 307 IIOMetadataNode root = 308 new IIOMetadataNode(nativeMetadataFormatName); 309 310 Box signatureBox = null, fileTypeBox = null, headerBox = null; 311 int signatureIndex = -1, fileTypeIndex = -1, headerIndex = -1; 312 313 int numBoxes = boxes.size(); 314 315 int found = 0; 316 for(int i = 0; i < numBoxes && found < 3; i++) { 317 Box box = (Box)boxes.get(i); 318 if(Box.getName(box.getType()).equals("JPEG2000SignatureBox")) { 319 signatureBox = box; 320 signatureIndex = i; 321 found++; 322 } else if(Box.getName(box.getType()).equals("JPEG2000FileTypeBox")) { 323 fileTypeBox = box; 324 fileTypeIndex = i; 325 found++; 326 } else if(Box.getName(box.getType()).equals("JPEG2000HeaderBox")) { 327 headerBox = box; 328 headerIndex = i; 329 found++; 330 } 331 } 332 333 if(signatureBox != null) { 334 insertNodeIntoTree(root, signatureBox.getNativeNode()); 335 } 336 337 if(fileTypeBox != null) { 338 insertNodeIntoTree(root, fileTypeBox.getNativeNode()); 339 } 340 341 if(headerBox != null) { 342 insertNodeIntoTree(root, headerBox.getNativeNode()); 343 } 344 345 for(int i = 0; i < numBoxes; i++) { 346 if(i == signatureIndex || 347 i == fileTypeIndex || 348 i == headerIndex) continue; 349 Box box = (Box)boxes.get(i); 350 IIOMetadataNode node = box.getNativeNode(); 351 insertNodeIntoTree(root, node); 352 } 353 return root; 354 } 355 356 // Standard tree node methods 357 protected IIOMetadataNode getStandardChromaNode() { 358 HeaderBox header = (HeaderBox)getElement("JPEG2000HeaderBox"); 359 PaletteBox palette = (PaletteBox)getElement("JPEG2000PaletteBox"); 360 ColorSpecificationBox color = 361 (ColorSpecificationBox)getElement("JPEG2000ColorSpecificationBox"); 362 363 IIOMetadataNode node = new IIOMetadataNode("Chroma"); 364 IIOMetadataNode subNode = null; 365 if (header != null) { 366 if (header.getUnknownColorspace() == 0) { 367 if (color != null && color.getMethod() == 1) { 368 subNode = new IIOMetadataNode("ColorSpaceType"); 369 int ecs = color.getEnumeratedColorSpace(); 370 if (ecs == FileFormatBoxes.CSB_ENUM_SRGB) 371 subNode.setAttribute("name", "RGB"); 372 if (ecs == FileFormatBoxes.CSB_ENUM_GREY) 373 subNode.setAttribute("name", "GRAY"); 374 node.appendChild(subNode); 375 } 376 } 377 378 subNode = new IIOMetadataNode("NumChannels"); 379 subNode.setAttribute("value", "" + header.getNumComponents()); 380 node.appendChild(subNode); 381 382 if (palette != null) { 383 subNode.setAttribute("value", "" + palette.getNumComp()); 384 subNode = new IIOMetadataNode("Palette"); 385 byte[][] lut = palette.getLUT(); 386 387 int size = lut[0].length; 388 int numComp = lut.length; 389 390 for (int i = 0; i < size; i++) { 391 IIOMetadataNode subNode1 = 392 new IIOMetadataNode("PaletteEntry"); 393 subNode1.setAttribute("index", ""+i); 394 subNode1.setAttribute("red", "" + (lut[0][i]&0xff)); 395 subNode1.setAttribute("green", "" + (lut[1][i]&0xff)); 396 subNode1.setAttribute("blue", "" + (lut[2][i]&0xff)); 397 if (numComp == 4) 398 subNode1.setAttribute("alpha", "" + (lut[3][i]&0xff)); 399 subNode.appendChild(subNode1); 400 } 401 node.appendChild(subNode); 402 } 403 } 404 return node; 405 } 406 407 protected IIOMetadataNode getStandardCompressionNode() { 408 IIOMetadataNode node = new IIOMetadataNode("Compression"); 409 410 // CompressionTypeName 411 IIOMetadataNode subNode = new IIOMetadataNode("CompressionTypeName"); 412 subNode.setAttribute("value", "JPEG2000"); 413 node.appendChild(subNode); 414 return node; 415 } 416 417 protected IIOMetadataNode getStandardDataNode() { 418 IIOMetadataNode node = new IIOMetadataNode("Data"); 419 PaletteBox palette = (PaletteBox)getElement("JPEG2000PaletteBox"); 420 boolean sampleFormat = false; 421 422 if (palette != null) { 423 IIOMetadataNode subNode = new IIOMetadataNode("SampleFormat"); 424 subNode.setAttribute("value", "Index"); 425 node.appendChild(subNode); 426 sampleFormat = true; 427 } 428 429 BitsPerComponentBox bitDepth = 430 (BitsPerComponentBox)getElement("JPEG2000BitsPerComponentBox"); 431 String value = ""; 432 boolean signed = false; 433 boolean gotSampleInfo = false; 434 435 // JPEG 2000 "B" parameter represents "bitDepth - 1" in the 436 // right 7 least significant bits with the most significant 437 // bit indicating signed if set and unsigned if not. 438 if (bitDepth != null) { 439 byte[] bits = bitDepth.getBitDepth(); 440 if ((bits[0] & 0x80) == 0x80) 441 signed = true; 442 443 int numComp = bits.length; 444 for (int i = 0; i < numComp; i++) { 445 value += (bits[i] & 0x7f) + 1; 446 if(i != numComp - 1) value += " "; 447 } 448 449 gotSampleInfo = true; 450 } else { 451 HeaderBox header = (HeaderBox)getElement("JPEG2000HeaderBox"); 452 if(header != null) { 453 int bits = header.getBitDepth(); 454 if ((bits & 0x80) == 0x80) 455 signed = true; 456 bits = (bits & 0x7f) + 1; 457 int numComp = header.getNumComponents(); 458 for (int i = 0; i < numComp; i++) { 459 value += bits; 460 if(i != numComp - 1) value += " "; 461 } 462 463 gotSampleInfo = true; 464 } 465 } 466 467 IIOMetadataNode subNode = null; 468 469 if(gotSampleInfo) { 470 subNode = new IIOMetadataNode("BitsPerSample"); 471 subNode.setAttribute("value", value); 472 node.appendChild(subNode); 473 } 474 475 subNode = new IIOMetadataNode("PlanarConfiguration"); 476 subNode.setAttribute("value", "TileInterleaved"); 477 node.appendChild(subNode); 478 479 if (!sampleFormat && gotSampleInfo) { 480 subNode = new IIOMetadataNode("SampleFormat"); 481 subNode.setAttribute("value", 482 signed ? "SignedIntegral": "UnsignedIntegral"); 483 node.appendChild(subNode); 484 } 485 486 return node; 487 } 488 489 protected IIOMetadataNode getStandardDimensionNode() { 490 ResolutionBox box = 491 (ResolutionBox)getElement("JPEG2000CaptureResolutionBox"); 492 if (box != null) { 493 IIOMetadataNode node = new IIOMetadataNode("Dimension"); 494 float hRes = box.getHorizontalResolution(); 495 float vRes = box.getVerticalResolution(); 496 float ratio = vRes / hRes; 497 IIOMetadataNode subNode = new IIOMetadataNode("PixelAspectRatio"); 498 subNode.setAttribute("value", "" + ratio); 499 node.appendChild(subNode); 500 501 subNode = new IIOMetadataNode("HorizontalPixelSize"); 502 subNode.setAttribute("value", "" + (1000 / hRes)); 503 node.appendChild(subNode); 504 505 subNode = new IIOMetadataNode("VerticalPixelSize"); 506 subNode.setAttribute("value", "" + (1000 / vRes)); 507 node.appendChild(subNode); 508 509 return node; 510 } 511 512 return null; 513 } 514 515 protected IIOMetadataNode getStandardTransparencyNode() { 516 ChannelDefinitionBox channel = 517 (ChannelDefinitionBox)getElement("JPEG2000ChannelDefinitionBox"); 518 if (channel != null) { 519 IIOMetadataNode node = new IIOMetadataNode("Transparency"); 520 521 boolean hasAlpha = false; 522 boolean isPremultiplied = false; 523 short[] type = channel.getTypes(); 524 525 for (int i = 0; i < type.length; i++) { 526 if (type[i] == 1) 527 hasAlpha = true; 528 if (type[i] == 2) 529 isPremultiplied = true; 530 } 531 532 String value = "none"; 533 if (isPremultiplied) 534 value = "premultiplied"; 535 else if (hasAlpha) 536 value = "nonpremultiplied"; 537 538 IIOMetadataNode subNode = new IIOMetadataNode("Alpha"); 539 subNode.setAttribute("value", value); 540 node.appendChild(subNode); 541 542 return node; 543 } 544 545 IIOMetadataNode node = new IIOMetadataNode("Transparency"); 546 IIOMetadataNode subNode = new IIOMetadataNode("Alpha"); 547 subNode.setAttribute("value", "none"); 548 node.appendChild(subNode); 549 550 return null; 551 } 552 553 protected IIOMetadataNode getStandardTextNode() { 554 if (boxes == null) 555 return null; 556 IIOMetadataNode text = null; 557 558 Iterator iterator = boxes.iterator(); 559 560 while(iterator.hasNext()) { 561 Box box = (Box)iterator.next(); 562 if (box instanceof XMLBox) { 563 if (text == null) 564 text = new IIOMetadataNode("Text"); 565 IIOMetadataNode subNode = new IIOMetadataNode("TextEntry"); 566 String content = new String(box.getContent()); 567 subNode.setAttribute("value", content); 568 text.appendChild(subNode); 569 } 570 } 571 return text; 572 } 573 574 public boolean isReadOnly() { 575 return false; 576 } 577 578 public void mergeTree(String formatName, Node root) 579 throws IIOInvalidTreeException { 580 if (formatName == null) { 581 throw new IllegalArgumentException(I18N.getString("J2KMetadata0")); 582 } 583 584 if (root == null) { 585 throw new IllegalArgumentException(I18N.getString("J2KMetadata2")); 586 } 587 588 if (formatName.equals(nativeMetadataFormatName) && 589 root.getNodeName().equals(nativeMetadataFormatName)) { 590 mergeNativeTree(root); 591 } else if (formatName.equals 592 (IIOMetadataFormatImpl.standardMetadataFormatName)) { 593 mergeStandardTree(root); 594 } else { 595 throw new IllegalArgumentException(I18N.getString("J2KMetadata1") 596 + " " + formatName); 597 } 598 } 599 600 public void setFromTree(String formatName, Node root) 601 throws IIOInvalidTreeException { 602 if (formatName == null) { 603 throw new IllegalArgumentException(I18N.getString("J2KMetadata0")); 604 } 605 606 if (root == null) { 607 throw new IllegalArgumentException(I18N.getString("J2KMetadata2")); 608 } 609 610 if (formatName.equals(nativeMetadataFormatName) && 611 root.getNodeName().equals(nativeMetadataFormatName)) { 612 boxes = new ArrayList(); 613 mergeNativeTree(root); 614 } else if (formatName.equals 615 (IIOMetadataFormatImpl.standardMetadataFormatName)) { 616 boxes = new ArrayList(); 617 mergeStandardTree(root); 618 } else { 619 throw new IllegalArgumentException(I18N.getString("J2KMetadata1") 620 + " " + formatName); 621 } 622 } 623 624 public void reset() { 625 boxes.clear(); 626 } 627 628 public void addNode(Box node) { 629 if (boxes == null) 630 boxes = new ArrayList(); 631 replace(Box.getName(node.getType()), node); 632 } 633 634 public Box getElement(String name) { 635 for (int i = boxes.size() - 1; i >= 0; i--) { 636 Box box = (Box)boxes.get(i); 637 if (name.equals(Box.getName(box.getType()))) 638 return box; 639 } 640 return null; 641 } 642 643 private void mergeNativeTree(Node root) throws IIOInvalidTreeException { 644 NodeList list = root.getChildNodes(); 645 for (int i = list.getLength() - 1; i >= 0; i--) { 646 Node node = list.item(i); 647 String name = node.getNodeName(); 648 if (format.getParent(name) != null) { 649 if (format.isLeaf(name)) { 650 String s = (String)Box.getAttribute(node, "Type"); 651 Box box = Box.createBox(Box.getTypeInt(s), node); 652 if (format.singleInstance(name)&&getElement(name) != null) { 653 replace(name, box); 654 } else 655 boxes.add(box); 656 } else { 657 mergeNativeTree(node); 658 } 659 } 660 } 661 } 662 663 private void mergeStandardTree(Node root) throws IIOInvalidTreeException { 664 NodeList children = root.getChildNodes(); 665 int numComps = 0; 666 667 for (int i = 0; i < children.getLength(); i++) { 668 Node node = children.item(i); 669 String name = node.getNodeName(); 670 if (name.equals("Chroma")) { 671 NodeList children1 = node.getChildNodes(); 672 for (int j = 0; j < children1.getLength(); j++) { 673 Node child = children1.item(j); 674 String name1 = child.getNodeName(); 675 676 if (name1.equals("NumChannels")) { 677 String s = (String)Box.getAttribute(child, "value"); 678 numComps = new Integer(s).intValue(); 679 } 680 681 if (name1.equals("ColorSpaceType")) 682 createColorSpecificationBoxFromStandardNode(child); 683 684 if (name1.equals("Palette")) { 685 createPaletteBoxFromStandardNode(child); 686 } 687 } 688 } else if (name.equals("Compression")) { 689 // Intentionally do nothing: just prevent entry into 690 // the default "else" block and an ensuing 691 // IIOInvalidTreeException; fixes 5110389. 692 } else if (name.equals("Data")) { 693 createBitsPerComponentBoxFromStandardNode(node); 694 createHeaderBoxFromStandardNode(node, numComps); 695 } else if (name.equals("Dimension")) { 696 createResolutionBoxFromStandardNode(node); 697 } else if (name.equals("Document")) { 698 createXMLBoxFromStandardNode(node); 699 } else if (name.equals("Text")) { 700 createXMLBoxFromStandardNode(node); 701 } else if (name.equals("Transparency")) { 702 createChannelDefinitionFromStandardNode(node); 703 } else { 704 throw new IIOInvalidTreeException(I18N.getString("J2KMetadata3") 705 + " " + name, node); 706 } 707 } 708 } 709 710 private void createColorSpecificationBoxFromStandardNode(Node node) { 711 if (node.getNodeName() != "ColorSpaceType") 712 throw new IllegalArgumentException(I18N.getString("J2KMetadata4")); 713 String name = (String)Box.getAttribute(node, "name"); 714 int ecs = name.equals("RGB") ? ColorSpecificationBox.ECS_sRGB : 715 (name.equals("Gray") ? ColorSpecificationBox.ECS_GRAY : 0); 716 717 if (ecs == ColorSpecificationBox.ECS_sRGB || 718 ecs ==ColorSpecificationBox.ECS_GRAY) { 719 replace ("JPEG2000ColorSpecificationBox", 720 new ColorSpecificationBox((byte)1, (byte)0, (byte)0, 721 ecs, null)); 722 } 723 } 724 725 private void createPaletteBoxFromStandardNode(Node node) { 726 if (node.getNodeName() != "Palette") 727 throw new IllegalArgumentException(I18N.getString("J2KMetadata5")); 728 NodeList children = node.getChildNodes(); 729 int maxIndex = -1; 730 boolean hasAlpha = false; 731 for (int i = 0; i < children.getLength(); i++) { 732 Node child = children.item(i); 733 String name = child.getNodeName(); 734 735 if (name.equals("PaletteEntry")) { 736 String s = (String)Box.getAttribute(child, "index"); 737 int index = new Integer(s).intValue(); 738 if(index > maxIndex) { 739 maxIndex = index; 740 } 741 if(Box.getAttribute(child, "alpha") != null) { 742 hasAlpha = true; 743 } 744 } 745 } 746 747 // Determine palette size. 748 int numBits = 32; 749 int mask = 0x80000000; 750 while(mask != 0 && (maxIndex & mask) == 0) { 751 numBits--; 752 mask >>>= 1; 753 } 754 int size = 1 << numBits; 755 756 byte[] red = new byte[size]; 757 byte[] green = new byte[size]; 758 byte[] blue = new byte[size]; 759 byte[] alpha = hasAlpha ? new byte[size]: null; 760 761 for (int i = 0; i < children.getLength(); i++) { 762 Node child = children.item(i); 763 String name = child.getNodeName(); 764 765 if (name.equals("PaletteEntry")) { 766 String s = (String)Box.getAttribute(child, "index"); 767 int index = new Integer(s).intValue(); 768 s = (String)Box.getAttribute(child, "red"); 769 red[index] = (byte)(new Integer(s).intValue()); 770 s = (String)Box.getAttribute(child, "green"); 771 green[index] = (byte)(new Integer(s).intValue()); 772 s = (String)Box.getAttribute(child, "blue"); 773 blue[index] = (byte)(new Integer(s).intValue()); 774 775 byte t = (byte)255; 776 s = (String)Box.getAttribute(child, "alpha"); 777 if(s != null) { 778 t = (byte)(new Integer(s).intValue()); 779 } 780 781 if(alpha != null) { 782 alpha[index] = t; 783 } 784 } 785 } 786 787 IndexColorModel icm; 788 if (alpha == null) 789 icm = new IndexColorModel(numBits, size, red, green, blue); 790 else 791 icm = new IndexColorModel(numBits, size, red, green, blue, alpha); 792 793 replace("JPEG2000PaletteBox", new PaletteBox(icm)); 794 } 795 796 private void createBitsPerComponentBoxFromStandardNode(Node node) { 797 if (node.getNodeName() != "Data") 798 throw new IllegalArgumentException(I18N.getString("J2KMetadata6")); 799 800 NodeList children = node.getChildNodes(); 801 802 byte[] bits = null; 803 boolean isSigned = false; 804 for (int i = 0; i < children.getLength(); i++) { 805 Node child = children.item(i); 806 String name = child.getNodeName(); 807 808 if (name.equals("BitsPerSample")) { 809 String s = (String)Box.getAttribute(child, "value"); 810 bits = (byte[])Box.parseByteArray(s).clone(); 811 } else if(name.equals("SampleFormat")) { 812 String s = (String)Box.getAttribute(child, "value"); 813 isSigned = s.equals("SignedIntegral"); 814 } 815 } 816 817 if(bits != null) { 818 // JPEG 2000 "B" parameter represents "bitDepth - 1" in the 819 // right 7 least significant bits with the most significant 820 // bit indicating signed if set and unsigned if not. 821 for (int i = 0; i < bits.length; i++) { 822 bits[i] = (byte)((bits[i]&0xff) - 1); 823 if(isSigned) { 824 bits[i] |= 0x80; 825 } 826 } 827 828 replace("JPEG2000BitsPerComponent", 829 new BitsPerComponentBox(bits)); 830 } 831 } 832 833 private void createResolutionBoxFromStandardNode(Node node) { 834 if (node.getNodeName() != "Dimension") 835 throw new IllegalArgumentException(I18N.getString("J2KMetadata7")); 836 NodeList children = node.getChildNodes(); 837 float hRes = 0.0f; 838 float vRes = 0.0f; 839 840 boolean gotH = false, gotV = false; 841 842 for (int i = 0; i < children.getLength(); i++) { 843 Node child = children.item(i); 844 String name = child.getNodeName(); 845 846 if (name.equals("HorizontalPixelSize")) { 847 String s = (String)Box.getAttribute(child, "value"); 848 hRes = new Float(s).floatValue(); 849 hRes = 1000 / hRes; 850 gotH = true; 851 } 852 853 if (name.equals("VerticalPixelSize")) { 854 String s = (String)Box.getAttribute(child, "value"); 855 vRes = new Float(s).floatValue(); 856 vRes = 1000 / vRes; 857 gotV = true; 858 } 859 } 860 861 if(gotH && !gotV) { 862 vRes = hRes; 863 } else if(gotV && !gotH) { 864 hRes = vRes; 865 } 866 867 if(gotH || gotV) { 868 replace("JPEG2000CaptureResolutionBox", 869 new ResolutionBox(0x72657363, hRes, vRes)); 870 } 871 } 872 873 private void createXMLBoxFromStandardNode(Node node) { 874 NodeList children = node.getChildNodes(); 875 String value = "<" + node.getNodeName() + ">"; 876 877 for (int i = 0; i < children.getLength(); i++) { 878 Node child = children.item(i); 879 String name = child.getNodeName(); 880 value += "<" + name + " "; 881 882 NamedNodeMap map = child.getAttributes(); 883 884 for (int j = 0; j < map.getLength(); j++) { 885 Node att = map.item(j); 886 value += att.getNodeName() + "=\"" + 887 att.getNodeValue() + "\" "; 888 } 889 890 value += " />"; 891 } 892 893 value += "</" + node.getNodeName() + ">"; 894 895 boxes.add(new XMLBox(value.getBytes())); 896 } 897 898 private void createHeaderBoxFromStandardNode(Node node, int numComps) { 899 HeaderBox header = (HeaderBox)getElement("JPEG2000HeaderBox"); 900 byte unknownColor = 901 (byte)(getElement("JPEG2000ColorSpecificationBox") == null ? 1: 0); 902 if (header != null) { 903 if (numComps ==0); 904 numComps = header.getNumComponents(); 905 906 header = new HeaderBox(header.getHeight(), header.getWidth(), 907 numComps, 908 header.getBitDepth(), 909 header.getCompressionType(), 910 unknownColor, 911 header.getIntellectualProperty()); 912 } else { 913 header = new HeaderBox(0, 0, numComps, 0, 0, unknownColor, 0); 914 } 915 replace("JPEG2000HeaderBox", header); 916 } 917 918 private void createChannelDefinitionFromStandardNode(Node node) { 919 if (node.getNodeName() != "Transparency") 920 throw new IllegalArgumentException(I18N.getString("J2KMetadata8")); 921 922 HeaderBox header = (HeaderBox)getElement("JPEG2000HeaderBox"); 923 int numComps = 3; 924 925 if (header != null) { 926 numComps = header.getNumComponents(); 927 } 928 929 NodeList children = node.getChildNodes(); 930 boolean hasAlpha = false; 931 boolean isPremultiplied = false; 932 933 for (int i = 0; i < children.getLength(); i++) { 934 Node child = children.item(i); 935 String name = child.getNodeName(); 936 937 if (name.equals("Alpha")) { 938 String value = (String)Box.getAttribute(child, "value"); 939 if (value.equals("premultiplied")) 940 isPremultiplied = true; 941 if (value.equals("nonpremultiplied")) 942 hasAlpha = true; 943 } 944 } 945 946 if (!hasAlpha) 947 return; 948 949 int num = (short)(numComps * (isPremultiplied ? 3 : 2)); 950 short[] channels = new short[num]; 951 short[] types = new short[num]; 952 short[] associations = new short[num]; 953 ChannelDefinitionBox.fillBasedOnBands(numComps, isPremultiplied, 954 channels, types, associations); 955 replace("JPEG2000ChannelDefinitionBox", 956 new ChannelDefinitionBox(channels, types, associations)); 957 } 958 959 private void replace(String name, Box box) { 960 for (int i = boxes.size() - 1; i >= 0; i--) { 961 Box box1 = (Box)boxes.get(i); 962 if (name.equals(Box.getName(box1.getType()))) { 963 boxes.set(i, box); 964 return; 965 } 966 } 967 968 boxes.add(box); 969 } 970 971 private boolean insertNodeIntoTree(IIOMetadataNode root, 972 IIOMetadataNode node) { 973 String name = node.getNodeName(); 974 String parent = format.getParent(name); 975 if (parent == null) 976 return false; 977 978 IIOMetadataNode parentNode = getNodeFromTree(root, parent, name); 979 if (parentNode == null) 980 parentNode = createNodeIntoTree(root, parent); 981 parentNode.appendChild(node); 982 return true; 983 } 984 985 private IIOMetadataNode getNodeFromTree(IIOMetadataNode root, 986 String name, 987 String childName) { 988 if (name.equals(root.getNodeName())) 989 return root; 990 991 NodeList list = root.getChildNodes(); 992 for (int i = 0; i < list.getLength(); i++) { 993 IIOMetadataNode node = (IIOMetadataNode)list.item(i); 994 if (node.getNodeName().equals(name)) { 995 if (name.equals("JPEG2000UUIDInfoBox") && 996 checkUUIDInfoBox(node, childName)) 997 continue; 998 else 999 return node; 1000 } 1001 node = getNodeFromTree(node, name, childName); 1002 if (node != null) 1003 return node; 1004 } 1005 1006 return null; 1007 } 1008 1009 private IIOMetadataNode createNodeIntoTree(IIOMetadataNode root, 1010 String name) { 1011 IIOMetadataNode node = getNodeFromTree(root, name, null); 1012 if (node != null) 1013 return node; 1014 1015 node = new IIOMetadataNode(name); 1016 1017 String parent = format.getParent(name); 1018 IIOMetadataNode parentNode = createNodeIntoTree(root, parent); 1019 parentNode.appendChild(node); 1020 1021 return node; 1022 } 1023 1024 private boolean isOriginalSigned(SampleModel sampleModel) { 1025 int type = sampleModel.getDataType(); 1026 if (type == DataBuffer.TYPE_BYTE || type == DataBuffer.TYPE_USHORT) 1027 return false; 1028 return true; 1029 } 1030 1031 /** Check whether the child with a name <code>childName</code> exists. 1032 * This method is designed because UUID info box may have many instances. 1033 * So if one of its sub-box is inserted into the tree, an empty slut for 1034 * this sub-box has to be find or created to avoid one UUID info box 1035 * has duplicated sub-boxes. The users have to guarantee each UUID info 1036 * box has all the sub-boxes. 1037 */ 1038 private boolean checkUUIDInfoBox(Node node, String childName) { 1039 1040 NodeList list = node.getChildNodes(); 1041 for (int i = 0; i < list.getLength(); i++) { 1042 IIOMetadataNode child = (IIOMetadataNode)list.item(i); 1043 String name = child.getNodeName(); 1044 1045 if (name.equals(childName)) 1046 return true; 1047 } 1048 1049 return false; 1050 } 1051}