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
027public class DhtSegment extends Segment {
028    public final List<HuffmanTable> huffmanTables;
029
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,
045                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 = new int[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 getMinCode(final int i) {
125            return minCode[i];
126        }
127
128        public int getMaxCode(final int i) {
129            return maxCode[i];
130        }
131
132        public int getValPtr(final int i) {
133            return valPtr[i];
134        }
135    }
136
137    public DhtSegment(final int marker, final byte[] segmentData) throws IOException {
138        this(marker, segmentData.length, new ByteArrayInputStream(segmentData));
139    }
140
141    public DhtSegment(final int marker, int length, final InputStream is)
142            throws IOException {
143        super(marker, length);
144
145        final ArrayList<HuffmanTable> huffmanTables = new ArrayList<>();
146        while (length > 0) {
147            final int tableClassAndDestinationId = 0xff & readByte(
148                    "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 = new int[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,
166                    destinationIdentifier, bits, huffVal));
167        }
168        this.huffmanTables = Collections.unmodifiableList(huffmanTables);
169    }
170
171    @Override
172    public String getDescription() {
173        return "DHT (" + getSegmentType() + ")";
174    }
175}