001/*
002 * $RCSfile: TIFFJPEGDecompressor.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.2 $
042 * $Date: 2006/04/11 22:10:36 $
043 * $State: Exp $
044 */
045package com.github.jaiimageio.impl.plugins.tiff;
046
047import java.io.ByteArrayInputStream;
048import java.io.IOException;
049import java.util.Iterator;
050
051import javax.imageio.ImageIO;
052import javax.imageio.ImageReadParam;
053import javax.imageio.ImageReader;
054import javax.imageio.stream.ImageInputStream;
055import javax.imageio.stream.MemoryCacheImageInputStream;
056
057import com.github.jaiimageio.plugins.tiff.BaselineTIFFTagSet;
058import com.github.jaiimageio.plugins.tiff.TIFFDecompressor;
059import com.github.jaiimageio.plugins.tiff.TIFFField;
060
061public class TIFFJPEGDecompressor extends TIFFDecompressor {
062    private static final boolean DEBUG = false; // XXX false for release.
063
064    // Start of Image
065    protected static final int SOI = 0xD8;
066
067    // End of Image
068    protected static final int EOI = 0xD9;
069
070    //private static ImageReaderSpi jpegReaderSPI = null;//XXX
071
072    protected ImageReader JPEGReader = null;
073    protected ImageReadParam JPEGParam;
074
075    protected boolean hasJPEGTables = false;
076    protected byte[] tables = null;
077
078    private byte[] data = new byte[0];
079
080    /* XXX
081    static {
082        try {
083            IIORegistry registry = IIORegistry.getDefaultInstance();
084            Class imageReaderClass =
085                Class.forName("javax.imageio.spi.ImageReaderSpi");
086            Iterator readerSPIs =
087                registry.getServiceProviders(imageReaderClass,
088                                             new JPEGSPIFilter(),
089                                             true);
090            if(readerSPIs.hasNext()) {
091                jpegReaderSPI = (ImageReaderSpi)readerSPIs.next();
092            }
093        } catch(Exception e) {
094            // Ignore it ...
095        }
096    }
097    */
098
099    public TIFFJPEGDecompressor() {}
100
101    /* XXX
102    private static class JPEGSPIFilter implements ServiceRegistry.Filter {
103        JPEGSPIFilter() {}
104
105        public boolean filter(Object provider) {
106            ImageReaderSpi readerSPI = (ImageReaderSpi)provider;
107
108            if(readerSPI.getPluginClassName().startsWith("com.sun.imageio")) {
109                String streamMetadataName =
110                    readerSPI.getNativeStreamMetadataFormatName();
111                if(streamMetadataName != null) {
112                    return streamMetadataName.indexOf("jpeg_stream") != -1;
113                } else {
114                    return false;
115                }
116            }
117
118            return false;
119        }
120    }
121    */
122
123    public void beginDecoding() {
124        // Initialize the JPEG reader if needed.
125        if(this.JPEGReader == null) {
126            if(DEBUG) System.out.println("Initializing JPEGReader");
127
128            /* XXX
129            if(this.jpegReaderSPI != null) {
130                try {
131                    this.JPEGReader = jpegReaderSPI.createReaderInstance();
132                } catch(Exception e) {
133                }
134            }
135
136            if(this.JPEGReader == null) {
137            */
138
139            // Get all JPEG readers.
140            Iterator iter = ImageIO.getImageReadersByFormatName("jpeg");
141
142            if(!iter.hasNext()) {
143                // XXX The exception thrown should be an IIOException.
144                throw new IllegalStateException("No JPEG readers found!");
145            }
146
147            // Initialize reader to the first one.
148            this.JPEGReader = (ImageReader)iter.next();
149
150            if(DEBUG) System.out.println("Using "+
151                                         JPEGReader.getClass().getName());
152
153            this.JPEGParam = JPEGReader.getDefaultReadParam();
154        }
155
156        // Get the JPEGTables field.
157        TIFFImageMetadata tmetadata = (TIFFImageMetadata)metadata;
158        TIFFField f =
159            tmetadata.getTIFFField(BaselineTIFFTagSet.TAG_JPEG_TABLES);
160
161        if (f != null) {
162            this.hasJPEGTables = true;
163            this.tables = f.getAsBytes();
164        } else {
165            this.hasJPEGTables = false;
166        }
167    }
168
169    public void decodeRaw(byte[] b,
170                          int dstOffset,
171                          int bitsPerPixel,
172                          int scanlineStride) throws IOException {
173        // Seek to the data position for this segment.
174        stream.seek(offset);
175
176        // Set the stream variable depending on presence of JPEGTables.
177        ImageInputStream is;
178        if(this.hasJPEGTables) {
179            if(DEBUG) System.out.println("Reading abbreviated stream.");
180            // The current strip or tile is an abbreviated JPEG stream.
181
182            // Reallocate memory if there is not enough already.
183            int dataLength = tables.length + byteCount;
184            if(data.length < dataLength) {
185                data = new byte[dataLength];
186            }
187
188            // Copy the tables ignoring any EOI and subsequent bytes.
189            int dataOffset = tables.length;
190            for(int i = tables.length - 2; i > 0; i--) {
191                if((tables[i] & 0xff) == 0xff &&
192                   (tables[i+1] & 0xff) == EOI) {
193                    dataOffset = i;
194                    break;
195                }
196            }
197            System.arraycopy(tables, 0, data, 0, dataOffset);
198
199            // Check for SOI and skip it if present.
200            byte byte1 = (byte)stream.read();
201            byte byte2 = (byte)stream.read();
202            if(!((byte1 & 0xff) == 0xff && (byte2 & 0xff) == SOI)) {
203                data[dataOffset++] = (byte)byte1;
204                data[dataOffset++] = (byte)byte2;
205            }
206
207            // Read remaining data.
208            stream.readFully(data, dataOffset, byteCount - 2);
209
210            // Create ImageInputStream.
211            ByteArrayInputStream bais = new ByteArrayInputStream(data);
212            is = new MemoryCacheImageInputStream(bais);
213        } else {
214            if(DEBUG) System.out.println("Reading complete stream.");
215            // The current strip or tile is a complete JPEG stream.
216            is = stream;
217        }
218
219        // Set the stream on the reader.
220        JPEGReader.setInput(is, false, true);
221
222        // Set the destination to the raw image ignoring the parameters.
223        JPEGParam.setDestination(rawImage);
224
225        // Read the strip or tile.
226        JPEGReader.read(0, JPEGParam);
227    }
228
229    protected void finalize() throws Throwable {
230        super.finalize();
231        JPEGReader.dispose();
232    }
233}