001/* 002 * $RCSfile: BMPMetadata.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.2 $ 042 * $Date: 2006/04/21 23:14:37 $ 043 * $State: Exp $ 044 */ 045package com.github.jaiimageio.impl.plugins.bmp; 046 047import java.io.UnsupportedEncodingException; 048import java.awt.image.ColorModel; 049import java.awt.image.DirectColorModel; 050import java.awt.image.IndexColorModel; 051import java.awt.image.SampleModel; 052import java.util.ArrayList; 053import java.util.Arrays; 054import java.util.Iterator; 055import java.util.List; 056import java.util.StringTokenizer; 057 058import javax.imageio.ImageTypeSpecifier; 059import javax.imageio.ImageWriteParam; 060import javax.imageio.metadata.IIOInvalidTreeException; 061import javax.imageio.metadata.IIOMetadata; 062import javax.imageio.metadata.IIOMetadataNode; 063import javax.imageio.metadata.IIOMetadataFormat; 064import javax.imageio.metadata.IIOMetadataFormatImpl; 065 066import org.w3c.dom.Node; 067 068import com.github.jaiimageio.impl.common.ImageUtil; 069import com.github.jaiimageio.plugins.bmp.BMPImageWriteParam; 070 071public class BMPMetadata extends IIOMetadata 072 implements Cloneable, BMPConstants { 073 074 public static final String nativeMetadataFormatName = 075 "com_sun_media_imageio_plugins_bmp_image_1.0"; 076 077 // Fields for Image Descriptor 078 public String bmpVersion; 079 public int width ; 080 public int height; 081 public short bitsPerPixel; 082 public int compression; 083 public int imageSize; 084 085 // Fields for PixelsPerMeter 086 public int xPixelsPerMeter; 087 public int yPixelsPerMeter; 088 089 public int colorsUsed; 090 public int colorsImportant; 091 092 // Fields for BI_BITFIELDS compression(Mask) 093 public int redMask; 094 public int greenMask; 095 public int blueMask; 096 public int alphaMask; 097 098 public int colorSpace; 099 100 // Fields for CIE XYZ for the LCS_CALIBRATED_RGB color space 101 public double redX; 102 public double redY; 103 public double redZ; 104 public double greenX; 105 public double greenY; 106 public double greenZ; 107 public double blueX; 108 public double blueY; 109 public double blueZ; 110 111 // Fields for Gamma values for the LCS_CALIBRATED_RGB color space 112 public int gammaRed; 113 public int gammaGreen; 114 public int gammaBlue; 115 116 public int intent; 117 118 // Fields for the Palette and Entries 119 public byte[] palette = null; 120 public int paletteSize; 121 public int red; 122 public int green; 123 public int blue; 124 125 // Fields from CommentExtension 126 // List of String 127 public List comments = null; // new ArrayList(); 128 129 public BMPMetadata() { 130 super(true, 131 nativeMetadataFormatName, 132 "com.github.jaiimageio.impl.bmp.BMPMetadataFormat", 133 null, null); 134 } 135 136 public BMPMetadata(IIOMetadata metadata) 137 throws IIOInvalidTreeException { 138 139 this(); 140 141 if(metadata != null) { 142 List formats = Arrays.asList(metadata.getMetadataFormatNames()); 143 144 if(formats.contains(nativeMetadataFormatName)) { 145 // Initialize from native image metadata format. 146 setFromTree(nativeMetadataFormatName, 147 metadata.getAsTree(nativeMetadataFormatName)); 148 } else if(metadata.isStandardMetadataFormatSupported()) { 149 // Initialize from standard metadata form of the input tree. 150 String format = 151 IIOMetadataFormatImpl.standardMetadataFormatName; 152 setFromTree(format, metadata.getAsTree(format)); 153 } 154 } 155 } 156 157 public boolean isReadOnly() { 158 return false; 159 } 160 161 public Object clone() { 162 BMPMetadata metadata; 163 try { 164 metadata = (BMPMetadata)super.clone(); 165 } catch (CloneNotSupportedException e) { 166 return null; 167 } 168 169 return metadata; 170 } 171 172 public Node getAsTree(String formatName) { 173 if (formatName.equals(nativeMetadataFormatName)) { 174 return getNativeTree(); 175 } else if (formatName.equals 176 (IIOMetadataFormatImpl.standardMetadataFormatName)) { 177 return getStandardTree(); 178 } else { 179 throw new IllegalArgumentException(I18N.getString("BMPMetadata0")); 180 } 181 } 182 183 private Node getNativeTree() { 184 IIOMetadataNode root = 185 new IIOMetadataNode(nativeMetadataFormatName); 186 187 addChildNode(root, "BMPVersion", bmpVersion); 188 addChildNode(root, "Width", new Integer(width)); 189 addChildNode(root, "Height", new Integer(height)); 190 addChildNode(root, "BitsPerPixel", new Short(bitsPerPixel)); 191 addChildNode(root, "Compression", new Integer(compression)); 192 addChildNode(root, "ImageSize", new Integer(imageSize)); 193 194 IIOMetadataNode node; 195 if(xPixelsPerMeter > 0 && yPixelsPerMeter > 0) { 196 node = addChildNode(root, "PixelsPerMeter", null); 197 addChildNode(node, "X", new Integer(xPixelsPerMeter)); 198 addChildNode(node, "Y", new Integer(yPixelsPerMeter)); 199 } 200 201 addChildNode(root, "ColorsUsed", new Integer(colorsUsed)); 202 addChildNode(root, "ColorsImportant", new Integer(colorsImportant)); 203 204 int version = 0; 205 for (int i = 0; i < bmpVersion.length(); i++) 206 if (Character.isDigit(bmpVersion.charAt(i))) 207 version = bmpVersion.charAt(i) -'0'; 208 209 if (version >= 4) { 210 node = addChildNode(root, "Mask", null); 211 addChildNode(node, "Red", new Integer(redMask)); 212 addChildNode(node, "Green", new Integer(greenMask)); 213 addChildNode(node, "Blue", new Integer(blueMask)); 214 addChildNode(node, "Alpha", new Integer(alphaMask)); 215 216 addChildNode(root, "ColorSpaceType", new Integer(colorSpace)); 217 218 node = addChildNode(root, "CIEXYZEndpoints", null); 219 addXYZPoints(node, "Red", redX, redY, redZ); 220 addXYZPoints(node, "Green", greenX, greenY, greenZ); 221 addXYZPoints(node, "Blue", blueX, blueY, blueZ); 222 223 node = addChildNode(root, "Gamma", null); 224 addChildNode(node, "Red", new Integer(gammaRed)); 225 addChildNode(node, "Green", new Integer(gammaGreen)); 226 addChildNode(node, "Blue", new Integer(gammaBlue)); 227 228 node = addChildNode(root, "Intent", new Integer(intent)); 229 } 230 231 // Palette 232 if ((palette != null) && (paletteSize > 0)) { 233 node = addChildNode(root, "Palette", null); 234 boolean isVersion2 = 235 bmpVersion != null && bmpVersion.equals(VERSION_2); 236 237 for (int i = 0, j = 0; i < paletteSize; i++) { 238 IIOMetadataNode entry = 239 addChildNode(node, "PaletteEntry", null); 240 blue = palette[j++] & 0xff; 241 green = palette[j++] & 0xff; 242 red = palette[j++] & 0xff; 243 addChildNode(entry, "Red", new Integer(red)); 244 addChildNode(entry, "Green", new Integer(green)); 245 addChildNode(entry, "Blue", new Integer(blue)); 246 if(!isVersion2) j++; // skip reserved entry 247 } 248 } 249 250 return root; 251 } 252 253 // Standard tree node methods 254 protected IIOMetadataNode getStandardChromaNode() { 255 256 IIOMetadataNode node = new IIOMetadataNode("Chroma"); 257 258 IIOMetadataNode subNode = new IIOMetadataNode("ColorSpaceType"); 259 String colorSpaceType; 260 if (((palette != null) && (paletteSize > 0)) || 261 (redMask != 0 || greenMask != 0 || blueMask != 0) || 262 bitsPerPixel > 8) { 263 colorSpaceType = "RGB"; 264 } else { 265 colorSpaceType = "GRAY"; 266 } 267 subNode.setAttribute("name", colorSpaceType); 268 node.appendChild(subNode); 269 270 subNode = new IIOMetadataNode("NumChannels"); 271 String numChannels; 272 if (((palette != null) && (paletteSize > 0)) || 273 (redMask != 0 || greenMask != 0 || blueMask != 0) || 274 bitsPerPixel > 8) { 275 if(alphaMask != 0) { 276 numChannels = "4"; 277 } else { 278 numChannels = "3"; 279 } 280 } else { 281 numChannels = "1"; 282 } 283 subNode.setAttribute("value", numChannels); 284 node.appendChild(subNode); 285 286 if(gammaRed != 0 && gammaGreen != 0 && gammaBlue != 0) { 287 subNode = new IIOMetadataNode("Gamma"); 288 Double gamma = new Double((gammaRed+gammaGreen+gammaBlue)/3.0); 289 subNode.setAttribute("value", gamma.toString()); 290 node.appendChild(subNode); 291 } 292 293 if(numChannels.equals("1") && 294 (palette == null || paletteSize == 0)) { 295 subNode = new IIOMetadataNode("BlackIsZero"); 296 subNode.setAttribute("value", "TRUE"); 297 node.appendChild(subNode); 298 } 299 300 if ((palette != null) && (paletteSize > 0)) { 301 subNode = new IIOMetadataNode("Palette"); 302 boolean isVersion2 = 303 bmpVersion != null && bmpVersion.equals(VERSION_2); 304 305 for (int i = 0, j = 0; i < paletteSize; i++) { 306 IIOMetadataNode subNode1 = 307 new IIOMetadataNode("PaletteEntry"); 308 subNode1.setAttribute("index", ""+i); 309 subNode1.setAttribute("blue", "" + (palette[j++]&0xff)); 310 subNode1.setAttribute("green", "" + (palette[j++]&0xff)); 311 subNode1.setAttribute("red", "" + (palette[j++]&0xff)); 312 if(!isVersion2) j++; // skip reserved entry 313 subNode.appendChild(subNode1); 314 } 315 node.appendChild(subNode); 316 } 317 318 return node; 319 } 320 321 protected IIOMetadataNode getStandardCompressionNode() { 322 IIOMetadataNode node = new IIOMetadataNode("Compression"); 323 324 // CompressionTypeName 325 IIOMetadataNode subNode = new IIOMetadataNode("CompressionTypeName"); 326 subNode.setAttribute("value", compressionTypeNames[compression]); 327 node.appendChild(subNode); 328 329 subNode = new IIOMetadataNode("Lossless"); 330 subNode.setAttribute("value", 331 compression == BI_JPEG ? "FALSE" : "TRUE"); 332 node.appendChild(subNode); 333 334 return node; 335 } 336 337 protected IIOMetadataNode getStandardDataNode() { 338 IIOMetadataNode node = new IIOMetadataNode("Data"); 339 340 String sampleFormat = (palette != null) && (paletteSize > 0) ? 341 "Index" : "UnsignedIntegral"; 342 IIOMetadataNode subNode = new IIOMetadataNode("SampleFormat"); 343 subNode.setAttribute("value", sampleFormat); 344 node.appendChild(subNode); 345 346 String bits = ""; 347 if(redMask != 0 || greenMask != 0 || blueMask != 0) { 348 bits = 349 countBits(redMask) + " " + 350 countBits(greenMask) + " " + 351 countBits(blueMask); 352 if(alphaMask != 0) { 353 bits += " " + countBits(alphaMask); 354 } 355 } else if(palette != null && paletteSize > 0) { 356 for(int i = 1; i <= 3; i++) { 357 bits += bitsPerPixel; 358 if(i != 3) { 359 bits += " "; 360 } 361 } 362 } else { 363 if (bitsPerPixel == 1) { 364 bits = "1"; 365 } else if (bitsPerPixel == 4) { 366 bits = "4"; 367 } else if (bitsPerPixel == 8) { 368 bits = "8"; 369 } else if (bitsPerPixel == 16) { 370 bits = "5 6 5"; 371 } else if (bitsPerPixel == 24) { 372 bits = "8 8 8"; 373 } else if ( bitsPerPixel == 32) { 374 bits = "8 8 8 8"; 375 } 376 } 377 378 if(!bits.equals("")) { 379 subNode = new IIOMetadataNode("BitsPerSample"); 380 subNode.setAttribute("value", bits); 381 node.appendChild(subNode); 382 } 383 384 return node; 385 } 386 387 protected IIOMetadataNode getStandardDimensionNode() { 388 if (yPixelsPerMeter > 0 && xPixelsPerMeter > 0) { 389 IIOMetadataNode node = new IIOMetadataNode("Dimension"); 390 float ratio = (float)yPixelsPerMeter / (float)xPixelsPerMeter; 391 IIOMetadataNode subNode = new IIOMetadataNode("PixelAspectRatio"); 392 subNode.setAttribute("value", "" + ratio); 393 node.appendChild(subNode); 394 395 subNode = new IIOMetadataNode("HorizontalPixelSize"); 396 subNode.setAttribute("value", "" + (1000.0F / xPixelsPerMeter)); 397 node.appendChild(subNode); 398 399 subNode = new IIOMetadataNode("VerticalPixelSize"); 400 subNode.setAttribute("value", "" + (1000.0F / yPixelsPerMeter)); 401 node.appendChild(subNode); 402 403 // Emit HorizontalPhysicalPixelSpacing and 404 // VerticalPhysicalPixelSpacing for historical reasonse: 405 // HorizontalPixelSize and VerticalPixelSize should have 406 // been used in the first place. 407 subNode = new IIOMetadataNode("HorizontalPhysicalPixelSpacing"); 408 subNode.setAttribute("value", "" + (1000.0F / xPixelsPerMeter)); 409 node.appendChild(subNode); 410 411 subNode = new IIOMetadataNode("VerticalPhysicalPixelSpacing"); 412 subNode.setAttribute("value", "" + (1000.0F / yPixelsPerMeter)); 413 node.appendChild(subNode); 414 415 return node; 416 } 417 return null; 418 } 419 420 protected IIOMetadataNode getStandardDocumentNode() { 421 if(bmpVersion != null) { 422 IIOMetadataNode node = new IIOMetadataNode("Document"); 423 IIOMetadataNode subNode = new IIOMetadataNode("FormatVersion"); 424 subNode.setAttribute("value", bmpVersion); 425 node.appendChild(subNode); 426 return node; 427 } 428 return null; 429 } 430 431 protected IIOMetadataNode getStandardTextNode() { 432 if(comments != null) { 433 IIOMetadataNode node = new IIOMetadataNode("Text"); 434 Iterator iter = comments.iterator(); 435 while(iter.hasNext()) { 436 String comment = (String)iter.next(); 437 IIOMetadataNode subNode = new IIOMetadataNode("TextEntry"); 438 subNode.setAttribute("keyword", "comment"); 439 subNode.setAttribute("value", comment); 440 node.appendChild(subNode); 441 } 442 return node; 443 } 444 return null; 445 } 446 447 protected IIOMetadataNode getStandardTransparencyNode() { 448 IIOMetadataNode node = new IIOMetadataNode("Transparency"); 449 IIOMetadataNode subNode = new IIOMetadataNode("Alpha"); 450 String alpha; 451 if(alphaMask != 0) { 452 alpha = "nonpremultiplied"; 453 } else { 454 alpha = "none"; 455 } 456 457 subNode.setAttribute("value", alpha); 458 node.appendChild(subNode); 459 return node; 460 } 461 462 // Shorthand for throwing an IIOInvalidTreeException 463 private void fatal(Node node, String reason) 464 throws IIOInvalidTreeException { 465 throw new IIOInvalidTreeException(reason, node); 466 } 467 468 // Get an integer-valued attribute 469 private int getIntAttribute(Node node, String name, 470 int defaultValue, boolean required) 471 throws IIOInvalidTreeException { 472 String value = getAttribute(node, name, null, required); 473 if (value == null) { 474 return defaultValue; 475 } 476 return Integer.parseInt(value); 477 } 478 479 // Get a double-valued attribute 480 private double getDoubleAttribute(Node node, String name, 481 double defaultValue, boolean required) 482 throws IIOInvalidTreeException { 483 String value = getAttribute(node, name, null, required); 484 if (value == null) { 485 return defaultValue; 486 } 487 return Double.parseDouble(value); 488 } 489 490 // Get a required integer-valued attribute 491 private int getIntAttribute(Node node, String name) 492 throws IIOInvalidTreeException { 493 return getIntAttribute(node, name, -1, true); 494 } 495 496 // Get a required double-valued attribute 497 private double getDoubleAttribute(Node node, String name) 498 throws IIOInvalidTreeException { 499 return getDoubleAttribute(node, name, -1.0F, true); 500 } 501 502 // Get a String-valued attribute 503 private String getAttribute(Node node, String name, 504 String defaultValue, boolean required) 505 throws IIOInvalidTreeException { 506 Node attr = node.getAttributes().getNamedItem(name); 507 if (attr == null) { 508 if (!required) { 509 return defaultValue; 510 } else { 511 fatal(node, "Required attribute " + name + " not present!"); 512 } 513 } 514 return attr.getNodeValue(); 515 } 516 517 // Get a required String-valued attribute 518 private String getAttribute(Node node, String name) 519 throws IIOInvalidTreeException { 520 return getAttribute(node, name, null, true); 521 } 522 523 void initialize(ColorModel cm, SampleModel sm, ImageWriteParam param) { 524 // bmpVersion and compression. 525 if(param != null) { 526 bmpVersion = BMPConstants.VERSION_3; 527 528 if(param.getCompressionMode() == ImageWriteParam.MODE_EXPLICIT) { 529 String compressionType = param.getCompressionType(); 530 compression = 531 BMPImageWriter.getCompressionType(compressionType); 532 } 533 } else { 534 bmpVersion = BMPConstants.VERSION_3; 535 compression = BMPImageWriter.getPreferredCompressionType(cm, sm); 536 } 537 538 // width and height 539 width = sm.getWidth(); 540 height = sm.getHeight(); 541 542 // bitsPerPixel 543 bitsPerPixel = (short)cm.getPixelSize(); 544 545 // mask 546 if(cm instanceof DirectColorModel) { 547 DirectColorModel dcm = (DirectColorModel)cm; 548 redMask = dcm.getRedMask(); 549 greenMask = dcm.getGreenMask(); 550 blueMask = dcm.getBlueMask(); 551 alphaMask = dcm.getAlphaMask(); 552 } 553 554 // palette and paletteSize 555 if(cm instanceof IndexColorModel) { 556 IndexColorModel icm = (IndexColorModel)cm; 557 paletteSize = icm.getMapSize(); 558 559 byte[] r = new byte[paletteSize]; 560 byte[] g = new byte[paletteSize]; 561 byte[] b = new byte[paletteSize]; 562 563 icm.getReds(r); 564 icm.getGreens(g); 565 icm.getBlues(b); 566 567 boolean isVersion2 = 568 bmpVersion != null && bmpVersion.equals(VERSION_2); 569 570 palette = new byte[(isVersion2 ? 3 : 4)*paletteSize]; 571 for(int i = 0, j = 0; i < paletteSize; i++) { 572 palette[j++] = b[i]; 573 palette[j++] = g[i]; 574 palette[j++] = r[i]; 575 if(!isVersion2) j++; // skip reserved entry 576 } 577 } 578 } 579 580 public void mergeTree(String formatName, Node root) 581 throws IIOInvalidTreeException { 582 if (formatName.equals(nativeMetadataFormatName)) { 583 if (root == null) { 584 throw new IllegalArgumentException("root == null!"); 585 } 586 mergeNativeTree(root); 587 } else if (formatName.equals 588 (IIOMetadataFormatImpl.standardMetadataFormatName)) { 589 if (root == null) { 590 throw new IllegalArgumentException("root == null!"); 591 } 592 mergeStandardTree(root); 593 } else { 594 throw new IllegalArgumentException("Not a recognized format!"); 595 } 596 } 597 598 private void mergeNativeTree(Node root) 599 throws IIOInvalidTreeException { 600 Node node = root; 601 if (!node.getNodeName().equals(nativeMetadataFormatName)) { 602 fatal(node, "Root must be " + nativeMetadataFormatName); 603 } 604 605 byte[] r = null, g = null, b = null; 606 int maxIndex = -1; 607 608 node = node.getFirstChild(); 609 while (node != null) { 610 String name = node.getNodeName(); 611 612 if (name.equals("BMPVersion")) { 613 String value = getStringValue(node); 614 if(value != null) bmpVersion = value; 615 } else if (name.equals("Width")) { 616 Integer value = getIntegerValue(node); 617 if(value != null) width = value.intValue(); 618 } else if (name.equals("Height")) { 619 Integer value = getIntegerValue(node); 620 if(value != null) height = value.intValue(); 621 } else if (name.equals("BitsPerPixel")) { 622 Short value = getShortValue(node); 623 if(value != null) bitsPerPixel = value.shortValue(); 624 } else if (name.equals("Compression")) { 625 Integer value = getIntegerValue(node); 626 if(value != null) compression = value.intValue(); 627 } else if (name.equals("ImageSize")) { 628 Integer value = getIntegerValue(node); 629 if(value != null) imageSize = value.intValue(); 630 } else if (name.equals("PixelsPerMeter")) { 631 Node subNode = node.getFirstChild(); 632 while (subNode != null) { 633 String subName = subNode.getNodeName(); 634 if(subName.equals("X")) { 635 Integer value = getIntegerValue(subNode); 636 if(value != null) 637 xPixelsPerMeter = value.intValue(); 638 } else if(subName.equals("Y")) { 639 Integer value = getIntegerValue(subNode); 640 if(value != null) 641 yPixelsPerMeter = value.intValue(); 642 } 643 subNode = subNode.getNextSibling(); 644 } 645 } else if (name.equals("ColorsUsed")) { 646 Integer value = getIntegerValue(node); 647 if(value != null) colorsUsed = value.intValue(); 648 } else if (name.equals("ColorsImportant")) { 649 Integer value = getIntegerValue(node); 650 if(value != null) colorsImportant = value.intValue(); 651 } else if (name.equals("Mask")) { 652 Node subNode = node.getFirstChild(); 653 while (subNode != null) { 654 String subName = subNode.getNodeName(); 655 if(subName.equals("Red")) { 656 Integer value = getIntegerValue(subNode); 657 if(value != null) 658 redMask = value.intValue(); 659 } else if(subName.equals("Green")) { 660 Integer value = getIntegerValue(subNode); 661 if(value != null) 662 greenMask = value.intValue(); 663 } else if(subName.equals("Blue")) { 664 Integer value = getIntegerValue(subNode); 665 if(value != null) 666 blueMask = value.intValue(); 667 } else if(subName.equals("Alpha")) { 668 Integer value = getIntegerValue(subNode); 669 if(value != null) 670 alphaMask = value.intValue(); 671 } 672 subNode = subNode.getNextSibling(); 673 } 674 } else if (name.equals("ColorSpace")) { 675 Integer value = getIntegerValue(node); 676 if(value != null) colorSpace = value.intValue(); 677 } else if (name.equals("CIEXYZEndpoints")) { 678 Node subNode = node.getFirstChild(); 679 while (subNode != null) { 680 String subName = subNode.getNodeName(); 681 if(subName.equals("Red")) { 682 Node subNode1 = subNode.getFirstChild(); 683 while (subNode1 != null) { 684 String subName1 = subNode1.getNodeName(); 685 if(subName1.equals("X")) { 686 Double value = getDoubleValue(subNode1); 687 if(value != null) 688 redX = value.doubleValue(); 689 } else if(subName1.equals("Y")) { 690 Double value = getDoubleValue(subNode1); 691 if(value != null) 692 redY = value.doubleValue(); 693 } else if(subName1.equals("Z")) { 694 Double value = getDoubleValue(subNode1); 695 if(value != null) 696 redZ = value.doubleValue(); 697 } 698 subNode1 = subNode1.getNextSibling(); 699 } 700 } else if(subName.equals("Green")) { 701 Node subNode1 = subNode.getFirstChild(); 702 while (subNode1 != null) { 703 String subName1 = subNode1.getNodeName(); 704 if(subName1.equals("X")) { 705 Double value = getDoubleValue(subNode1); 706 if(value != null) 707 greenX = value.doubleValue(); 708 } else if(subName1.equals("Y")) { 709 Double value = getDoubleValue(subNode1); 710 if(value != null) 711 greenY = value.doubleValue(); 712 } else if(subName1.equals("Z")) { 713 Double value = getDoubleValue(subNode1); 714 if(value != null) 715 greenZ = value.doubleValue(); 716 } 717 subNode1 = subNode1.getNextSibling(); 718 } 719 } else if(subName.equals("Blue")) { 720 Node subNode1 = subNode.getFirstChild(); 721 while (subNode1 != null) { 722 String subName1 = subNode1.getNodeName(); 723 if(subName1.equals("X")) { 724 Double value = getDoubleValue(subNode1); 725 if(value != null) 726 blueX = value.doubleValue(); 727 } else if(subName1.equals("Y")) { 728 Double value = getDoubleValue(subNode1); 729 if(value != null) 730 blueY = value.doubleValue(); 731 } else if(subName1.equals("Z")) { 732 Double value = getDoubleValue(subNode1); 733 if(value != null) 734 blueZ = value.doubleValue(); 735 } 736 subNode1 = subNode1.getNextSibling(); 737 } 738 } 739 subNode = subNode.getNextSibling(); 740 } 741 } else if (name.equals("Gamma")) { 742 Node subNode = node.getFirstChild(); 743 while (subNode != null) { 744 String subName = subNode.getNodeName(); 745 if(subName.equals("Red")) { 746 Integer value = getIntegerValue(subNode); 747 if(value != null) 748 gammaRed = value.intValue(); 749 } else if(subName.equals("Green")) { 750 Integer value = getIntegerValue(subNode); 751 if(value != null) 752 gammaGreen = value.intValue(); 753 } else if(subName.equals("Blue")) { 754 Integer value = getIntegerValue(subNode); 755 if(value != null) 756 gammaBlue = value.intValue(); 757 } 758 subNode = subNode.getNextSibling(); 759 } 760 } else if (name.equals("Intent")) { 761 Integer value = getIntegerValue(node); 762 if(value != null) intent = value.intValue(); 763 } else if (name.equals("Palette")) { 764 paletteSize = getIntAttribute(node, "sizeOfPalette"); 765 766 r = new byte[paletteSize]; 767 g = new byte[paletteSize]; 768 b = new byte[paletteSize]; 769 maxIndex = -1; 770 771 Node paletteEntry = node.getFirstChild(); 772 if (paletteEntry == null) { 773 fatal(node, "Palette has no entries!"); 774 } 775 776 int numPaletteEntries = 0; 777 while (paletteEntry != null) { 778 if (!paletteEntry.getNodeName().equals("PaletteEntry")) { 779 fatal(node, 780 "Only a PaletteEntry may be a child of a Palette!"); 781 } 782 783 int index = -1; 784 Node subNode = paletteEntry.getFirstChild(); 785 while (subNode != null) { 786 String subName = subNode.getNodeName(); 787 if(subName.equals("Index")) { 788 Integer value = getIntegerValue(subNode); 789 if(value != null) 790 index = value.intValue(); 791 if (index < 0 || index > paletteSize - 1) { 792 fatal(node, 793 "Bad value for PaletteEntry attribute index!"); 794 } 795 } else if(subName.equals("Red")) { 796 Integer value = getIntegerValue(subNode); 797 if(value != null) 798 red = value.intValue(); 799 } else if(subName.equals("Green")) { 800 Integer value = getIntegerValue(subNode); 801 if(value != null) 802 green = value.intValue(); 803 } else if(subName.equals("Blue")) { 804 Integer value = getIntegerValue(subNode); 805 if(value != null) 806 blue = value.intValue(); 807 } 808 subNode = subNode.getNextSibling(); 809 } 810 811 if(index == -1) { 812 index = numPaletteEntries; 813 } 814 if (index > maxIndex) { 815 maxIndex = index; 816 } 817 818 r[index] = (byte)red; 819 g[index] = (byte)green; 820 b[index] = (byte)blue; 821 822 numPaletteEntries++; 823 paletteEntry = paletteEntry.getNextSibling(); 824 } 825 } else if (name.equals("CommentExtensions")) { 826 // CommentExtension 827 Node commentExtension = node.getFirstChild(); 828 if (commentExtension == null) { 829 fatal(node, "CommentExtensions has no entries!"); 830 } 831 832 if(comments == null) { 833 comments = new ArrayList(); 834 } 835 836 while (commentExtension != null) { 837 if (!commentExtension.getNodeName().equals("CommentExtension")) { 838 fatal(node, 839 "Only a CommentExtension may be a child of a CommentExtensions!"); 840 } 841 842 comments.add(getAttribute(commentExtension, "value")); 843 844 commentExtension = commentExtension.getNextSibling(); 845 } 846 } else { 847 fatal(node, "Unknown child of root node!"); 848 } 849 850 node = node.getNextSibling(); 851 } 852 853 if(r != null && g != null && b != null) { 854 boolean isVersion2 = 855 bmpVersion != null && bmpVersion.equals(VERSION_2); 856 857 int numEntries = maxIndex + 1; 858 palette = new byte[(isVersion2 ? 3 : 4)*numEntries]; 859 for(int i = 0, j = 0; i < numEntries; i++) { 860 palette[j++] = b[i]; 861 palette[j++] = g[i]; 862 palette[j++] = r[i]; 863 if(!isVersion2) j++; // skip reserved entry 864 } 865 } 866 } 867 868 private void mergeStandardTree(Node root) 869 throws IIOInvalidTreeException { 870 Node node = root; 871 if (!node.getNodeName() 872 .equals(IIOMetadataFormatImpl.standardMetadataFormatName)) { 873 fatal(node, "Root must be " + 874 IIOMetadataFormatImpl.standardMetadataFormatName); 875 } 876 877 String colorSpaceType = null; 878 int numChannels = 0; 879 int[] bitsPerSample = null; 880 boolean hasAlpha = false; 881 882 byte[] r = null, g = null, b = null; 883 int maxIndex = -1; 884 885 node = node.getFirstChild(); 886 while(node != null) { 887 String name = node.getNodeName(); 888 889 if (name.equals("Chroma")) { 890 Node child = node.getFirstChild(); 891 while (child != null) { 892 String childName = child.getNodeName(); 893 if (childName.equals("ColorSpaceType")) { 894 colorSpaceType = getAttribute(child, "name"); 895 } else if (childName.equals("NumChannels")) { 896 numChannels = getIntAttribute(child, "value"); 897 } else if (childName.equals("Gamma")) { 898 gammaRed = gammaGreen = gammaBlue = 899 (int)(getDoubleAttribute(child, "value") + 0.5); 900 } else if (childName.equals("Palette")) { 901 r = new byte[256]; 902 g = new byte[256]; 903 b = new byte[256]; 904 maxIndex = -1; 905 906 Node paletteEntry = child.getFirstChild(); 907 if (paletteEntry == null) { 908 fatal(node, "Palette has no entries!"); 909 } 910 911 while (paletteEntry != null) { 912 if (!paletteEntry.getNodeName().equals("PaletteEntry")) { 913 fatal(node, 914 "Only a PaletteEntry may be a child of a Palette!"); 915 } 916 917 int index = getIntAttribute(paletteEntry, "index"); 918 if (index < 0 || index > 255) { 919 fatal(node, 920 "Bad value for PaletteEntry attribute index!"); 921 } 922 if (index > maxIndex) { 923 maxIndex = index; 924 } 925 r[index] = 926 (byte)getIntAttribute(paletteEntry, "red"); 927 g[index] = 928 (byte)getIntAttribute(paletteEntry, "green"); 929 b[index] = 930 (byte)getIntAttribute(paletteEntry, "blue"); 931 932 paletteEntry = paletteEntry.getNextSibling(); 933 } 934 } 935 936 child = child.getNextSibling(); 937 } 938 } else if (name.equals("Compression")) { 939 Node child = node.getFirstChild(); 940 while(child != null) { 941 String childName = child.getNodeName(); 942 if (childName.equals("CompressionTypeName")) { 943 String compressionName = getAttribute(child, "value"); 944 compression = 945 BMPImageWriter.getCompressionType(compressionName); 946 } 947 child = child.getNextSibling(); 948 } 949 } else if (name.equals("Data")) { 950 Node child = node.getFirstChild(); 951 while(child != null) { 952 String childName = child.getNodeName(); 953 if (childName.equals("BitsPerSample")) { 954 List bps = new ArrayList(4); 955 String s = getAttribute(child, "value"); 956 StringTokenizer t = new StringTokenizer(s); 957 while(t.hasMoreTokens()) { 958 bps.add(Integer.valueOf(t.nextToken())); 959 } 960 bitsPerSample = new int[bps.size()]; 961 for(int i = 0; i < bitsPerSample.length; i++) { 962 bitsPerSample[i] = 963 ((Integer)bps.get(i)).intValue(); 964 } 965 break; 966 } 967 child = child.getNextSibling(); 968 } 969 } else if (name.equals("Dimension")) { 970 boolean gotWidth = false; 971 boolean gotHeight = false; 972 boolean gotAspectRatio = false; 973 boolean gotSpaceX = false; 974 boolean gotSpaceY = false; 975 976 double width = -1.0F; 977 double height = -1.0F; 978 double aspectRatio = -1.0F; 979 double spaceX = -1.0F; 980 double spaceY = -1.0F; 981 982 Node child = node.getFirstChild(); 983 while (child != null) { 984 String childName = child.getNodeName(); 985 if (childName.equals("PixelAspectRatio")) { 986 aspectRatio = getDoubleAttribute(child, "value"); 987 gotAspectRatio = true; 988 } else if (childName.equals("HorizontalPixelSize")) { 989 width = getDoubleAttribute(child, "value"); 990 gotWidth = true; 991 } else if (childName.equals("VerticalPixelSize")) { 992 height = getDoubleAttribute(child, "value"); 993 gotHeight = true; 994 } else if (childName.equals("HorizontalPhysicalPixelSpacing")) { 995 spaceX = getDoubleAttribute(child, "value"); 996 gotSpaceX = true; 997 } else if (childName.equals("VerticalPhysicalPixelSpacing")) { 998 spaceY = getDoubleAttribute(child, "value"); 999 gotSpaceY = true; 1000 // } else if (childName.equals("ImageOrientation")) { 1001 // } else if (childName.equals("HorizontalPosition")) { 1002 // } else if (childName.equals("VerticalPosition")) { 1003 // } else if (childName.equals("HorizontalPixelOffset")) { 1004 // } else if (childName.equals("VerticalPixelOffset")) { 1005 } 1006 child = child.getNextSibling(); 1007 } 1008 1009 // Use PhysicalPixelSpacing if PixelSize not available. 1010 if(!(gotWidth || gotHeight) && (gotSpaceX || gotSpaceY)) { 1011 width = spaceX; 1012 gotWidth = gotSpaceX; 1013 height = spaceY; 1014 gotHeight = gotSpaceY; 1015 } 1016 1017 // Round floating point values to obtain int resolution. 1018 if (gotWidth && gotHeight) { 1019 xPixelsPerMeter = (int)(1000.0/width + 0.5); 1020 yPixelsPerMeter = (int)(1000.0/height + 0.5); 1021 } else if (gotAspectRatio && aspectRatio != 0.0) { 1022 if(gotWidth) { 1023 xPixelsPerMeter = (int)(1000.0/width + 0.5); 1024 yPixelsPerMeter = 1025 (int)(aspectRatio*(1000.0/width) + 0.5); 1026 } else if(gotHeight) { 1027 xPixelsPerMeter = 1028 (int)(1000.0/height/aspectRatio + 0.5); 1029 yPixelsPerMeter = (int)(1000.0/height + 0.5); 1030 } 1031 } 1032 } else if (name.equals("Document")) { 1033 Node child = node.getFirstChild(); 1034 while(child != null) { 1035 String childName = child.getNodeName(); 1036 if (childName.equals("FormatVersion")) { 1037 bmpVersion = getAttribute(child, "value"); 1038 break; 1039 } 1040 child = child.getNextSibling(); 1041 } 1042 } else if (name.equals("Text")) { 1043 Node child = node.getFirstChild(); 1044 while(child != null) { 1045 String childName = child.getNodeName(); 1046 if (childName.equals("TextEntry")) { 1047 if(comments == null) { 1048 comments = new ArrayList(); 1049 } 1050 comments.add(getAttribute(child, "value")); 1051 } 1052 child = child.getNextSibling(); 1053 } 1054 } else if (name.equals("Transparency")) { 1055 Node child = node.getFirstChild(); 1056 while(child != null) { 1057 String childName = child.getNodeName(); 1058 if (childName.equals("Alpha")) { 1059 hasAlpha = 1060 !getAttribute(child, "value").equals("none"); 1061 break; 1062 } 1063 child = child.getNextSibling(); 1064 } 1065 } else { 1066 // XXX Ignore it. 1067 } 1068 1069 node = node.getNextSibling(); 1070 } 1071 1072 // Set bitsPerPixel. 1073 if(bitsPerSample != null) { 1074 if(palette != null && paletteSize > 0) { 1075 bitsPerPixel = (short)bitsPerSample[0]; 1076 } else { 1077 bitsPerPixel = 0; 1078 for(int i = 0; i < bitsPerSample.length; i++) { 1079 bitsPerPixel += bitsPerSample[i]; 1080 } 1081 } 1082 } else if(palette != null) { 1083 bitsPerPixel = 8; 1084 } else if(numChannels == 1) { 1085 bitsPerPixel = 8; 1086 } else if(numChannels == 3) { 1087 bitsPerPixel = 24; 1088 } else if(numChannels == 4) { 1089 bitsPerPixel = 32; 1090 } else if(colorSpaceType.equals("GRAY")) { 1091 bitsPerPixel = 8; 1092 } else if(colorSpaceType.equals("RGB")) { 1093 bitsPerPixel = (short)(hasAlpha ? 32 : 24); 1094 } 1095 1096 // Set RGB masks. 1097 if((bitsPerSample != null && bitsPerSample.length == 4) || 1098 bitsPerPixel >= 24) { 1099 redMask = 0x00ff0000; 1100 greenMask = 0x0000ff00; 1101 blueMask = 0x000000ff; 1102 } 1103 1104 // Set alpha mask. 1105 if((bitsPerSample != null && bitsPerSample.length == 4) || 1106 bitsPerPixel > 24) { 1107 alphaMask = 0xff000000; 1108 } 1109 1110 // Set palette 1111 if(r != null && g != null && b != null) { 1112 boolean isVersion2 = 1113 bmpVersion != null && bmpVersion.equals(VERSION_2); 1114 1115 paletteSize = maxIndex + 1; 1116 palette = new byte[(isVersion2 ? 3 : 4)*paletteSize]; 1117 for(int i = 0, j = 0; i < paletteSize; i++) { 1118 palette[j++] = b[i]; 1119 palette[j++] = g[i]; 1120 palette[j++] = r[i]; 1121 if(!isVersion2) j++; // skip reserved entry 1122 } 1123 } 1124 } 1125 1126 public void reset() { 1127 // Fields for Image Descriptor 1128 bmpVersion = null; 1129 width = 0; 1130 height = 0; 1131 bitsPerPixel = 0; 1132 compression = 0; 1133 imageSize = 0; 1134 1135 // Fields for PixelsPerMeter 1136 xPixelsPerMeter = 0; 1137 yPixelsPerMeter = 0; 1138 1139 colorsUsed = 0; 1140 colorsImportant = 0; 1141 1142 // Fields for BI_BITFIELDS compression(Mask) 1143 redMask = 0; 1144 greenMask = 0; 1145 blueMask = 0; 1146 alphaMask = 0; 1147 1148 colorSpace = 0; 1149 1150 // Fields for CIE XYZ for the LCS_CALIBRATED_RGB color space 1151 redX = 0; 1152 redY = 0; 1153 redZ = 0; 1154 greenX = 0; 1155 greenY = 0; 1156 greenZ = 0; 1157 blueX = 0; 1158 blueY = 0; 1159 blueZ = 0; 1160 1161 // Fields for Gamma values for the LCS_CALIBRATED_RGB color space 1162 gammaRed = 0; 1163 gammaGreen = 0; 1164 gammaBlue = 0; 1165 1166 intent = 0; 1167 1168 // Fields for the Palette and Entries 1169 palette = null; 1170 paletteSize = 0; 1171 red = 0; 1172 green = 0; 1173 blue = 0; 1174 1175 // Fields from CommentExtension 1176 comments = null; 1177 } 1178 1179 private String countBits(int num) { 1180 int count = 0; 1181 while(num != 0) { 1182 if ((num & 1) == 1) 1183 count++; 1184 num >>>= 1; 1185 } 1186 1187 return count == 0 ? "0" : "" + count; 1188 } 1189 1190 private void addXYZPoints(IIOMetadataNode root, String name, double x, double y, double z) { 1191 IIOMetadataNode node = addChildNode(root, name, null); 1192 addChildNode(node, "X", new Double(x)); 1193 addChildNode(node, "Y", new Double(y)); 1194 addChildNode(node, "Z", new Double(z)); 1195 } 1196 1197 private IIOMetadataNode addChildNode(IIOMetadataNode root, 1198 String name, 1199 Object object) { 1200 IIOMetadataNode child = new IIOMetadataNode(name); 1201 if (object != null) { 1202 child.setUserObject(object); 1203 child.setNodeValue(ImageUtil.convertObjectToString(object)); 1204 } 1205 root.appendChild(child); 1206 return child; 1207 } 1208 1209 private Object getObjectValue(Node node) { 1210 Object tmp = node.getNodeValue(); 1211 1212 if(tmp == null && node instanceof IIOMetadataNode) { 1213 tmp = ((IIOMetadataNode)node).getUserObject(); 1214 } 1215 1216 return tmp; 1217 } 1218 1219 private String getStringValue(Node node) { 1220 Object tmp = getObjectValue(node); 1221 return tmp instanceof String ? (String)tmp : null; 1222 } 1223 1224 private Byte getByteValue(Node node) { 1225 Object tmp = getObjectValue(node); 1226 Byte value = null; 1227 if(tmp instanceof String) { 1228 value = Byte.valueOf((String)tmp); 1229 } else if(tmp instanceof Byte) { 1230 value = (Byte)tmp; 1231 } 1232 return value; 1233 } 1234 1235 private Short getShortValue(Node node) { 1236 Object tmp = getObjectValue(node); 1237 Short value = null; 1238 if(tmp instanceof String) { 1239 value = Short.valueOf((String)tmp); 1240 } else if(tmp instanceof Short) { 1241 value = (Short)tmp; 1242 } 1243 return value; 1244 } 1245 1246 private Integer getIntegerValue(Node node) { 1247 Object tmp = getObjectValue(node); 1248 Integer value = null; 1249 if(tmp instanceof String) { 1250 value = Integer.valueOf((String)tmp); 1251 } else if(tmp instanceof Integer) { 1252 value = (Integer)tmp; 1253 } else if(tmp instanceof Byte) { 1254 value = new Integer(((Byte)tmp).byteValue() & 0xff); 1255 } 1256 return value; 1257 } 1258 1259 private Double getDoubleValue(Node node) { 1260 Object tmp = getObjectValue(node); 1261 Double value = null; 1262 if(tmp instanceof String) { 1263 value = Double.valueOf((String)tmp); 1264 } else if(tmp instanceof Double) { 1265 value = (Double)tmp; 1266 } 1267 return value; 1268 } 1269}