001/* 002 * $RCSfile: GIFImageMetadata.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.1 $ 042 * $Date: 2006/03/24 22:30:09 $ 043 * $State: Exp $ 044 */ 045 046package com.github.jaiimageio.impl.plugins.gif; 047 048import java.io.UnsupportedEncodingException; 049import java.util.ArrayList; 050import java.util.Iterator; 051import java.util.List; 052import javax.imageio.ImageTypeSpecifier; 053import javax.imageio.metadata.IIOInvalidTreeException; 054import javax.imageio.metadata.IIOMetadata; 055import javax.imageio.metadata.IIOMetadataNode; 056import javax.imageio.metadata.IIOMetadataFormat; 057import javax.imageio.metadata.IIOMetadataFormatImpl; 058import org.w3c.dom.Node; 059 060/** 061 * @version 0.5 062 */ 063public class GIFImageMetadata extends GIFMetadata { 064 065 // package scope 066 static final String 067 nativeMetadataFormatName = "javax_imageio_gif_image_1.0"; 068 069 static final String[] disposalMethodNames = { 070 "none", 071 "doNotDispose", 072 "restoreToBackgroundColor", 073 "restoreToPrevious", 074 "undefinedDisposalMethod4", 075 "undefinedDisposalMethod5", 076 "undefinedDisposalMethod6", 077 "undefinedDisposalMethod7" 078 }; 079 080 // Fields from Image Descriptor 081 public int imageLeftPosition; 082 public int imageTopPosition; 083 public int imageWidth; 084 public int imageHeight; 085 public boolean interlaceFlag = false; 086 public boolean sortFlag = false; 087 public byte[] localColorTable = null; 088 089 // Fields from Graphic Control Extension 090 public int disposalMethod = 0; 091 public boolean userInputFlag = false; 092 public boolean transparentColorFlag = false; 093 public int delayTime = 0; 094 public int transparentColorIndex = 0; 095 096 // Fields from Plain Text Extension 097 public boolean hasPlainTextExtension = false; 098 public int textGridLeft; 099 public int textGridTop; 100 public int textGridWidth; 101 public int textGridHeight; 102 public int characterCellWidth; 103 public int characterCellHeight; 104 public int textForegroundColor; 105 public int textBackgroundColor; 106 public byte[] text; 107 108 // Fields from ApplicationExtension 109 // List of byte[] 110 public List applicationIDs = null; // new ArrayList(); 111 112 // List of byte[] 113 public List authenticationCodes = null; // new ArrayList(); 114 115 // List of byte[] 116 public List applicationData = null; // new ArrayList(); 117 118 // Fields from CommentExtension 119 // List of byte[] 120 public List comments = null; // new ArrayList(); 121 122 protected GIFImageMetadata(boolean standardMetadataFormatSupported, 123 String nativeMetadataFormatName, 124 String nativeMetadataFormatClassName, 125 String[] extraMetadataFormatNames, 126 String[] extraMetadataFormatClassNames) 127 { 128 super(standardMetadataFormatSupported, 129 nativeMetadataFormatName, 130 nativeMetadataFormatClassName, 131 extraMetadataFormatNames, 132 extraMetadataFormatClassNames); 133 } 134 135 public GIFImageMetadata() { 136 this(true, 137 nativeMetadataFormatName, 138 "com.github.jaiimageio.impl.plugins.gif.GIFImageMetadataFormat", 139 null, null); 140 } 141 142 public boolean isReadOnly() { 143 return true; 144 } 145 146 public Node getAsTree(String formatName) { 147 if (formatName.equals(nativeMetadataFormatName)) { 148 return getNativeTree(); 149 } else if (formatName.equals 150 (IIOMetadataFormatImpl.standardMetadataFormatName)) { 151 return getStandardTree(); 152 } else { 153 throw new IllegalArgumentException("Not a recognized format!"); 154 } 155 } 156 157 private String toISO8859(byte[] data) { 158 try { 159 return new String(data, "ISO-8859-1"); 160 } catch (UnsupportedEncodingException e) { 161 return ""; 162 } 163 } 164 165 private Node getNativeTree() { 166 IIOMetadataNode node; // scratch node 167 IIOMetadataNode root = 168 new IIOMetadataNode(nativeMetadataFormatName); 169 170 // Image descriptor 171 node = new IIOMetadataNode("ImageDescriptor"); 172 node.setAttribute("imageLeftPosition", 173 Integer.toString(imageLeftPosition)); 174 node.setAttribute("imageTopPosition", 175 Integer.toString(imageTopPosition)); 176 node.setAttribute("imageWidth", Integer.toString(imageWidth)); 177 node.setAttribute("imageHeight", Integer.toString(imageHeight)); 178 node.setAttribute("interlaceFlag", 179 interlaceFlag ? "true" : "false"); 180 root.appendChild(node); 181 182 // Local color table 183 if (localColorTable != null) { 184 node = new IIOMetadataNode("LocalColorTable"); 185 int numEntries = localColorTable.length/3; 186 node.setAttribute("sizeOfLocalColorTable", 187 Integer.toString(numEntries)); 188 node.setAttribute("sortFlag", 189 sortFlag ? "TRUE" : "FALSE"); 190 191 for (int i = 0; i < numEntries; i++) { 192 IIOMetadataNode entry = 193 new IIOMetadataNode("ColorTableEntry"); 194 entry.setAttribute("index", Integer.toString(i)); 195 int r = localColorTable[3*i] & 0xff; 196 int g = localColorTable[3*i + 1] & 0xff; 197 int b = localColorTable[3*i + 2] & 0xff; 198 entry.setAttribute("red", Integer.toString(r)); 199 entry.setAttribute("green", Integer.toString(g)); 200 entry.setAttribute("blue", Integer.toString(b)); 201 node.appendChild(entry); 202 } 203 root.appendChild(node); 204 } 205 206 // Graphic control extension 207 node = new IIOMetadataNode("GraphicControlExtension"); 208 node.setAttribute("disposalMethod", 209 disposalMethodNames[disposalMethod]); 210 node.setAttribute("userInputFlag", 211 userInputFlag ? "true" : "false"); 212 node.setAttribute("transparentColorFlag", 213 transparentColorFlag ? "true" : "false"); 214 node.setAttribute("delayTime", 215 Integer.toString(delayTime)); 216 node.setAttribute("transparentColorIndex", 217 Integer.toString(transparentColorIndex)); 218 root.appendChild(node); 219 220 if (hasPlainTextExtension) { 221 node = new IIOMetadataNode("PlainTextExtension"); 222 node.setAttribute("textGridLeft", 223 Integer.toString(textGridLeft)); 224 node.setAttribute("textGridTop", 225 Integer.toString(textGridTop)); 226 node.setAttribute("textGridWidth", 227 Integer.toString(textGridWidth)); 228 node.setAttribute("textGridHeight", 229 Integer.toString(textGridHeight)); 230 node.setAttribute("characterCellWidth", 231 Integer.toString(characterCellWidth)); 232 node.setAttribute("characterCellHeight", 233 Integer.toString(characterCellHeight)); 234 node.setAttribute("textForegroundColor", 235 Integer.toString(textForegroundColor)); 236 node.setAttribute("textBackgroundColor", 237 Integer.toString(textBackgroundColor)); 238 node.setAttribute("text", toISO8859(text)); 239 240 root.appendChild(node); 241 } 242 243 // Application extensions 244 int numAppExtensions = applicationIDs == null ? 245 0 : applicationIDs.size(); 246 if (numAppExtensions > 0) { 247 node = new IIOMetadataNode("ApplicationExtensions"); 248 for (int i = 0; i < numAppExtensions; i++) { 249 IIOMetadataNode appExtNode = 250 new IIOMetadataNode("ApplicationExtension"); 251 byte[] applicationID = (byte[])applicationIDs.get(i); 252 appExtNode.setAttribute("applicationID", 253 toISO8859(applicationID)); 254 byte[] authenticationCode = (byte[])authenticationCodes.get(i); 255 appExtNode.setAttribute("authenticationCode", 256 toISO8859(authenticationCode)); 257 byte[] appData = (byte[])applicationData.get(i); 258 appExtNode.setUserObject((byte[])appData.clone()); 259 node.appendChild(appExtNode); 260 } 261 262 root.appendChild(node); 263 } 264 265 // Comment extensions 266 int numComments = comments == null ? 0 : comments.size(); 267 if (numComments > 0) { 268 node = new IIOMetadataNode("CommentExtensions"); 269 for (int i = 0; i < numComments; i++) { 270 IIOMetadataNode commentNode = 271 new IIOMetadataNode("CommentExtension"); 272 byte[] comment = (byte[])comments.get(i); 273 commentNode.setAttribute("value", toISO8859(comment)); 274 node.appendChild(commentNode); 275 } 276 277 root.appendChild(node); 278 } 279 280 return root; 281 } 282 283 public IIOMetadataNode getStandardChromaNode() { 284 IIOMetadataNode chroma_node = new IIOMetadataNode("Chroma"); 285 IIOMetadataNode node = null; // scratch node 286 287 node = new IIOMetadataNode("ColorSpaceType"); 288 node.setAttribute("name", "RGB"); 289 chroma_node.appendChild(node); 290 291 node = new IIOMetadataNode("NumChannels"); 292 node.setAttribute("value", transparentColorFlag ? "4" : "3"); 293 chroma_node.appendChild(node); 294 295 // Gamma not in format 296 297 node = new IIOMetadataNode("BlackIsZero"); 298 node.setAttribute("value", "TRUE"); 299 chroma_node.appendChild(node); 300 301 if (localColorTable != null) { 302 node = new IIOMetadataNode("Palette"); 303 int numEntries = localColorTable.length/3; 304 for (int i = 0; i < numEntries; i++) { 305 IIOMetadataNode entry = 306 new IIOMetadataNode("PaletteEntry"); 307 entry.setAttribute("index", Integer.toString(i)); 308 entry.setAttribute("red", 309 Integer.toString(localColorTable[3*i] & 0xff)); 310 entry.setAttribute("green", 311 Integer.toString(localColorTable[3*i + 1] & 0xff)); 312 entry.setAttribute("blue", 313 Integer.toString(localColorTable[3*i + 2] & 0xff)); 314 node.appendChild(entry); 315 } 316 chroma_node.appendChild(node); 317 } 318 319 // BackgroundIndex not in image 320 // BackgroundColor not in format 321 322 return chroma_node; 323 } 324 325 public IIOMetadataNode getStandardCompressionNode() { 326 IIOMetadataNode compression_node = new IIOMetadataNode("Compression"); 327 IIOMetadataNode node = null; // scratch node 328 329 node = new IIOMetadataNode("CompressionTypeName"); 330 node.setAttribute("value", "lzw"); 331 compression_node.appendChild(node); 332 333 node = new IIOMetadataNode("Lossless"); 334 node.setAttribute("value", "TRUE"); 335 compression_node.appendChild(node); 336 337 node = new IIOMetadataNode("NumProgressiveScans"); 338 node.setAttribute("value", interlaceFlag ? "4" : "1"); 339 compression_node.appendChild(node); 340 341 // BitRate not in format 342 343 return compression_node; 344 } 345 346 public IIOMetadataNode getStandardDataNode() { 347 IIOMetadataNode data_node = new IIOMetadataNode("Data"); 348 IIOMetadataNode node = null; // scratch node 349 350 // PlanarConfiguration not in format 351 352 node = new IIOMetadataNode("SampleFormat"); 353 node.setAttribute("value", "Index"); 354 data_node.appendChild(node); 355 356 // BitsPerSample not in image 357 // SignificantBitsPerSample not in format 358 // SampleMSB not in format 359 360 return data_node; 361 } 362 363 public IIOMetadataNode getStandardDimensionNode() { 364 IIOMetadataNode dimension_node = new IIOMetadataNode("Dimension"); 365 IIOMetadataNode node = null; // scratch node 366 367 // PixelAspectRatio not in image 368 369 node = new IIOMetadataNode("ImageOrientation"); 370 node.setAttribute("value", "Normal"); 371 dimension_node.appendChild(node); 372 373 // HorizontalPixelSize not in format 374 // VerticalPixelSize not in format 375 // HorizontalPhysicalPixelSpacing not in format 376 // VerticalPhysicalPixelSpacing not in format 377 // HorizontalPosition not in format 378 // VerticalPosition not in format 379 380 node = new IIOMetadataNode("HorizontalPixelOffset"); 381 node.setAttribute("value", Integer.toString(imageLeftPosition)); 382 dimension_node.appendChild(node); 383 384 node = new IIOMetadataNode("VerticalPixelOffset"); 385 node.setAttribute("value", Integer.toString(imageTopPosition)); 386 dimension_node.appendChild(node); 387 388 // HorizontalScreenSize not in image 389 // VerticalScreenSize not in image 390 391 return dimension_node; 392 } 393 394 // Document not in image 395 396 public IIOMetadataNode getStandardTextNode() { 397 if (comments == null) { 398 return null; 399 } 400 Iterator commentIter = comments.iterator(); 401 if (!commentIter.hasNext()) { 402 return null; 403 } 404 405 IIOMetadataNode text_node = new IIOMetadataNode("Text"); 406 IIOMetadataNode node = null; // scratch node 407 408 while (commentIter.hasNext()) { 409 byte[] comment = (byte[])commentIter.next(); 410 String s = null; 411 try { 412 s = new String(comment, "ISO-8859-1"); 413 } catch (UnsupportedEncodingException e) { 414 throw new RuntimeException("Encoding ISO-8859-1 unknown!"); 415 } 416 417 node = new IIOMetadataNode("TextEntry"); 418 node.setAttribute("value", s); 419 node.setAttribute("encoding", "ISO-8859-1"); 420 node.setAttribute("compression", "none"); 421 text_node.appendChild(node); 422 } 423 424 return text_node; 425 } 426 427 public IIOMetadataNode getStandardTransparencyNode() { 428 if (!transparentColorFlag) { 429 return null; 430 } 431 432 IIOMetadataNode transparency_node = 433 new IIOMetadataNode("Transparency"); 434 IIOMetadataNode node = null; // scratch node 435 436 // Alpha not in format 437 438 node = new IIOMetadataNode("TransparentIndex"); 439 node.setAttribute("value", 440 Integer.toString(transparentColorIndex)); 441 transparency_node.appendChild(node); 442 443 // TransparentColor not in format 444 // TileTransparencies not in format 445 // TileOpacities not in format 446 447 return transparency_node; 448 } 449 450 public void setFromTree(String formatName, Node root) 451 throws IIOInvalidTreeException 452 { 453 throw new IllegalStateException("Metadata is read-only!"); 454 } 455 456 protected void mergeNativeTree(Node root) throws IIOInvalidTreeException 457 { 458 throw new IllegalStateException("Metadata is read-only!"); 459 } 460 461 protected void mergeStandardTree(Node root) throws IIOInvalidTreeException 462 { 463 throw new IllegalStateException("Metadata is read-only!"); 464 } 465 466 public void reset() { 467 throw new IllegalStateException("Metadata is read-only!"); 468 } 469}