1/*
2 * Copyright (c) 2001, 2014, 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.jpeg;
27
28import javax.imageio.metadata.IIOInvalidTreeException;
29import javax.imageio.metadata.IIOMetadataNode;
30import javax.imageio.stream.ImageOutputStream;
31import javax.imageio.plugins.jpeg.JPEGHuffmanTable;
32
33import java.io.IOException;
34import java.util.List;
35import java.util.ArrayList;
36import java.util.Iterator;
37
38import org.w3c.dom.Node;
39import org.w3c.dom.NodeList;
40import org.w3c.dom.NamedNodeMap;
41
42/**
43 * A DHT (Define Huffman Table) marker segment.
44 */
45class DHTMarkerSegment extends MarkerSegment {
46    List<Htable> tables = new ArrayList<>();
47
48    DHTMarkerSegment(boolean needFour) {
49        super(JPEG.DHT);
50        tables.add(new Htable(JPEGHuffmanTable.StdDCLuminance, true, 0));
51        if (needFour) {
52            tables.add(new Htable(JPEGHuffmanTable.StdDCChrominance, true, 1));
53        }
54        tables.add(new Htable(JPEGHuffmanTable.StdACLuminance, false, 0));
55        if (needFour) {
56            tables.add(new Htable(JPEGHuffmanTable.StdACChrominance, false, 1));
57        }
58    }
59
60    DHTMarkerSegment(JPEGBuffer buffer) throws IOException {
61        super(buffer);
62        int count = length;
63        while (count > 0) {
64            Htable newGuy = new Htable(buffer);
65            tables.add(newGuy);
66            count -= 1 + 16 + newGuy.values.length;
67        }
68        buffer.bufAvail -= length;
69    }
70
71    DHTMarkerSegment(JPEGHuffmanTable[] dcTables,
72                     JPEGHuffmanTable[] acTables) {
73        super(JPEG.DHT);
74        for (int i = 0; i < dcTables.length; i++) {
75            tables.add(new Htable(dcTables[i], true, i));
76        }
77        for (int i = 0; i < acTables.length; i++) {
78            tables.add(new Htable(acTables[i], false, i));
79        }
80    }
81
82    DHTMarkerSegment(Node node) throws IIOInvalidTreeException {
83        super(JPEG.DHT);
84        NodeList children = node.getChildNodes();
85        int size = children.getLength();
86        if ((size < 1) || (size > 4)) {
87            throw new IIOInvalidTreeException("Invalid DHT node", node);
88        }
89        for (int i = 0; i < size; i++) {
90            tables.add(new Htable(children.item(i)));
91        }
92    }
93
94    protected Object clone() {
95        DHTMarkerSegment newGuy = (DHTMarkerSegment) super.clone();
96        newGuy.tables = new ArrayList<>(tables.size());
97        Iterator<Htable> iter = tables.iterator();
98        while (iter.hasNext()) {
99            Htable table = iter.next();
100            newGuy.tables.add((Htable) table.clone());
101        }
102        return newGuy;
103    }
104
105    IIOMetadataNode getNativeNode() {
106        IIOMetadataNode node = new IIOMetadataNode("dht");
107        for (int i= 0; i<tables.size(); i++) {
108            Htable table = tables.get(i);
109            node.appendChild(table.getNativeNode());
110        }
111        return node;
112    }
113
114    /**
115     * Writes the data for this segment to the stream in
116     * valid JPEG format.
117     */
118    void write(ImageOutputStream ios) throws IOException {
119        // We don't write DHT segments; the IJG library does.
120    }
121
122    void print() {
123        printTag("DHT");
124        System.out.println("Num tables: "
125                           + Integer.toString(tables.size()));
126        for (int i= 0; i<tables.size(); i++) {
127            Htable table = tables.get(i);
128            table.print();
129        }
130        System.out.println();
131
132    }
133
134    Htable getHtableFromNode(Node node) throws IIOInvalidTreeException {
135        return new Htable(node);
136    }
137
138    void addHtable(JPEGHuffmanTable table, boolean isDC, int id) {
139        tables.add(new Htable(table, isDC, id));
140    }
141
142    /**
143     * A Huffman table within a DHT marker segment.
144     */
145    class Htable implements Cloneable {
146        int tableClass;  // 0 == DC, 1 == AC
147        int tableID; // 0 - 4
148        private static final int NUM_LENGTHS = 16;
149        // # of codes of each length
150        short [] numCodes = new short[NUM_LENGTHS];
151        short [] values;
152
153        Htable(JPEGBuffer buffer) {
154            tableClass = buffer.buf[buffer.bufPtr] >>> 4;
155            tableID = buffer.buf[buffer.bufPtr++] & 0xf;
156            for (int i = 0; i < NUM_LENGTHS; i++) {
157                numCodes[i] = (short) (buffer.buf[buffer.bufPtr++] & 0xff);
158            }
159
160            int numValues = 0;
161            for (int i = 0; i < NUM_LENGTHS; i++) {
162                numValues += numCodes[i];
163            }
164            values = new short[numValues];
165            for (int i = 0; i < numValues; i++) {
166                values[i] = (short) (buffer.buf[buffer.bufPtr++] & 0xff);
167            }
168        }
169
170        Htable(JPEGHuffmanTable table, boolean isDC, int id) {
171            tableClass = isDC ? 0 : 1;
172            tableID = id;
173            numCodes = table.getLengths();
174            values = table.getValues();
175        }
176
177        Htable(Node node) throws IIOInvalidTreeException {
178            if (node.getNodeName().equals("dhtable")) {
179                NamedNodeMap attrs = node.getAttributes();
180                int count = attrs.getLength();
181                if (count != 2) {
182                    throw new IIOInvalidTreeException
183                        ("dhtable node must have 2 attributes", node);
184                }
185                tableClass = getAttributeValue(node, attrs, "class", 0, 1, true);
186                tableID = getAttributeValue(node, attrs, "htableId", 0, 3, true);
187                if (node instanceof IIOMetadataNode) {
188                    IIOMetadataNode ourNode = (IIOMetadataNode) node;
189                    JPEGHuffmanTable table =
190                        (JPEGHuffmanTable) ourNode.getUserObject();
191                    if (table == null) {
192                        throw new IIOInvalidTreeException
193                            ("dhtable node must have user object", node);
194                    }
195                    numCodes = table.getLengths();
196                    values = table.getValues();
197                } else {
198                    throw new IIOInvalidTreeException
199                        ("dhtable node must have user object", node);
200                }
201            } else {
202                throw new IIOInvalidTreeException
203                    ("Invalid node, expected dqtable", node);
204            }
205
206        }
207
208        protected Object clone() {
209            Htable newGuy = null;
210            try {
211                newGuy = (Htable) super.clone();
212            } catch (CloneNotSupportedException e) {} // won't happen
213            if (numCodes != null) {
214                newGuy.numCodes = numCodes.clone();
215            }
216            if (values != null) {
217                newGuy.values = values.clone();
218            }
219            return newGuy;
220        }
221
222        IIOMetadataNode getNativeNode() {
223            IIOMetadataNode node = new IIOMetadataNode("dhtable");
224            node.setAttribute("class", Integer.toString(tableClass));
225            node.setAttribute("htableId", Integer.toString(tableID));
226
227            node.setUserObject(new JPEGHuffmanTable(numCodes, values));
228
229            return node;
230        }
231
232
233        void print() {
234            System.out.println("Huffman Table");
235            System.out.println("table class: "
236                               + ((tableClass == 0) ? "DC":"AC"));
237            System.out.println("table id: " + Integer.toString(tableID));
238
239            (new JPEGHuffmanTable(numCodes, values)).toString();
240            /*
241              System.out.print("Lengths:");
242              for (int i=0; i<16; i++) {
243              System.out.print(" " + Integer.toString(numCodes[i]));
244              }
245              int count = 0;
246              if (values.length > 16) {
247              System.out.println("\nFirst 16 Values:");
248              count = 16;
249              } else {
250              System.out.println("\nValues:");
251              count = values.length;
252              }
253              for (int i=0; i<count; i++) {
254              System.out.println(Integer.toString(values[i]&0xff));
255              }
256            */
257        }
258    }
259
260}
261