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 andTable[] = {
  34         511,
  35         1023,
  36         2047,
  37         4095
  38     };
  39 
  40     private int predictor;
  41 
  42     private byte[] srcData;
  43     private byte[] dstData;
  44 
  45     private int srcIndex;
  46     private int dstIndex;
  47 
  48     private byte stringTable[][];
  49     private int tableIndex, bitsToGet = 9;
  50 
  51     private int nextData = 0;
  52     private int nextBits = 0;
  53 
  54     public TIFFLZWDecompressor(int predictor) throws IIOException {
  55         super();
  56 
  57         if (predictor != BaselineTIFFTagSet.PREDICTOR_NONE &&
  58             predictor !=
  59             BaselineTIFFTagSet.PREDICTOR_HORIZONTAL_DIFFERENCING) {
  60             throw new IIOException("Illegal value for Predictor in " +
  61                                    "TIFF file");
  62         }
  63 
  64         this.predictor = predictor;
  65     }
  66 
  67     public void decodeRaw(byte[] b,
  68                           int dstOffset,
  69                           int bitsPerPixel,
  70                           int scanlineStride) throws IOException {
  71 
  72         // Check bitsPerSample.
  73         if (predictor ==
  74             BaselineTIFFTagSet.PREDICTOR_HORIZONTAL_DIFFERENCING) {
  75             int len = bitsPerSample.length;
  76             for(int i = 0; i < len; i++) {
  77                 if(bitsPerSample[i] != 8) {
  78                     throw new IIOException
  79                         (bitsPerSample[i] + "-bit samples "+
  80                          "are not supported for Horizontal "+
  81                          "differencing Predictor");
  82                 }
  83             }
  84         }
  85 
  86         stream.seek(offset);
  87 
  88         byte[] sdata = new byte[byteCount];
  89         stream.readFully(sdata);
  90 
  91         int bytesPerRow = (srcWidth*bitsPerPixel + 7)/8;
  92         byte[] buf;
  93         int bufOffset;
  94         if(bytesPerRow == scanlineStride) {
  95             buf = b;
  96             bufOffset = dstOffset;
  97         } else {
  98             buf = new byte[bytesPerRow*srcHeight];
  99             bufOffset = 0;
 100         }
 101 
 102         int numBytesDecoded = decode(sdata, 0, buf, bufOffset);
 103 
 104         if(bytesPerRow != scanlineStride) {
 105             int off = 0;
 106             for (int y = 0; y < srcHeight; y++) {
 107                 System.arraycopy(buf, off, b, dstOffset, bytesPerRow);
 108                 off += bytesPerRow;
 109                 dstOffset += scanlineStride;
 110             }
 111         }
 112     }
 113 
 114     public int decode(byte[] sdata, int srcOffset,
 115                       byte[] ddata, int dstOffset)
 116         throws IOException {
 117         if (sdata[0] == (byte)0x00 && sdata[1] == (byte)0x01) {
 118             throw new IIOException
 119                 ("TIFF 5.0-style LZW compression is not supported!");
 120         }
 121 
 122         this.srcData = sdata;
 123         this.dstData = ddata;
 124 
 125         this.srcIndex = srcOffset;
 126         this.dstIndex = dstOffset;
 127 
 128         this.nextData = 0;
 129         this.nextBits = 0;
 130 
 131         initializeStringTable();
 132 
 133         int code, oldCode = 0;
 134         byte[] string;
 135 
 136         while ((code = getNextCode()) != 257) {
 137             if (code == 256) {
 138                 initializeStringTable();
 139                 code = getNextCode();
 140                 if (code == 257) {
 141                     break;
 142                 }
 143 
 144                 writeString(stringTable[code]);
 145                 oldCode = code;
 146             } else {
 147                 if (code < tableIndex) {
 148                     string = stringTable[code];
 149 
 150                     writeString(string);
 151                     addStringToTable(stringTable[oldCode], string[0]);
 152                     oldCode = code;
 153                 } else {
 154                     string = stringTable[oldCode];
 155                     string = composeString(string, string[0]);
 156                     writeString(string);
 157                     addStringToTable(string);
 158                     oldCode = code;
 159                 }
 160             }
 161         }
 162 
 163         if (predictor ==
 164             BaselineTIFFTagSet.PREDICTOR_HORIZONTAL_DIFFERENCING) {
 165 
 166             for (int j = 0; j < srcHeight; j++) {
 167 
 168                 int count = dstOffset + samplesPerPixel * (j * srcWidth + 1);
 169 
 170                 for (int i = samplesPerPixel; i < srcWidth * samplesPerPixel; i++) {
 171 
 172                     dstData[count] += dstData[count - samplesPerPixel];
 173                     count++;
 174                 }
 175             }
 176         }
 177 
 178         return dstIndex - dstOffset;
 179     }
 180 
 181     /**
 182      * Initialize the string table.
 183      */
 184     public void initializeStringTable() {
 185         stringTable = new byte[4096][];
 186 
 187         for (int i = 0; i < 256; i++) {
 188             stringTable[i] = new byte[1];
 189             stringTable[i][0] = (byte)i;
 190         }
 191 
 192         tableIndex = 258;
 193         bitsToGet = 9;
 194     }
 195 
 196     /**
 197      * Write out the string just uncompressed.
 198      */
 199     public void writeString(byte string[]) {
 200         if(dstIndex < dstData.length) {
 201             int maxIndex = Math.min(string.length,
 202                                     dstData.length - dstIndex);
 203 
 204             for (int i=0; i < maxIndex; i++) {
 205                 dstData[dstIndex++] = string[i];
 206             }
 207         }
 208     }
 209 
 210     /**
 211      * Add a new string to the string table.
 212      */
 213     public void addStringToTable(byte oldString[], byte newString) {
 214         int length = oldString.length;
 215         byte string[] = new byte[length + 1];
 216         System.arraycopy(oldString, 0, string, 0, length);
 217         string[length] = newString;
 218 
 219         // Add this new String to the table
 220         stringTable[tableIndex++] = string;
 221 
 222         if (tableIndex == 511) {
 223             bitsToGet = 10;
 224         } else if (tableIndex == 1023) {
 225             bitsToGet = 11;
 226         } else if (tableIndex == 2047) {
 227             bitsToGet = 12;
 228         }
 229     }
 230 
 231     /**
 232      * Add a new string to the string table.
 233      */
 234     public void addStringToTable(byte string[]) {
 235         // Add this new String to the table
 236         stringTable[tableIndex++] = string;
 237 
 238         if (tableIndex == 511) {
 239             bitsToGet = 10;
 240         } else if (tableIndex == 1023) {
 241             bitsToGet = 11;
 242         } else if (tableIndex == 2047) {
 243             bitsToGet = 12;
 244         }
 245     }
 246 
 247     /**
 248      * Append {@code newString} to the end of {@code oldString}.
 249      */
 250     public byte[] composeString(byte oldString[], byte newString) {
 251         int length = oldString.length;
 252         byte string[] = new byte[length + 1];
 253         System.arraycopy(oldString, 0, string, 0, length);
 254         string[length] = newString;
 255 
 256         return string;
 257     }
 258 
 259     // Returns the next 9, 10, 11 or 12 bits
 260     public int getNextCode() {
 261         // Attempt to get the next code. The exception is caught to make
 262         // this robust to cases wherein the EndOfInformation code has been
 263         // omitted from a strip. Examples of such cases have been observed
 264         // in practice.
 265 
 266         try {
 267             nextData = (nextData << 8) | (srcData[srcIndex++] & 0xff);
 268             nextBits += 8;
 269 
 270             if (nextBits < bitsToGet) {
 271                 nextData = (nextData << 8) | (srcData[srcIndex++] & 0xff);
 272                 nextBits += 8;
 273             }
 274 
 275             int code =
 276                 (nextData >> (nextBits - bitsToGet)) & andTable[bitsToGet - 9];
 277             nextBits -= bitsToGet;
 278 
 279             return code;
 280         } catch (ArrayIndexOutOfBoundsException e) {
 281             // Strip not terminated as expected: return EndOfInformation code.
 282             return 257;
 283         }
 284     }
 285 }