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 class DhtSegment extends AbstractSegment { 030 public static class HuffmanTable { 031 // some arrays are better off one-based 032 // to avoid subtractions by one later when indexing them 033 public final int tableClass; 034 public final int destinationIdentifier; 035 private final int[] huffVal; // 0-based 036 037 // derived properties: 038 private final int[] huffSize = new int[16 * 256]; // 0-based 039 private final int[] huffCode; // 0-based 040 private final int[] minCode = new int[1 + 16]; // 1-based 041 private final int[] maxCode = new int[1 + 16]; // 1-based 042 private final int[] valPtr = new int[1 + 16]; // 1-based 043 044 HuffmanTable(final int tableClass, final int destinationIdentifier, final int[] bits, final int[] huffVal) { 045 this.tableClass = tableClass; 046 this.destinationIdentifier = destinationIdentifier; 047// this.bits = bits; // 1-based; not used outside the ctor 048 this.huffVal = huffVal; 049 050 // "generate_size_table", section C.2, figure C.1, page 51 of ITU-T 051 // T.81: 052 int k = 0; 053 int i = 1; 054 int j = 1; 055 int lastK = -1; 056 while (true) { 057 if (j > bits[i]) { 058 i++; 059 j = 1; 060 if (i > 16) { 061 huffSize[k] = 0; 062 lastK = k; 063 break; 064 } 065 } else { 066 huffSize[k] = i; 067 k++; 068 j++; 069 } 070 } 071 072 // "generate_code_table", section C.2, figure C.2, page 52 of ITU-T 073 // T.81: 074 k = 0; 075 int code = 0; 076 int si = huffSize[0]; 077 huffCode = Allocator.intArray(lastK); 078 while (true) { 079 if (k >= lastK) { 080 break; 081 } 082 huffCode[k] = code; 083 code++; 084 k++; 085 086 if (huffSize[k] == si) { 087 continue; 088 } 089 if (huffSize[k] == 0) { 090 break; 091 } 092 do { 093 code <<= 1; 094 si++; 095 } while (huffSize[k] != si); 096 } 097 098 // "Decoder_tables", section F.2.2.3, figure F.15, page 108 of T.81: 099 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}