1 /*
2  * Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 package com.sun.imageio.plugins.tiff;
26 
27 import java.io.IOException;
28 import javax.imageio.IIOException;
29 import javax.imageio.plugins.tiff.BaselineTIFFTagSet;
30 
31 class TIFFLZWDecompressor extends TIFFDecompressor {
32 
33     private static final int CLEAR_CODE = 256;
34     private static final int EOI_CODE   = 257;
35     private static final int FIRST_CODE = 258;
36 
37     private static final int andTable[] = {
38         511,
39         1023,
40         2047,
41         4095
42     };
43 
44     private int predictor;
45 
46     // whether to reverse the bits in each byte of the input data, i.e.,
47     // convert right-to-left fill order (lsb) to left-to-right (msb).
48     private boolean flipBits;
49 
50     private byte[] srcData;
51     private byte[] dstData;
52 
53     private int srcIndex;
54     private int dstIndex;
55 
56     private byte stringTable[][];
57     private int tableIndex, bitsToGet = 9;
58 
59     private int nextData = 0;
60     private int nextBits = 0;
61 
TIFFLZWDecompressor(int predictor, int fillOrder)62     public TIFFLZWDecompressor(int predictor, int fillOrder)
63         throws IIOException {
64         super();
65 
66         if (predictor != BaselineTIFFTagSet.PREDICTOR_NONE &&
67             predictor !=
68             BaselineTIFFTagSet.PREDICTOR_HORIZONTAL_DIFFERENCING) {
69             throw new IIOException("Illegal value for Predictor in " +
70                                    "TIFF file");
71         }
72 
73         this.predictor = predictor;
74 
75         flipBits = fillOrder == BaselineTIFFTagSet.FILL_ORDER_RIGHT_TO_LEFT;
76     }
77 
decodeRaw(byte[] b, int dstOffset, int bitsPerPixel, int scanlineStride)78     public void decodeRaw(byte[] b,
79                           int dstOffset,
80                           int bitsPerPixel,
81                           int scanlineStride) throws IOException {
82 
83         // Check bitsPerSample.
84         if (predictor ==
85             BaselineTIFFTagSet.PREDICTOR_HORIZONTAL_DIFFERENCING) {
86             int len = bitsPerSample.length;
87             for(int i = 0; i < len; i++) {
88                 if(bitsPerSample[i] != 8) {
89                     throw new IIOException
90                         (bitsPerSample[i] + "-bit samples "+
91                          "are not supported for Horizontal "+
92                          "differencing Predictor");
93                 }
94             }
95         }
96 
97         stream.seek(offset);
98 
99         byte[] sdata = new byte[byteCount];
100         stream.readFully(sdata);
101 
102         if (flipBits) {
103             for (int i = 0; i < byteCount; i++) {
104                 sdata[i] = TIFFFaxDecompressor.flipTable[sdata[i] & 0xff];
105             }
106         }
107 
108         int bytesPerRow = (srcWidth*bitsPerPixel + 7)/8;
109         byte[] buf;
110         int bufOffset;
111         if(bytesPerRow == scanlineStride) {
112             buf = b;
113             bufOffset = dstOffset;
114         } else {
115             buf = new byte[bytesPerRow*srcHeight];
116             bufOffset = 0;
117         }
118 
119         int numBytesDecoded = decode(sdata, 0, buf, bufOffset);
120 
121         if(bytesPerRow != scanlineStride) {
122             int off = 0;
123             for (int y = 0; y < srcHeight; y++) {
124                 System.arraycopy(buf, off, b, dstOffset, bytesPerRow);
125                 off += bytesPerRow;
126                 dstOffset += scanlineStride;
127             }
128         }
129     }
130 
decode(byte[] sdata, int srcOffset, byte[] ddata, int dstOffset)131     public int decode(byte[] sdata, int srcOffset,
132                       byte[] ddata, int dstOffset)
133         throws IOException {
134         if (sdata[0] == (byte)0x00 && sdata[1] == (byte)0x01) {
135             throw new IIOException
136                 ("TIFF 5.0-style LZW compression is not supported!");
137         }
138 
139         this.srcData = sdata;
140         this.dstData = ddata;
141 
142         this.srcIndex = srcOffset;
143         this.dstIndex = dstOffset;
144 
145         this.nextData = 0;
146         this.nextBits = 0;
147 
148         initializeStringTable();
149 
150         int code, oldCode = 0;
151         byte[] string;
152 
153         while ((code = getNextCode()) != EOI_CODE) {
154             if (code == CLEAR_CODE) {
155                 initializeStringTable();
156                 code = getNextCode();
157                 if (code == EOI_CODE) {
158                     break;
159                 }
160 
161                 writeString(stringTable[code]);
162                 oldCode = code;
163             } else {
164                 if (code < tableIndex) {
165                     string = stringTable[code];
166 
167                     writeString(string);
168                     addStringToTable(stringTable[oldCode], string[0]);
169                     oldCode = code;
170                 } else {
171                     string = stringTable[oldCode];
172                     string = composeString(string, string[0]);
173                     writeString(string);
174                     addStringToTable(string);
175                     oldCode = code;
176                 }
177             }
178         }
179 
180         if (predictor ==
181             BaselineTIFFTagSet.PREDICTOR_HORIZONTAL_DIFFERENCING) {
182             int step = planar || samplesPerPixel == 1 ? 1 : samplesPerPixel;
183 
184             int samplesPerRow = step * srcWidth;
185 
186             int off = dstOffset + step;
187             for (int j = 0; j < srcHeight; j++) {
188                 int count = off;
189                 for (int i = step; i < samplesPerRow; i++) {
190                     dstData[count] += dstData[count - step];
191                     count++;
192                 }
193                 off += samplesPerRow;
194             }
195         }
196 
197         return dstIndex - dstOffset;
198     }
199 
200     /**
201      * Initialize the string table.
202      */
initializeStringTable()203     public void initializeStringTable() {
204         stringTable = new byte[4096][];
205 
206         for (int i = 0; i < CLEAR_CODE; i++) {
207             stringTable[i] = new byte[1];
208             stringTable[i][0] = (byte)i;
209         }
210 
211         tableIndex = FIRST_CODE;
212         bitsToGet = 9;
213     }
214 
215     /**
216      * Write out the string just uncompressed.
217      */
writeString(byte string[])218     public void writeString(byte string[]) {
219         if(dstIndex < dstData.length) {
220             int maxIndex = Math.min(string.length,
221                                     dstData.length - dstIndex);
222 
223             for (int i=0; i < maxIndex; i++) {
224                 dstData[dstIndex++] = string[i];
225             }
226         }
227     }
228 
229     /**
230      * Add a new string to the string table.
231      */
addStringToTable(byte oldString[], byte newString)232     public void addStringToTable(byte oldString[], byte newString) {
233         int length = oldString.length;
234         byte string[] = new byte[length + 1];
235         System.arraycopy(oldString, 0, string, 0, length);
236         string[length] = newString;
237 
238         // Add this new String to the table
239         stringTable[tableIndex++] = string;
240 
241         if (tableIndex == 511) {
242             bitsToGet = 10;
243         } else if (tableIndex == 1023) {
244             bitsToGet = 11;
245         } else if (tableIndex == 2047) {
246             bitsToGet = 12;
247         }
248     }
249 
250     /**
251      * Add a new string to the string table.
252      */
addStringToTable(byte string[])253     public void addStringToTable(byte string[]) {
254         // Add this new String to the table
255         stringTable[tableIndex++] = string;
256 
257         if (tableIndex == 511) {
258             bitsToGet = 10;
259         } else if (tableIndex == 1023) {
260             bitsToGet = 11;
261         } else if (tableIndex == 2047) {
262             bitsToGet = 12;
263         }
264     }
265 
266     /**
267      * Append {@code newString} to the end of {@code oldString}.
268      */
composeString(byte oldString[], byte newString)269     public byte[] composeString(byte oldString[], byte newString) {
270         int length = oldString.length;
271         byte string[] = new byte[length + 1];
272         System.arraycopy(oldString, 0, string, 0, length);
273         string[length] = newString;
274 
275         return string;
276     }
277 
278     // Returns the next 9, 10, 11 or 12 bits
getNextCode()279     public int getNextCode() {
280         // Attempt to get the next code. The exception is caught to make
281         // this robust to cases wherein the EndOfInformation code has been
282         // omitted from a strip. Examples of such cases have been observed
283         // in practice.
284 
285         try {
286             nextData = (nextData << 8) | (srcData[srcIndex++] & 0xff);
287             nextBits += 8;
288 
289             if (nextBits < bitsToGet) {
290                 nextData = (nextData << 8) | (srcData[srcIndex++] & 0xff);
291                 nextBits += 8;
292             }
293 
294             int code =
295                 (nextData >> (nextBits - bitsToGet)) & andTable[bitsToGet - 9];
296             nextBits -= bitsToGet;
297 
298             return code;
299         } catch (ArrayIndexOutOfBoundsException e) {
300             // Strip not terminated as expected: return EndOfInformation code.
301             return EOI_CODE;
302         }
303     }
304 }
305