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