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}