001/*
002 * $RCSfile: GIFStreamMetadata.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:10 $
043 * $State: Exp $
044 */
045
046package com.github.jaiimageio.impl.plugins.gif;
047
048import javax.imageio.metadata.IIOInvalidTreeException;
049import javax.imageio.metadata.IIOMetadataFormatImpl;
050import javax.imageio.metadata.IIOMetadataNode;
051
052import org.w3c.dom.Node;
053
054// TODO - document elimination of globalColorTableFlag
055
056/**
057 * @version 0.5
058 */
059public class GIFStreamMetadata extends GIFMetadata {
060
061    // package scope
062    static final String
063        nativeMetadataFormatName = "javax_imageio_gif_stream_1.0";
064
065    public static final String[] versionStrings = { "87a", "89a" };
066
067    public String version; // 87a or 89a
068    public int logicalScreenWidth;
069    public int logicalScreenHeight;
070    public int colorResolution; // 1 to 8
071    public int pixelAspectRatio;
072
073    public int backgroundColorIndex; // Valid if globalColorTable != null
074    public boolean sortFlag; // Valid if globalColorTable != null
075
076    public static final String[] colorTableSizes = {
077        "2", "4", "8", "16", "32", "64", "128", "256"
078    };
079
080    // Set global color table flag in header to 0 if null, 1 otherwise
081    public byte[] globalColorTable = null;
082
083    protected GIFStreamMetadata(boolean standardMetadataFormatSupported,
084                                String nativeMetadataFormatName,
085                                String nativeMetadataFormatClassName,
086                                String[] extraMetadataFormatNames,
087                                String[] extraMetadataFormatClassNames)
088    {
089        super(standardMetadataFormatSupported,
090              nativeMetadataFormatName,
091              nativeMetadataFormatClassName,
092              extraMetadataFormatNames,
093              extraMetadataFormatClassNames);
094    }
095    
096    public GIFStreamMetadata() {
097        this(true, 
098              nativeMetadataFormatName,
099              "com.github.jaiimageio.impl.plugins.gif.GIFStreamMetadataFormat",
100              null, null);
101
102    }
103
104    public boolean isReadOnly() {
105        return true;
106    }
107    
108    public Node getAsTree(String formatName) {
109        if (formatName.equals(nativeMetadataFormatName)) {
110            return getNativeTree();
111        } else if (formatName.equals
112                   (IIOMetadataFormatImpl.standardMetadataFormatName)) {
113            return getStandardTree();
114        } else {
115            throw new IllegalArgumentException("Not a recognized format!");
116        }
117    }
118
119    private Node getNativeTree() {
120        IIOMetadataNode node; // scratch node
121        IIOMetadataNode root =
122            new IIOMetadataNode(nativeMetadataFormatName);
123            
124        node = new IIOMetadataNode("Version");
125        node.setAttribute("value", version);
126        root.appendChild(node);
127        
128        // Image descriptor
129        node = new IIOMetadataNode("LogicalScreenDescriptor");
130        /* NB: At the moment we use empty strings to support undefined 
131         * integer values in tree representation. 
132         * We need to add better support for undefined/default values later.
133         */  
134        node.setAttribute("logicalScreenWidth",
135                          logicalScreenWidth == UNDEFINED_INTEGER_VALUE ?
136                          "" : Integer.toString(logicalScreenWidth));
137        node.setAttribute("logicalScreenHeight",
138                          logicalScreenHeight == UNDEFINED_INTEGER_VALUE ?
139                          "" : Integer.toString(logicalScreenHeight));
140        // Stored value plus one
141        node.setAttribute("colorResolution",
142                          colorResolution == UNDEFINED_INTEGER_VALUE ?
143                          "" : Integer.toString(colorResolution));
144        node.setAttribute("pixelAspectRatio",
145                          Integer.toString(pixelAspectRatio));
146        root.appendChild(node);
147
148        if (globalColorTable != null) {
149            node = new IIOMetadataNode("GlobalColorTable");
150            int numEntries = globalColorTable.length/3;
151            node.setAttribute("sizeOfGlobalColorTable",
152                              Integer.toString(numEntries));
153            node.setAttribute("backgroundColorIndex",
154                              Integer.toString(backgroundColorIndex));
155            node.setAttribute("sortFlag",
156                              sortFlag ? "TRUE" : "FALSE");
157
158            for (int i = 0; i < numEntries; i++) {
159                IIOMetadataNode entry =
160                    new IIOMetadataNode("ColorTableEntry");
161                entry.setAttribute("index", Integer.toString(i));
162                int r = globalColorTable[3*i] & 0xff;
163                int g = globalColorTable[3*i + 1] & 0xff;
164                int b = globalColorTable[3*i + 2] & 0xff;
165                entry.setAttribute("red", Integer.toString(r));
166                entry.setAttribute("green", Integer.toString(g));
167                entry.setAttribute("blue", Integer.toString(b));
168                node.appendChild(entry);
169            }
170            root.appendChild(node);
171        }
172
173        return root;
174    }
175
176    public IIOMetadataNode getStandardChromaNode() {
177        IIOMetadataNode chroma_node = new IIOMetadataNode("Chroma");
178        IIOMetadataNode node = null; // scratch node
179
180        node = new IIOMetadataNode("ColorSpaceType");
181        node.setAttribute("name", "RGB");
182        chroma_node.appendChild(node);
183
184        node = new IIOMetadataNode("BlackIsZero");
185        node.setAttribute("value", "TRUE");
186        chroma_node.appendChild(node);
187
188        // NumChannels not in stream
189        // Gamma not in format
190
191        if (globalColorTable != null) {
192            node = new IIOMetadataNode("Palette");
193            int numEntries = globalColorTable.length/3;
194            for (int i = 0; i < numEntries; i++) {
195                IIOMetadataNode entry =
196                    new IIOMetadataNode("PaletteEntry");
197                entry.setAttribute("index", Integer.toString(i));
198                entry.setAttribute("red",
199                           Integer.toString(globalColorTable[3*i] & 0xff));
200                entry.setAttribute("green",
201                           Integer.toString(globalColorTable[3*i + 1] & 0xff));
202                entry.setAttribute("blue",
203                           Integer.toString(globalColorTable[3*i + 2] & 0xff));
204                node.appendChild(entry);
205            }
206            chroma_node.appendChild(node);
207
208            // backgroundColorIndex is valid iff there is a color table
209            node = new IIOMetadataNode("BackgroundIndex");
210            node.setAttribute("value", Integer.toString(backgroundColorIndex));
211            chroma_node.appendChild(node);
212        }
213
214        return chroma_node;
215    }
216
217    public IIOMetadataNode getStandardCompressionNode() {
218        IIOMetadataNode compression_node = new IIOMetadataNode("Compression");
219        IIOMetadataNode node = null; // scratch node
220
221        node = new IIOMetadataNode("CompressionTypeName");
222        node.setAttribute("value", "lzw");
223        compression_node.appendChild(node);
224
225        node = new IIOMetadataNode("Lossless");
226        node.setAttribute("value", "true");
227        compression_node.appendChild(node);
228
229        // NumProgressiveScans not in stream
230        // BitRate not in format
231
232        return compression_node;
233    }
234
235    public IIOMetadataNode getStandardDataNode() {
236        IIOMetadataNode data_node = new IIOMetadataNode("Data");
237        IIOMetadataNode node = null; // scratch node
238
239        // PlanarConfiguration
240
241        node = new IIOMetadataNode("SampleFormat");
242        node.setAttribute("value", "Index");
243        data_node.appendChild(node);
244
245        node = new IIOMetadataNode("BitsPerSample");
246        node.setAttribute("value",
247                          colorResolution == UNDEFINED_INTEGER_VALUE ?
248                          "" : Integer.toString(colorResolution));
249        data_node.appendChild(node);
250        
251        // SignificantBitsPerSample
252        // SampleMSB
253        
254        return data_node;
255    }
256
257    public IIOMetadataNode getStandardDimensionNode() {
258        IIOMetadataNode dimension_node = new IIOMetadataNode("Dimension");
259        IIOMetadataNode node = null; // scratch node
260
261        node = new IIOMetadataNode("PixelAspectRatio");
262        float aspectRatio = 1.0F;
263        if (pixelAspectRatio != 0) {
264            aspectRatio = (pixelAspectRatio + 15)/64.0F;
265        }
266        node.setAttribute("value", Float.toString(aspectRatio));
267        dimension_node.appendChild(node);
268
269        node = new IIOMetadataNode("ImageOrientation");
270        node.setAttribute("value", "Normal");
271        dimension_node.appendChild(node);
272
273        // HorizontalPixelSize not in format
274        // VerticalPixelSize not in format
275        // HorizontalPhysicalPixelSpacing not in format
276        // VerticalPhysicalPixelSpacing not in format
277        // HorizontalPosition not in format
278        // VerticalPosition not in format
279        // HorizontalPixelOffset not in stream
280        // VerticalPixelOffset not in stream
281
282        node = new IIOMetadataNode("HorizontalScreenSize");
283        node.setAttribute("value", 
284                          logicalScreenWidth == UNDEFINED_INTEGER_VALUE ?
285                          "" : Integer.toString(logicalScreenWidth));
286        dimension_node.appendChild(node);
287        
288        node = new IIOMetadataNode("VerticalScreenSize");
289        node.setAttribute("value",
290                          logicalScreenHeight == UNDEFINED_INTEGER_VALUE ?
291                          "" : Integer.toString(logicalScreenHeight));
292        dimension_node.appendChild(node);
293        
294        return dimension_node;
295    }
296
297    public IIOMetadataNode getStandardDocumentNode() {
298        IIOMetadataNode document_node = new IIOMetadataNode("Document");
299        IIOMetadataNode node = null; // scratch node
300
301        node = new IIOMetadataNode("FormatVersion");
302        node.setAttribute("value", version);
303        document_node.appendChild(node);
304
305        // SubimageInterpretation not in format
306        // ImageCreationTime not in format
307        // ImageModificationTime not in format
308
309        return document_node;
310    }
311
312    public IIOMetadataNode getStandardTextNode() {
313        // Not in stream
314        return null;
315    }
316
317    public IIOMetadataNode getStandardTransparencyNode() {
318        // Not in stream
319        return null;
320    }
321
322    public void setFromTree(String formatName, Node root)
323        throws IIOInvalidTreeException
324    {
325        throw new IllegalStateException("Metadata is read-only!");
326    }
327
328    protected void mergeNativeTree(Node root) throws IIOInvalidTreeException
329    {
330        throw new IllegalStateException("Metadata is read-only!");
331    }
332
333    protected void mergeStandardTree(Node root) throws IIOInvalidTreeException
334    {
335        throw new IllegalStateException("Metadata is read-only!");
336    }
337
338    public void reset() {
339        throw new IllegalStateException("Metadata is read-only!");
340    }
341}