/* * @(#)QtImageDecoder.java 1.8 06/10/10 * * Copyright 1990-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 only, as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License version 2 for more details (a copy is * included at /legal/license.txt). * * You should have received a copy of the GNU General Public License * version 2 along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa * Clara, CA 95054 or visit www.sun.com if you need additional * information or have any questions. * */ package sun.awt.qt; import java.util.Vector; import java.io.InputStream; import java.io.IOException; import java.awt.image.*; import java.io.* ; import sun.awt.image.* ; /** * <code>QtImageDecoder</code> uses QT's native decoders to decode image * formats (GIF, JPEG, PNG). An instance is instantiated by * <code>QtImageDecoderFactory</code>. This class is optimized to load images * faster by utilizing the QT's native decoders. If the applications performs * image manipulation then the performance is no better than the java decoders. * We acheive high performance by allowing the QT's native decoders to * create a QImage, which we then pass it to QtImageRepresentation. We * eliminate decoding in java as well as calling <code>set*Pixels()</code>, * which gives us a significant improvemement in the image load time. */ class QtImageDecoder extends ImageDecoder { private static final DirectColorModel RGB24_DCM = new DirectColorModel(24, 0xff0000, 0xff00, 0xff) ; private static final DirectColorModel RGB32_DCM = new DirectColorModel(32, 0xff000000, 0xff0000, 0xff00, 0xff) ; /** * Image format. This is one of the constants defined in * <code>sun.awt.image.ImageDecoderFactory</code> */ protected String imgFormat ; /** * Width of the image */ protected int width ; /** * Height of the image */ protected int height ; /** * The color model used by the image */ protected ColorModel colorModel ; static { initIDs(); } QtImageDecoder(InputStreamImageSource src, InputStream is, String imgFormat) { super(src, is); this.imgFormat = imgFormat ; this.width = -1 ; this.height = -1 ; this.colorModel = null ; } public boolean sendPixels(int pixels[], int y) { int count = setPixels(0, y, pixels.length, 1, this.colorModel, pixels, 0, pixels.length); if (count <= 0) { aborted = true; } return !aborted; } public boolean sendPixels(byte pixels[], int y) { int count = setPixels(0, y, pixels.length, 1, this.colorModel, pixels, 0, pixels.length ); if (count <= 0) { aborted = true; } return !aborted; } /** * Produce an image from the stream. */ public void produceImage() throws IOException, ImageFormatException { int imageHandle = 0 ; try { /* the native decoder needs the complete compressed data * to decode. */ byte[] buffer = getEncodedImage() ; /* decode the data using platform specific decoder. The method * returns the decoded image as the return value as an opaque * object and updates the image dimension and color model * instance variables to valid values. */ imageHandle = decodeImage(buffer, buffer.length) ; if ( imageHandle == 0 ) throw new ImageFormatException("Qt Image decoding error") ; if (!aborted) { /* send image dimensions to all consumers */ setDimensions(this.width,this.height) ; /* send color model to all consumers */ setColorModel(this.colorModel) ; /* Notify directly to all internal consumers that the decoding * is done. After this call all internal consumers can start * to render the data in the image. */ int count = imageComplete(imageHandle, ImageConsumer.STATICIMAGEDONE, true); /* if there are external consumers, then we need to send * pixels using setPixels() calls for them */ if ( count > 0 ) { /* get the pixels from the native representation */ if ( this.colorModel.getPixelSize() == 8 ) { byte[] pixels = new byte[this.width] ; for ( int y = 0 ; y < this.height ; y++ ) { getPixels(imageHandle,y,pixels) ; sendPixels(pixels,y) ; } } else { int[] pixels = new int[this.width] ; for ( int y = 0 ; y < this.height ; y++ ) { getPixels(imageHandle,y,pixels) ; sendPixels(pixels,y) ; } } } imageComplete(ImageConsumer.STATICIMAGEDONE, true); } } catch (IOException e) { if (!aborted) { throw e; } } finally { close(); if ( imageHandle != 0 ) disposeImage(imageHandle) ; } } /** * Creates a byte array of the encoded/compressed image stream */ protected byte[] getEncodedImage() throws IOException { /* get the compressed data from the input stream */ BufferedInputStream bis = null ; if ( input instanceof BufferedInputStream ) bis = (BufferedInputStream)input ; else bis = new BufferedInputStream(input) ; /* get all the encoded bytes from the stream */ byte[] buffer = null ; int bytes_read = 0 ; int bytes_available = 0 ; ByteArrayOutputStream baos = null ; while ((bytes_available = bis.available()) > 0 ) { if ( buffer != null ) { /* * If we are here, then we have more data from the * stream, let us copy the previous buffer of data to * the byte array output stream */ if ( baos == null ) { baos = new ByteArrayOutputStream(bytes_read+ bytes_available); } baos.write(buffer,0,bytes_read) ; } buffer = new byte[bytes_available] ; bytes_read = bis.read(buffer) ; if ( bytes_read == -1 ) { break ; } } if ( baos != null ) { if ( bytes_read > 0 ) { baos.write(buffer,0,bytes_read) ; } buffer = baos.toByteArray() ; } return buffer ; } protected int setPixels(int x, int y, int w, int h, ColorModel model, byte pix[], int off, int scansize) { source.latchConsumers(this); ImageConsumerQueue cq = null; int count = 0; ImageConsumer consumer = null ; while ((cq = nextConsumer(cq)) != null) { consumer = cq.getConsumer() ; if ( !(consumer instanceof QtImageRepresentation) ) { consumer.setPixels(x, y, w, h, model, pix, off, scansize); count++; } } return count; } protected int setPixels(int x, int y, int w, int h, ColorModel model, int pix[], int off, int scansize) { source.latchConsumers(this); ImageConsumerQueue cq = null; int count = 0; ImageConsumer consumer = null ; while ((cq = nextConsumer(cq)) != null) { consumer = cq.getConsumer() ; if ( !(consumer instanceof QtImageRepresentation) ) { consumer.setPixels(x, y, w, h, model, pix, off, scansize); count++; } } return count; } protected int imageComplete(int imageHandle,int status, boolean done) { source.latchConsumers(this); if (done) { finished = true; source.doneDecoding(this); } ImageConsumerQueue cq = null; int count = 0 ; ImageConsumer consumer = null ; while ((cq = nextConsumer(cq)) != null) { consumer = cq.getConsumer() ; if ( consumer instanceof QtImageRepresentation ) { ((QtImageRepresentation)consumer).setNativeImage(imageHandle); consumer.imageComplete(status); } else { count ++ ; } } return count; /* count of all consumers */ } protected int imageComplete(int status, boolean done) { source.latchConsumers(this); if (done) { finished = true; source.doneDecoding(this); } ImageConsumerQueue cq = null; int count = 0; ImageConsumer consumer = null ; while ((cq = nextConsumer(cq)) != null) { consumer = cq.getConsumer() ; if ( !(consumer instanceof QtImageRepresentation) ) { consumer.imageComplete(status); count++; } } return count; } /** * Decode the image data and return a native representation of the * decoded pixels. The method should also set the basic attributes of * the image like image dimensions and color model. * * @param data encoded image stream * @param length number of bytes in the image stream, that should be * used to decode * * @return a handle that represents the decoded image */ private native int decodeImage(byte[] data, int length) ; /** * Disposes the refernce to the image handle. This should freeup any * resources allocated on the native side. * * @param imageHandle image handle */ private native void disposeImage(int imageHandle) ; /** * Get the image pixels. This is called for indexed color model images * * @param imageHandle image handle * @param y the scan line. This should be a zero based index and should be * less than the height of the image * @param pixels array to hold the pixels. This length of the should be * atleast the image's width. */ private static native void getPixels(int imageHandle,int y,byte[] pixels) ; /** * Get the image pixels. This is called for direct color model images * * @param imageHandle image handle * @param y the scan line. This should be a zero based index and should be * less than the height of the image * @param pixels array to hold the pixels. This length of the should be * atleast the image's width. */ private static native void getPixels(int imageHandle,int y,int[] pixels) ; /** * Perform class initializations */ private static native void initIDs(); }