001/* 002 * Licensed under the Apache License, Version 2.0 (the "License"); 003 * you may not use this file except in compliance with the License. 004 * You may obtain a copy of the License at 005 * 006 * http://www.apache.org/licenses/LICENSE-2.0 007 * 008 * Unless required by applicable law or agreed to in writing, software 009 * distributed under the License is distributed on an "AS IS" BASIS, 010 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 011 * See the License for the specific language governing permissions and 012 * limitations under the License. 013 * under the License. 014 */ 015 016package org.apache.commons.imaging.formats.jpeg.segments; 017 018import static org.apache.commons.imaging.common.BinaryFunctions.readByte; 019 020import java.io.ByteArrayInputStream; 021import java.io.IOException; 022import java.io.InputStream; 023import java.util.ArrayList; 024import java.util.Collections; 025import java.util.List; 026 027import org.apache.commons.imaging.common.Allocator; 028 029public final class DhtSegment extends AbstractSegment { 030 031 public static class HuffmanTable { 032 // some arrays are better off one-based 033 // to avoid subtractions by one later when indexing them 034 public final int tableClass; 035 public final int destinationIdentifier; 036 private final int[] huffVal; // 0-based 037 038 // derived properties: 039 private final int[] huffSize = new int[16 * 256]; // 0-based 040 private final int[] huffCode; // 0-based 041 private final int[] minCode = new int[1 + 16]; // 1-based 042 private final int[] maxCode = new int[1 + 16]; // 1-based 043 private final int[] valPtr = new int[1 + 16]; // 1-based 044 045 HuffmanTable(final int tableClass, final int destinationIdentifier, final int[] bits, final int[] huffVal) { 046 this.tableClass = tableClass; 047 this.destinationIdentifier = destinationIdentifier; 048// this.bits = bits; // 1-based; not used outside the ctor 049 this.huffVal = huffVal; 050 051 // "generate_size_table", section C.2, figure C.1, page 51 of ITU-T 052 // T.81: 053 int k = 0; 054 int i = 1; 055 int j = 1; 056 int lastK = -1; 057 while (true) { 058 if (j > bits[i]) { 059 i++; 060 j = 1; 061 if (i > 16) { 062 huffSize[k] = 0; 063 lastK = k; 064 break; 065 } 066 } else { 067 huffSize[k] = i; 068 k++; 069 j++; 070 } 071 } 072 073 // "generate_code_table", section C.2, figure C.2, page 52 of ITU-T 074 // T.81: 075 k = 0; 076 int code = 0; 077 int si = huffSize[0]; 078 huffCode = Allocator.intArray(lastK); 079 while (true) { 080 if (k >= lastK) { 081 break; 082 } 083 huffCode[k] = code; 084 code++; 085 k++; 086 087 if (huffSize[k] == si) { 088 continue; 089 } 090 if (huffSize[k] == 0) { 091 break; 092 } 093 do { 094 code <<= 1; 095 si++; 096 } while (huffSize[k] != si); 097 } 098 099 // "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}