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
26package com.sun.imageio.plugins.gif;
27
28import javax.imageio.metadata.IIOInvalidTreeException;
29import javax.imageio.metadata.IIOMetadataNode;
30import javax.imageio.metadata.IIOMetadataFormatImpl;
31import org.w3c.dom.Node;
32
33// TODO - document elimination of globalColorTableFlag
34
35public 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
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
72    public GIFStreamMetadata() {
73        this(true,
74              nativeMetadataFormatName,
75              "com.sun.imageio.plugins.gif.GIFStreamMetadataFormat",
76              null, null);
77
78    }
79
80    public boolean isReadOnly() {
81        return true;
82    }
83
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
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
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
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
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
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
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
288    public IIOMetadataNode getStandardTextNode() {
289        // Not in stream
290        return null;
291    }
292
293    public IIOMetadataNode getStandardTransparencyNode() {
294        // Not in stream
295        return null;
296    }
297
298    public void setFromTree(String formatName, Node root)
299        throws IIOInvalidTreeException
300    {
301        throw new IllegalStateException("Metadata is read-only!");
302    }
303
304    protected void mergeNativeTree(Node root) throws IIOInvalidTreeException
305    {
306        throw new IllegalStateException("Metadata is read-only!");
307    }
308
309    protected void mergeStandardTree(Node root) throws IIOInvalidTreeException
310    {
311        throw new IllegalStateException("Metadata is read-only!");
312    }
313
314    public void reset() {
315        throw new IllegalStateException("Metadata is read-only!");
316    }
317}
318