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