1 /*
2  * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package com.sun.imageio.plugins.gif;
27 
28 import javax.imageio.metadata.IIOInvalidTreeException;
29 import javax.imageio.metadata.IIOMetadataNode;
30 import javax.imageio.metadata.IIOMetadataFormatImpl;
31 import org.w3c.dom.Node;
32 
33 // TODO - document elimination of globalColorTableFlag
34 
35 public class GIFStreamMetadata extends GIFMetadata {
36 
37     // package scope
38     static final String
39         nativeMetadataFormatName = "javax_imageio_gif_stream_1.0";
40 
41     static final String[] versionStrings = { "87a", "89a" };
42 
43     public String version; // 87a or 89a
44     public int logicalScreenWidth;
45     public int logicalScreenHeight;
46     public int colorResolution; // 1 to 8
47     public int pixelAspectRatio;
48 
49     public int backgroundColorIndex; // Valid if globalColorTable != null
50     public boolean sortFlag; // Valid if globalColorTable != null
51 
52     static final String[] colorTableSizes = {
53         "2", "4", "8", "16", "32", "64", "128", "256"
54     };
55 
56     // Set global color table flag in header to 0 if null, 1 otherwise
57     public byte[] globalColorTable = null;
58 
GIFStreamMetadata(boolean standardMetadataFormatSupported, String nativeMetadataFormatName, String nativeMetadataFormatClassName, String[] extraMetadataFormatNames, String[] extraMetadataFormatClassNames)59     protected GIFStreamMetadata(boolean standardMetadataFormatSupported,
60                                 String nativeMetadataFormatName,
61                                 String nativeMetadataFormatClassName,
62                                 String[] extraMetadataFormatNames,
63                                 String[] extraMetadataFormatClassNames)
64     {
65         super(standardMetadataFormatSupported,
66               nativeMetadataFormatName,
67               nativeMetadataFormatClassName,
68               extraMetadataFormatNames,
69               extraMetadataFormatClassNames);
70     }
71 
GIFStreamMetadata()72     public GIFStreamMetadata() {
73         this(true,
74               nativeMetadataFormatName,
75               "com.sun.imageio.plugins.gif.GIFStreamMetadataFormat",
76               null, null);
77 
78     }
79 
isReadOnly()80     public boolean isReadOnly() {
81         return true;
82     }
83 
getAsTree(String formatName)84     public Node getAsTree(String formatName) {
85         if (formatName.equals(nativeMetadataFormatName)) {
86             return getNativeTree();
87         } else if (formatName.equals
88                    (IIOMetadataFormatImpl.standardMetadataFormatName)) {
89             return getStandardTree();
90         } else {
91             throw new IllegalArgumentException("Not a recognized format!");
92         }
93     }
94 
getNativeTree()95     private Node getNativeTree() {
96         IIOMetadataNode node; // scratch node
97         IIOMetadataNode root =
98             new IIOMetadataNode(nativeMetadataFormatName);
99 
100         node = new IIOMetadataNode("Version");
101         node.setAttribute("value", version);
102         root.appendChild(node);
103 
104         // Image descriptor
105         node = new IIOMetadataNode("LogicalScreenDescriptor");
106         /* NB: At the moment we use empty strings to support undefined
107          * integer values in tree representation.
108          * We need to add better support for undefined/default values later.
109          */
110         node.setAttribute("logicalScreenWidth",
111                           logicalScreenWidth == UNDEFINED_INTEGER_VALUE ?
112                           "" : Integer.toString(logicalScreenWidth));
113         node.setAttribute("logicalScreenHeight",
114                           logicalScreenHeight == UNDEFINED_INTEGER_VALUE ?
115                           "" : Integer.toString(logicalScreenHeight));
116         // Stored value plus one
117         node.setAttribute("colorResolution",
118                           colorResolution == UNDEFINED_INTEGER_VALUE ?
119                           "" : Integer.toString(colorResolution));
120         node.setAttribute("pixelAspectRatio",
121                           Integer.toString(pixelAspectRatio));
122         root.appendChild(node);
123 
124         if (globalColorTable != null) {
125             node = new IIOMetadataNode("GlobalColorTable");
126             int numEntries = globalColorTable.length/3;
127             node.setAttribute("sizeOfGlobalColorTable",
128                               Integer.toString(numEntries));
129             node.setAttribute("backgroundColorIndex",
130                               Integer.toString(backgroundColorIndex));
131             node.setAttribute("sortFlag",
132                               sortFlag ? "TRUE" : "FALSE");
133 
134             for (int i = 0; i < numEntries; i++) {
135                 IIOMetadataNode entry =
136                     new IIOMetadataNode("ColorTableEntry");
137                 entry.setAttribute("index", Integer.toString(i));
138                 int r = globalColorTable[3*i] & 0xff;
139                 int g = globalColorTable[3*i + 1] & 0xff;
140                 int b = globalColorTable[3*i + 2] & 0xff;
141                 entry.setAttribute("red", Integer.toString(r));
142                 entry.setAttribute("green", Integer.toString(g));
143                 entry.setAttribute("blue", Integer.toString(b));
144                 node.appendChild(entry);
145             }
146             root.appendChild(node);
147         }
148 
149         return root;
150     }
151 
getStandardChromaNode()152     public IIOMetadataNode getStandardChromaNode() {
153         IIOMetadataNode chroma_node = new IIOMetadataNode("Chroma");
154         IIOMetadataNode node = null; // scratch node
155 
156         node = new IIOMetadataNode("ColorSpaceType");
157         node.setAttribute("name", "RGB");
158         chroma_node.appendChild(node);
159 
160         node = new IIOMetadataNode("BlackIsZero");
161         node.setAttribute("value", "TRUE");
162         chroma_node.appendChild(node);
163 
164         // NumChannels not in stream
165         // Gamma not in format
166 
167         if (globalColorTable != null) {
168             node = new IIOMetadataNode("Palette");
169             int numEntries = globalColorTable.length/3;
170             for (int i = 0; i < numEntries; i++) {
171                 IIOMetadataNode entry =
172                     new IIOMetadataNode("PaletteEntry");
173                 entry.setAttribute("index", Integer.toString(i));
174                 entry.setAttribute("red",
175                            Integer.toString(globalColorTable[3*i] & 0xff));
176                 entry.setAttribute("green",
177                            Integer.toString(globalColorTable[3*i + 1] & 0xff));
178                 entry.setAttribute("blue",
179                            Integer.toString(globalColorTable[3*i + 2] & 0xff));
180                 node.appendChild(entry);
181             }
182             chroma_node.appendChild(node);
183 
184             // backgroundColorIndex is valid iff there is a color table
185             node = new IIOMetadataNode("BackgroundIndex");
186             node.setAttribute("value", Integer.toString(backgroundColorIndex));
187             chroma_node.appendChild(node);
188         }
189 
190         return chroma_node;
191     }
192 
getStandardCompressionNode()193     public IIOMetadataNode getStandardCompressionNode() {
194         IIOMetadataNode compression_node = new IIOMetadataNode("Compression");
195         IIOMetadataNode node = null; // scratch node
196 
197         node = new IIOMetadataNode("CompressionTypeName");
198         node.setAttribute("value", "lzw");
199         compression_node.appendChild(node);
200 
201         node = new IIOMetadataNode("Lossless");
202         node.setAttribute("value", "TRUE");
203         compression_node.appendChild(node);
204 
205         // NumProgressiveScans not in stream
206         // BitRate not in format
207 
208         return compression_node;
209     }
210 
getStandardDataNode()211     public IIOMetadataNode getStandardDataNode() {
212         IIOMetadataNode data_node = new IIOMetadataNode("Data");
213         IIOMetadataNode node = null; // scratch node
214 
215         // PlanarConfiguration
216 
217         node = new IIOMetadataNode("SampleFormat");
218         node.setAttribute("value", "Index");
219         data_node.appendChild(node);
220 
221         node = new IIOMetadataNode("BitsPerSample");
222         node.setAttribute("value",
223                           colorResolution == UNDEFINED_INTEGER_VALUE ?
224                           "" : Integer.toString(colorResolution));
225         data_node.appendChild(node);
226 
227         // SignificantBitsPerSample
228         // SampleMSB
229 
230         return data_node;
231     }
232 
getStandardDimensionNode()233     public IIOMetadataNode getStandardDimensionNode() {
234         IIOMetadataNode dimension_node = new IIOMetadataNode("Dimension");
235         IIOMetadataNode node = null; // scratch node
236 
237         node = new IIOMetadataNode("PixelAspectRatio");
238         float aspectRatio = 1.0F;
239         if (pixelAspectRatio != 0) {
240             aspectRatio = (pixelAspectRatio + 15)/64.0F;
241         }
242         node.setAttribute("value", Float.toString(aspectRatio));
243         dimension_node.appendChild(node);
244 
245         node = new IIOMetadataNode("ImageOrientation");
246         node.setAttribute("value", "Normal");
247         dimension_node.appendChild(node);
248 
249         // HorizontalPixelSize not in format
250         // VerticalPixelSize not in format
251         // HorizontalPhysicalPixelSpacing not in format
252         // VerticalPhysicalPixelSpacing not in format
253         // HorizontalPosition not in format
254         // VerticalPosition not in format
255         // HorizontalPixelOffset not in stream
256         // VerticalPixelOffset not in stream
257 
258         node = new IIOMetadataNode("HorizontalScreenSize");
259         node.setAttribute("value",
260                           logicalScreenWidth == UNDEFINED_INTEGER_VALUE ?
261                           "" : Integer.toString(logicalScreenWidth));
262         dimension_node.appendChild(node);
263 
264         node = new IIOMetadataNode("VerticalScreenSize");
265         node.setAttribute("value",
266                           logicalScreenHeight == UNDEFINED_INTEGER_VALUE ?
267                           "" : Integer.toString(logicalScreenHeight));
268         dimension_node.appendChild(node);
269 
270         return dimension_node;
271     }
272 
getStandardDocumentNode()273     public IIOMetadataNode getStandardDocumentNode() {
274         IIOMetadataNode document_node = new IIOMetadataNode("Document");
275         IIOMetadataNode node = null; // scratch node
276 
277         node = new IIOMetadataNode("FormatVersion");
278         node.setAttribute("value", version);
279         document_node.appendChild(node);
280 
281         // SubimageInterpretation not in format
282         // ImageCreationTime not in format
283         // ImageModificationTime not in format
284 
285         return document_node;
286     }
287 
getStandardTextNode()288     public IIOMetadataNode getStandardTextNode() {
289         // Not in stream
290         return null;
291     }
292 
getStandardTransparencyNode()293     public IIOMetadataNode getStandardTransparencyNode() {
294         // Not in stream
295         return null;
296     }
297 
setFromTree(String formatName, Node root)298     public void setFromTree(String formatName, Node root)
299         throws IIOInvalidTreeException
300     {
301         throw new IllegalStateException("Metadata is read-only!");
302     }
303 
mergeNativeTree(Node root)304     protected void mergeNativeTree(Node root) throws IIOInvalidTreeException
305     {
306         throw new IllegalStateException("Metadata is read-only!");
307     }
308 
mergeStandardTree(Node root)309     protected void mergeStandardTree(Node root) throws IIOInvalidTreeException
310     {
311         throw new IllegalStateException("Metadata is read-only!");
312     }
313 
reset()314     public void reset() {
315         throw new IllegalStateException("Metadata is read-only!");
316     }
317 }
318