View Javadoc
1   /*
2    *  Licensed under the Apache License, Version 2.0 (the "License");
3    *  you may not use this file except in compliance with the License.
4    *  You may obtain a copy of the License at
5    *
6    *       http://www.apache.org/licenses/LICENSE-2.0
7    *
8    *  Unless required by applicable law or agreed to in writing, software
9    *  distributed under the License is distributed on an "AS IS" BASIS,
10   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11   *  See the License for the specific language governing permissions and
12   *  limitations under the License.
13   *  under the License.
14   */
15  
16  package org.apache.commons.imaging.formats.jpeg.segments;
17  
18  import static org.apache.commons.imaging.common.BinaryFunctions.readByte;
19  
20  import java.io.ByteArrayInputStream;
21  import java.io.IOException;
22  import java.io.InputStream;
23  import java.util.ArrayList;
24  import java.util.Collections;
25  import java.util.List;
26  
27  import org.apache.commons.imaging.common.Allocator;
28  
29  public class DhtSegment extends AbstractSegment {
30      public static class HuffmanTable {
31          // some arrays are better off one-based
32          // to avoid subtractions by one later when indexing them
33          public final int tableClass;
34          public final int destinationIdentifier;
35          private final int[] huffVal; // 0-based
36  
37          // derived properties:
38          private final int[] huffSize = new int[16 * 256]; // 0-based
39          private final int[] huffCode; // 0-based
40          private final int[] minCode = new int[1 + 16]; // 1-based
41          private final int[] maxCode = new int[1 + 16]; // 1-based
42          private final int[] valPtr = new int[1 + 16]; // 1-based
43  
44          HuffmanTable(final int tableClass, final int destinationIdentifier, final int[] bits, final int[] huffVal) {
45              this.tableClass = tableClass;
46              this.destinationIdentifier = destinationIdentifier;
47  //            this.bits = bits; // 1-based; not used outside the ctor
48              this.huffVal = huffVal;
49  
50              // "generate_size_table", section C.2, figure C.1, page 51 of ITU-T
51              // T.81:
52              int k = 0;
53              int i = 1;
54              int j = 1;
55              int lastK = -1;
56              while (true) {
57                  if (j > bits[i]) {
58                      i++;
59                      j = 1;
60                      if (i > 16) {
61                          huffSize[k] = 0;
62                          lastK = k;
63                          break;
64                      }
65                  } else {
66                      huffSize[k] = i;
67                      k++;
68                      j++;
69                  }
70              }
71  
72              // "generate_code_table", section C.2, figure C.2, page 52 of ITU-T
73              // T.81:
74              k = 0;
75              int code = 0;
76              int si = huffSize[0];
77              huffCode = Allocator.intArray(lastK);
78              while (true) {
79                  if (k >= lastK) {
80                      break;
81                  }
82                  huffCode[k] = code;
83                  code++;
84                  k++;
85  
86                  if (huffSize[k] == si) {
87                      continue;
88                  }
89                  if (huffSize[k] == 0) {
90                      break;
91                  }
92                  do {
93                      code <<= 1;
94                      si++;
95                  } while (huffSize[k] != si);
96              }
97  
98              // "Decoder_tables", section F.2.2.3, figure F.15, page 108 of T.81:
99              i = 0;
100             j = 0;
101             while (true) {
102                 i++;
103                 if (i > 16) {
104                     break;
105                 }
106                 if (bits[i] == 0) {
107                     maxCode[i] = -1;
108                 } else {
109                     valPtr[i] = j;
110                     minCode[i] = huffCode[j];
111                     j += bits[i] - 1;
112                     maxCode[i] = huffCode[j];
113                     j++;
114                 }
115             }
116 
117         }
118 
119         public int getHuffVal(final int i) {
120             return huffVal[i];
121         }
122 
123         public int getMaxCode(final int i) {
124             return maxCode[i];
125         }
126 
127         public int getMinCode(final int i) {
128             return minCode[i];
129         }
130 
131         public int getValPtr(final int i) {
132             return valPtr[i];
133         }
134     }
135 
136     public final List<HuffmanTable> huffmanTables;
137 
138     public DhtSegment(final int marker, final byte[] segmentData) throws IOException {
139         this(marker, segmentData.length, new ByteArrayInputStream(segmentData));
140     }
141 
142     public DhtSegment(final int marker, int length, final InputStream is) throws IOException {
143         super(marker, length);
144 
145         final ArrayList<HuffmanTable> huffmanTables = new ArrayList<>();
146         while (length > 0) {
147             final int tableClassAndDestinationId = 0xff & readByte("TableClassAndDestinationId", is, "Not a Valid JPEG File");
148             length--;
149             final int tableClass = tableClassAndDestinationId >> 4 & 0xf;
150             final int destinationIdentifier = tableClassAndDestinationId & 0xf;
151             final int[] bits = new int[1 + 16];
152             int bitsSum = 0;
153             for (int i = 1; i < bits.length; i++) {
154                 bits[i] = 0xff & readByte("Li", is, "Not a Valid JPEG File");
155                 length--;
156                 bitsSum += bits[i];
157             }
158             final int[] huffVal = Allocator.intArray(bitsSum);
159             for (int i = 0; i < bitsSum; i++) {
160                 huffVal[i] = 0xff & readByte("Vij", is, "Not a Valid JPEG File");
161                 length--;
162             }
163 
164             huffmanTables.add(new HuffmanTable(tableClass, destinationIdentifier, bits, huffVal));
165         }
166         this.huffmanTables = Collections.unmodifiableList(huffmanTables);
167     }
168 
169     @Override
170     public String getDescription() {
171         return "DHT (" + getSegmentType() + ")";
172     }
173 }