    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}