001    /*
002     * TIFFDecoderDeflated
003     *
004     * Copyright (c) 2002 Marco Schmidt.
005     * All rights reserved.
006     */
007    
008    package net.sourceforge.jiu.codecs.tiff;
009    
010    import java.io.DataInput;
011    import java.io.IOException;
012    import java.util.zip.DataFormatException;
013    import java.util.zip.Inflater;
014    import net.sourceforge.jiu.codecs.tiff.TIFFConstants;
015    import net.sourceforge.jiu.codecs.tiff.TIFFDecoder;
016    import net.sourceforge.jiu.codecs.tiff.TIFFImageFileDirectory;
017    import net.sourceforge.jiu.codecs.InvalidFileStructureException;
018    import net.sourceforge.jiu.ops.MissingParameterException;
019    
020    /**
021     * A TIFF decoder for files compressed with the <em>Deflated</em> method.
022     * This compression algorithm has the values <code>31946</code> 
023     * ({@link TIFFConstants#COMPRESSION_DEFLATED_INOFFICIAL}) and <code>8</code>
024     * ({@link TIFFConstants#COMPRESSION_DEFLATED_OFFICIAL}) 
025     * in the compression tag of an image file directory.
026     * All types of image data can be compressed with this method.
027     * <p>
028     * This decoder makes use of the package java.util.zip which comes with an Inflater
029     * class which does most of the use.
030     * All the decoder has to do is feed it with compressed data from the input file.
031     * and give decompressed data received from the Inflater to the putBytes method.
032     * @author Marco Schmidt
033     * @since 0.9.0
034     */
035    public class TIFFDecoderDeflated extends TIFFDecoder
036    {
037            private DataInput in;
038            private int compressedSize;
039    
040            public void decode() throws 
041                    InvalidFileStructureException,
042                    IOException
043            {
044                    Inflater inflater = new Inflater();
045                    byte[] ioBuffer = new byte[20000];
046                    byte[] data = new byte[getBytesPerRow()];
047                    // determine how many bytes have to be read from inflater
048                    int numRows = getY2();
049                    TIFFImageFileDirectory ifd = getImageFileDirectory();
050                    if (numRows > ifd.getHeight() - 1)
051                    {
052                            numRows = ifd.getHeight() - 1;
053                    }
054                    numRows -= getY1();
055                    int remainingBytes = numRows * data.length;
056                    // now read and decompress as long as there is data left to decompress
057                    while (compressedSize > 0 || remainingBytes > 0)
058                    {
059                            if (inflater.needsInput())
060                            {
061                                    // read compressed data from input
062                                    int numBytes;
063                                    if (compressedSize > ioBuffer.length)
064                                    {
065                                            numBytes = ioBuffer.length;
066                                    }
067                                    else
068                                    {
069                                            numBytes = compressedSize;
070                                    }
071                                    in.readFully(ioBuffer, 0, numBytes);
072                                    // give data to inflater
073                                    inflater.setInput(ioBuffer, 0, numBytes);
074                                    compressedSize -= numBytes;
075                            }
076                            else
077                            {
078                                    // determine how many bytes to decompress in this loop iteration
079                                    int numBytes;
080                                    if (remainingBytes > data.length)
081                                    {
082                                            numBytes = data.length;
083                                    }
084                                    else
085                                    {
086                                            numBytes = remainingBytes;
087                                    }
088                                    int numInflated;
089                                    // do the decompression
090                                    try
091                                    {
092                                            numInflated = inflater.inflate(data, 0, numBytes);
093                                    }
094                                    catch (DataFormatException dfe)
095                                    {
096                                            throw new InvalidFileStructureException("Error in compressed input data: " + dfe.toString());
097                                    }
098                                    // store decompressed data and update number of bytes left to decompress
099                                    if (numInflated > 0)
100                                    {
101                                            putBytes(data, 0, numInflated);
102                                            remainingBytes -= numInflated;
103                                    }
104                            }
105                    }
106            }
107    
108            public Integer[] getCompressionTypes()
109            {
110                    return new Integer[] {new Integer(TIFFConstants.COMPRESSION_DEFLATED_INOFFICIAL), new Integer(TIFFConstants.COMPRESSION_DEFLATED_OFFICIAL)};
111            }
112    
113            public void initialize() throws
114                    IOException, 
115                    MissingParameterException
116    
117            {
118                    super.initialize();
119                    in = getInput();
120                    compressedSize = getImageFileDirectory().getByteCount(getTileIndex());
121            }       
122    }