001/*
002 * $RCSfile: TIFFLZWDecompressor.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
051import com.github.jaiimageio.plugins.tiff.BaselineTIFFTagSet;
052import com.github.jaiimageio.plugins.tiff.TIFFDecompressor;
053
054public class TIFFLZWDecompressor extends TIFFDecompressor {
055
056    private static final boolean DEBUG = false;
057
058    private static final int andTable[] = {
059        511, 
060        1023,
061        2047,
062        4095
063    };
064
065    int predictor;
066
067    byte[] srcData;
068    byte[] dstData;
069
070    int srcIndex;
071    int dstIndex;
072
073    byte stringTable[][];
074    int tableIndex, bitsToGet = 9;
075
076    int nextData = 0;
077    int nextBits = 0;
078
079    public TIFFLZWDecompressor(int predictor) throws IIOException {
080        super();
081
082        if (predictor != BaselineTIFFTagSet.PREDICTOR_NONE && 
083            predictor != 
084            BaselineTIFFTagSet.PREDICTOR_HORIZONTAL_DIFFERENCING) {
085            throw new IIOException("Illegal value for Predictor in " +
086                                   "TIFF file");
087        }
088
089        if(DEBUG) {
090            System.out.println("Using horizontal differencing predictor");
091        }
092
093        this.predictor = predictor;
094    }
095
096    public void decodeRaw(byte[] b,
097                          int dstOffset,
098                          int bitsPerPixel,
099                          int scanlineStride) throws IOException {
100
101        // Check bitsPerSample.
102        if (predictor == 
103            BaselineTIFFTagSet.PREDICTOR_HORIZONTAL_DIFFERENCING) {
104            int len = bitsPerSample.length;
105            for(int i = 0; i < len; i++) {
106                if(bitsPerSample[i] != 8) {
107                    throw new IIOException
108                        (bitsPerSample[i] + "-bit samples "+
109                         "are not supported for Horizontal "+
110                         "differencing Predictor");
111                }
112            }
113        }
114
115        stream.seek(offset);
116
117        byte[] sdata = new byte[byteCount];
118        stream.readFully(sdata);
119
120        int bytesPerRow = (srcWidth*bitsPerPixel + 7)/8;
121        byte[] buf;
122        int bufOffset;
123        if(bytesPerRow == scanlineStride) {
124            buf = b;
125            bufOffset = dstOffset;
126        } else {
127            buf = new byte[bytesPerRow*srcHeight];
128            bufOffset = 0;
129        }
130
131        int numBytesDecoded = decode(sdata, 0, buf, bufOffset);
132
133        if(bytesPerRow != scanlineStride) {
134            if(DEBUG) {
135                System.out.println("bytesPerRow != scanlineStride");
136            }
137            int off = 0;
138            for (int y = 0; y < srcHeight; y++) {
139                System.arraycopy(buf, off, b, dstOffset, bytesPerRow);
140                off += bytesPerRow;
141                dstOffset += scanlineStride;
142            }
143        }
144    }
145
146    public int decode(byte[] sdata, int srcOffset,
147                      byte[] ddata, int dstOffset)
148        throws IOException {
149        if (sdata[0] == (byte)0x00 && sdata[1] == (byte)0x01) {
150            throw new IIOException
151                ("TIFF 5.0-style LZW compression is not supported!");
152        }
153
154        this.srcData = sdata;
155        this.dstData = ddata;
156
157        this.srcIndex = srcOffset;
158        this.dstIndex = dstOffset;
159
160        this.nextData = 0;
161        this.nextBits = 0;
162
163        initializeStringTable();
164
165        int code, oldCode = 0;
166        byte[] string;
167
168        while ((code = getNextCode()) != 257) {
169            if (code == 256) {
170                initializeStringTable();
171                code = getNextCode();
172                if (code == 257) {
173                    break;
174                }
175
176                writeString(stringTable[code]);
177                oldCode = code;
178            } else {
179                if (code < tableIndex) {
180                    string = stringTable[code];
181
182                    writeString(string);
183                    addStringToTable(stringTable[oldCode], string[0]); 
184                    oldCode = code;
185                } else {
186                    string = stringTable[oldCode];
187                    string = composeString(string, string[0]);
188                    writeString(string);
189                    addStringToTable(string);
190                    oldCode = code;
191                }
192            }
193        }
194
195        if (predictor ==
196            BaselineTIFFTagSet.PREDICTOR_HORIZONTAL_DIFFERENCING) {
197
198            for (int j = 0; j < srcHeight; j++) {
199                
200                int count = dstOffset + samplesPerPixel * (j * srcWidth + 1);
201                
202                for (int i = samplesPerPixel; i < srcWidth * samplesPerPixel; i++) {
203                    
204                    dstData[count] += dstData[count - samplesPerPixel];
205                    count++;
206                }
207            }
208        }
209
210        return dstIndex - dstOffset;
211    }
212
213    /**
214     * Initialize the string table.
215     */
216    public void initializeStringTable() {
217        stringTable = new byte[4096][];
218        
219        for (int i = 0; i < 256; i++) {
220            stringTable[i] = new byte[1];
221            stringTable[i][0] = (byte)i;
222        }
223        
224        tableIndex = 258;
225        bitsToGet = 9;
226    }
227
228    /**
229     * Write out the string just uncompressed.
230     */
231    public void writeString(byte string[]) {
232        if(dstIndex < dstData.length) {
233            int maxIndex = Math.min(string.length,
234                                    dstData.length - dstIndex);
235
236            for (int i=0; i < maxIndex; i++) {
237                dstData[dstIndex++] = string[i];
238            }
239        }
240    }
241    
242    /**
243     * Add a new string to the string table.
244     */
245    public void addStringToTable(byte oldString[], byte newString) {
246        int length = oldString.length;
247        byte string[] = new byte[length + 1];
248        System.arraycopy(oldString, 0, string, 0, length);
249        string[length] = newString;
250        
251        // Add this new String to the table
252        stringTable[tableIndex++] = string;
253        
254        if (tableIndex == 511) {
255            bitsToGet = 10;
256        } else if (tableIndex == 1023) {
257            bitsToGet = 11;
258        } else if (tableIndex == 2047) {
259            bitsToGet = 12;
260        } 
261    }
262
263    /**
264     * Add a new string to the string table.
265     */
266    public void addStringToTable(byte string[]) {
267        // Add this new String to the table
268        stringTable[tableIndex++] = string;
269        
270        if (tableIndex == 511) {
271            bitsToGet = 10;
272        } else if (tableIndex == 1023) {
273            bitsToGet = 11;
274        } else if (tableIndex == 2047) {
275            bitsToGet = 12;
276        } 
277    }
278
279    /**
280     * Append <code>newString</code> to the end of <code>oldString</code>.
281     */
282    public byte[] composeString(byte oldString[], byte newString) {
283        int length = oldString.length;
284        byte string[] = new byte[length + 1];
285        System.arraycopy(oldString, 0, string, 0, length);
286        string[length] = newString;
287
288        return string;
289    }
290
291    // Returns the next 9, 10, 11 or 12 bits
292    public int getNextCode() {
293        // Attempt to get the next code. The exception is caught to make
294        // this robust to cases wherein the EndOfInformation code has been
295        // omitted from a strip. Examples of such cases have been observed
296        // in practice.
297
298        try {
299            nextData = (nextData << 8) | (srcData[srcIndex++] & 0xff);
300            nextBits += 8;
301
302            if (nextBits < bitsToGet) {
303                nextData = (nextData << 8) | (srcData[srcIndex++] & 0xff);
304                nextBits += 8;
305            }
306
307            int code =
308                (nextData >> (nextBits - bitsToGet)) & andTable[bitsToGet - 9];
309            nextBits -= bitsToGet;
310
311            return code;
312        } catch (ArrayIndexOutOfBoundsException e) {
313            // Strip not terminated as expected: return EndOfInformation code.
314            return 257;
315        }
316    }
317}
318