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 
  26 package com.sun.imageio.plugins.jpeg;
  27 
  28 import javax.imageio.metadata.IIOInvalidTreeException;
  29 import javax.imageio.metadata.IIOMetadataNode;
  30 import javax.imageio.stream.ImageOutputStream;
  31 import javax.imageio.plugins.jpeg.JPEGHuffmanTable;
  32 
  33 import java.io.IOException;
  34 import java.util.List;
  35 import java.util.ArrayList;
  36 import java.util.Iterator;
  37 
  38 import org.w3c.dom.Node;
  39 import org.w3c.dom.NodeList;
  40 import org.w3c.dom.NamedNodeMap;
  41 
  42 /**
  43  * A DHT (Define Huffman Table) marker segment.
  44  */
  45 class DHTMarkerSegment extends MarkerSegment {
  46     List 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 iter = tables.iterator();
  98         while (iter.hasNext()) {
  99             Htable table = (Htable) iter.next();
 100             newGuy.tables.add(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 = (Htable) 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 = (Htable) 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 }