001/*
002 * $RCSfile: TIFFLZWUtil.java,v $
003 *
004 * 
005 * Copyright (c) 2005 Sun Microsystems, Inc. All  Rights Reserved.
006 * 
007 * Redistribution and use in source and binary forms, with or without
008 * modification, are permitted provided that the following conditions
009 * are met: 
010 * 
011 * - Redistribution of source code must retain the above copyright 
012 *   notice, this  list of conditions and the following disclaimer.
013 * 
014 * - Redistribution in binary form must reproduce the above copyright
015 *   notice, this list of conditions and the following disclaimer in 
016 *   the documentation and/or other materials provided with the
017 *   distribution.
018 * 
019 * Neither the name of Sun Microsystems, Inc. or the names of 
020 * contributors may be used to endorse or promote products derived 
021 * from this software without specific prior written permission.
022 * 
023 * This software is provided "AS IS," without a warranty of any 
024 * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND 
025 * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, 
026 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
027 * EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL 
028 * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF 
029 * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
030 * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR 
031 * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
032 * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
033 * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
034 * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
035 * POSSIBILITY OF SUCH DAMAGES. 
036 * 
037 * You acknowledge that this software is not designed or intended for 
038 * use in the design, construction, operation or maintenance of any 
039 * nuclear facility. 
040 *
041 * $Revision: 1.1 $
042 * $Date: 2005/02/11 05:01:48 $
043 * $State: Exp $
044 */
045package com.github.jaiimageio.impl.plugins.tiff;
046
047import java.io.IOException;
048
049import javax.imageio.IIOException;
050
051public class TIFFLZWUtil {
052
053    private static final boolean debug = false;
054
055    public TIFFLZWUtil() {
056    }
057
058    byte[] srcData;
059    int srcIndex;
060
061    byte[] dstData;
062    int dstIndex = 0;
063
064    byte stringTable[][];
065    int tableIndex, bitsToGet = 9;
066
067    int predictor, samplesPerPixel;
068    int nextData = 0;
069    int nextBits = 0;
070
071    private static final int andTable[] = {
072        511, 
073        1023,
074        2047,
075        4095
076    };
077
078    public byte[] decode(byte[] data, int predictor, int samplesPerPixel,
079                         int width, int height) throws IOException {
080        if (data[0] == (byte)0x00 && data[1] == (byte)0x01) {
081            throw new IIOException("TIFF 5.0-style LZW compression is not supported!");
082        }
083
084        this.srcData = data;
085        this.srcIndex = 0;
086        this.nextData = 0;
087        this.nextBits = 0;
088
089        this.dstData = new byte[8192];
090        this.dstIndex = 0;
091
092        initializeStringTable();
093
094        int code, oldCode = 0;
095        byte[] string;
096
097        while ((code = getNextCode()) != 257) {
098            if (code == 256) {
099                initializeStringTable();
100                code = getNextCode();
101                if (code == 257) {
102                    break;
103                }
104
105                writeString(stringTable[code]);
106                oldCode = code;
107            } else {
108                if (code < tableIndex) {
109                    string = stringTable[code];
110
111                    writeString(string);
112                    addStringToTable(stringTable[oldCode], string[0]); 
113                    oldCode = code;
114                } else {
115                    string = stringTable[oldCode];
116                    string = composeString(string, string[0]);
117                    writeString(string);
118                    addStringToTable(string);
119                    oldCode = code;
120                }
121            }
122        }
123
124        if (predictor == 2) {
125            
126            int count;
127            for (int j = 0; j < height; j++) {
128                
129                count = samplesPerPixel * (j * width + 1);
130                
131                for (int i = samplesPerPixel; i < width * samplesPerPixel; i++) {
132                    
133                    dstData[count] += dstData[count - samplesPerPixel];
134                    count++;
135                }
136            }
137        }
138
139        byte[] newDstData = new byte[dstIndex];
140        System.arraycopy(dstData, 0, newDstData, 0, dstIndex);
141        return newDstData;
142    }
143
144    /**
145     * Initialize the string table.
146     */
147    public void initializeStringTable() {
148        stringTable = new byte[4096][];
149        
150        for (int i = 0; i < 256; i++) {
151            stringTable[i] = new byte[1];
152            stringTable[i][0] = (byte)i;
153        }
154        
155        tableIndex = 258;
156        bitsToGet = 9;
157    }
158
159    private void ensureCapacity(int bytesToAdd) {
160        if (dstIndex + bytesToAdd > dstData.length) {
161            byte[] newDstData = new byte[Math.max((int)(dstData.length*1.2f),
162                                                  dstIndex + bytesToAdd)];
163            System.arraycopy(dstData, 0, newDstData, 0, dstData.length);
164            dstData = newDstData;
165        }
166    }
167
168    /**
169     * Write out the string just uncompressed.
170     */
171    public void writeString(byte string[]) {
172        ensureCapacity(string.length);
173        for (int i = 0; i < string.length; i++) {
174            dstData[dstIndex++] = string[i];
175        }
176    }
177    
178    /**
179     * Add a new string to the string table.
180     */
181    public void addStringToTable(byte oldString[], byte newString) {
182        int length = oldString.length;
183        byte string[] = new byte[length + 1];
184        System.arraycopy(oldString, 0, string, 0, length);
185        string[length] = newString;
186        
187        // Add this new String to the table
188        stringTable[tableIndex++] = string;
189        
190        if (tableIndex == 511) {
191            bitsToGet = 10;
192        } else if (tableIndex == 1023) {
193            bitsToGet = 11;
194        } else if (tableIndex == 2047) {
195            bitsToGet = 12;
196        } 
197    }
198
199    /**
200     * Add a new string to the string table.
201     */
202    public void addStringToTable(byte string[]) {
203        // Add this new String to the table
204        stringTable[tableIndex++] = string;
205        
206        if (tableIndex == 511) {
207            bitsToGet = 10;
208        } else if (tableIndex == 1023) {
209            bitsToGet = 11;
210        } else if (tableIndex == 2047) {
211            bitsToGet = 12;
212        } 
213    }
214
215    /**
216     * Append <code>newString</code> to the end of <code>oldString</code>.
217     */
218    public byte[] composeString(byte oldString[], byte newString) {
219        int length = oldString.length;
220        byte string[] = new byte[length + 1];
221        System.arraycopy(oldString, 0, string, 0, length);
222        string[length] = newString;
223
224        return string;
225    }
226
227    // Returns the next 9, 10, 11 or 12 bits
228    public int getNextCode() {
229        // Attempt to get the next code. The exception is caught to make
230        // this robust to cases wherein the EndOfInformation code has been
231        // omitted from a strip. Examples of such cases have been observed
232        // in practice.
233
234        try {
235            nextData = (nextData << 8) | (srcData[srcIndex++] & 0xff);
236            nextBits += 8;
237
238            if (nextBits < bitsToGet) {
239                nextData = (nextData << 8) | (srcData[srcIndex++] & 0xff);
240                nextBits += 8;
241            }
242
243            int code =
244                (nextData >> (nextBits - bitsToGet)) & andTable[bitsToGet - 9];
245            nextBits -= bitsToGet;
246
247            return code;
248        } catch (ArrayIndexOutOfBoundsException e) {
249            // Strip not terminated as expected: return EndOfInformation code.
250            return 257;
251        }
252    }
253}