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 final class DhtSegment extends AbstractSegment {
30  
31      public static class HuffmanTable {
32          // some arrays are better off one-based
33          // to avoid subtractions by one later when indexing them
34          public final int tableClass;
35          public final int destinationIdentifier;
36          private final int[] huffVal; // 0-based
37  
38          // derived properties:
39          private final int[] huffSize = new int[16 * 256]; // 0-based
40          private final int[] huffCode; // 0-based
41          private final int[] minCode = new int[1 + 16]; // 1-based
42          private final int[] maxCode = new int[1 + 16]; // 1-based
43          private final int[] valPtr = new int[1 + 16]; // 1-based
44  
45          HuffmanTable(final int tableClass, final int destinationIdentifier, final int[] bits, final int[] huffVal) {
46              this.tableClass = tableClass;
47              this.destinationIdentifier = destinationIdentifier;
48  //            this.bits = bits; // 1-based; not used outside the ctor
49              this.huffVal = huffVal;
50  
51              // "generate_size_table", section C.2, figure C.1, page 51 of ITU-T
52              // T.81:
53              int k = 0;
54              int i = 1;
55              int j = 1;
56              int lastK = -1;
57              while (true) {
58                  if (j > bits[i]) {
59                      i++;
60                      j = 1;
61                      if (i > 16) {
62                          huffSize[k] = 0;
63                          lastK = k;
64                          break;
65                      }
66                  } else {
67                      huffSize[k] = i;
68                      k++;
69                      j++;
70                  }
71              }
72  
73              // "generate_code_table", section C.2, figure C.2, page 52 of ITU-T
74              // T.81:
75              k = 0;
76              int code = 0;
77              int si = huffSize[0];
78              huffCode = Allocator.intArray(lastK);
79              while (true) {
80                  if (k >= lastK) {
81                      break;
82                  }
83                  huffCode[k] = code;
84                  code++;
85                  k++;
86  
87                  if (huffSize[k] == si) {
88                      continue;
89                  }
90                  if (huffSize[k] == 0) {
91                      break;
92                  }
93                  do {
94                      code <<= 1;
95                      si++;
96                  } while (huffSize[k] != si);
97              }
98  
99              // "Decoder_tables", section F.2.2.3, figure F.15, page 108 of T.81:
100             i = 0;
101             j = 0;
102             while (true) {
103                 i++;
104                 if (i > 16) {
105                     break;
106                 }
107                 if (bits[i] == 0) {
108                     maxCode[i] = -1;
109                 } else {
110                     valPtr[i] = j;
111                     minCode[i] = huffCode[j];
112                     j += bits[i] - 1;
113                     maxCode[i] = huffCode[j];
114                     j++;
115                 }
116             }
117 
118         }
119 
120         public int getHuffVal(final int i) {
121             return huffVal[i];
122         }
123 
124         public int getMaxCode(final int i) {
125             return maxCode[i];
126         }
127 
128         public int getMinCode(final int i) {
129             return minCode[i];
130         }
131 
132         public int getValPtr(final int i) {
133             return valPtr[i];
134         }
135     }
136 
137     public final List<HuffmanTable> huffmanTables;
138 
139     public DhtSegment(final int marker, final byte[] segmentData) throws IOException {
140         this(marker, segmentData.length, new ByteArrayInputStream(segmentData));
141     }
142 
143     public DhtSegment(final int marker, int length, final InputStream is) throws IOException {
144         super(marker, length);
145 
146         final ArrayList<HuffmanTable> huffmanTables = new ArrayList<>();
147         while (length > 0) {
148             final int tableClassAndDestinationId = 0xff & readByte("TableClassAndDestinationId", is, "Not a Valid JPEG File");
149             length--;
150             final int tableClass = tableClassAndDestinationId >> 4 & 0xf;
151             final int destinationIdentifier = tableClassAndDestinationId & 0xf;
152             final int[] bits = new int[1 + 16];
153             int bitsSum = 0;
154             for (int i = 1; i < bits.length; i++) {
155                 bits[i] = 0xff & readByte("Li", is, "Not a Valid JPEG File");
156                 length--;
157                 bitsSum += bits[i];
158             }
159             final int[] huffVal = Allocator.intArray(bitsSum);
160             for (int i = 0; i < bitsSum; i++) {
161                 huffVal[i] = 0xff & readByte("Vij", is, "Not a Valid JPEG File");
162                 length--;
163             }
164 
165             huffmanTables.add(new HuffmanTable(tableClass, destinationIdentifier, bits, huffVal));
166         }
167         this.huffmanTables = Collections.unmodifiableList(huffmanTables);
168     }
169 
170     @Override
171     public String getDescription() {
172         return "DHT (" + getSegmentType() + ")";
173     }
174 }