001: /*
002: * @(#)QtImageDecoder.java 1.8 06/10/10
003: *
004: * Copyright 1990-2006 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: *
026: */
027:
028: package sun.awt.qt;
029:
030: import java.util.Vector;
031: import java.io.InputStream;
032: import java.io.IOException;
033: import java.awt.image.*;
034:
035: import java.io.*;
036: import sun.awt.image.*;
037:
038: /**
039: * <code>QtImageDecoder</code> uses QT's native decoders to decode image
040: * formats (GIF, JPEG, PNG). An instance is instantiated by
041: * <code>QtImageDecoderFactory</code>. This class is optimized to load images
042: * faster by utilizing the QT's native decoders. If the applications performs
043: * image manipulation then the performance is no better than the java decoders.
044: * We acheive high performance by allowing the QT's native decoders to
045: * create a QImage, which we then pass it to QtImageRepresentation. We
046: * eliminate decoding in java as well as calling <code>set*Pixels()</code>,
047: * which gives us a significant improvemement in the image load time.
048: */
049: class QtImageDecoder extends ImageDecoder {
050: private static final DirectColorModel RGB24_DCM = new DirectColorModel(
051: 24, 0xff0000, 0xff00, 0xff);
052: private static final DirectColorModel RGB32_DCM = new DirectColorModel(
053: 32, 0xff000000, 0xff0000, 0xff00, 0xff);
054:
055: /**
056: * Image format. This is one of the constants defined in
057: * <code>sun.awt.image.ImageDecoderFactory</code>
058: */
059: protected String imgFormat;
060:
061: /**
062: * Width of the image
063: */
064: protected int width;
065:
066: /**
067: * Height of the image
068: */
069: protected int height;
070:
071: /**
072: * The color model used by the image
073: */
074: protected ColorModel colorModel;
075:
076: static {
077: initIDs();
078: }
079:
080: QtImageDecoder(InputStreamImageSource src, InputStream is,
081: String imgFormat) {
082: super (src, is);
083: this .imgFormat = imgFormat;
084: this .width = -1;
085: this .height = -1;
086: this .colorModel = null;
087: }
088:
089: public boolean sendPixels(int pixels[], int y) {
090: int count = setPixels(0, y, pixels.length, 1, this .colorModel,
091: pixels, 0, pixels.length);
092: if (count <= 0) {
093: aborted = true;
094: }
095: return !aborted;
096: }
097:
098: public boolean sendPixels(byte pixels[], int y) {
099: int count = setPixels(0, y, pixels.length, 1, this .colorModel,
100: pixels, 0, pixels.length);
101: if (count <= 0) {
102: aborted = true;
103: }
104: return !aborted;
105: }
106:
107: /**
108: * Produce an image from the stream.
109: */
110: public void produceImage() throws IOException, ImageFormatException {
111: int imageHandle = 0;
112: try {
113: /* the native decoder needs the complete compressed data
114: * to decode.
115: */
116: byte[] buffer = getEncodedImage();
117:
118: /* decode the data using platform specific decoder. The method
119: * returns the decoded image as the return value as an opaque
120: * object and updates the image dimension and color model
121: * instance variables to valid values.
122: */
123: imageHandle = decodeImage(buffer, buffer.length);
124: if (imageHandle == 0)
125: throw new ImageFormatException(
126: "Qt Image decoding error");
127: if (!aborted) {
128: /* send image dimensions to all consumers */
129: setDimensions(this .width, this .height);
130:
131: /* send color model to all consumers */
132: setColorModel(this .colorModel);
133:
134: /* Notify directly to all internal consumers that the decoding
135: * is done. After this call all internal consumers can start
136: * to render the data in the image.
137: */
138: int count = imageComplete(imageHandle,
139: ImageConsumer.STATICIMAGEDONE, true);
140:
141: /* if there are external consumers, then we need to send
142: * pixels using setPixels() calls for them
143: */
144: if (count > 0) {
145: /* get the pixels from the native representation */
146: if (this .colorModel.getPixelSize() == 8) {
147: byte[] pixels = new byte[this .width];
148: for (int y = 0; y < this .height; y++) {
149: getPixels(imageHandle, y, pixels);
150: sendPixels(pixels, y);
151: }
152: } else {
153: int[] pixels = new int[this .width];
154: for (int y = 0; y < this .height; y++) {
155: getPixels(imageHandle, y, pixels);
156: sendPixels(pixels, y);
157: }
158: }
159: }
160:
161: imageComplete(ImageConsumer.STATICIMAGEDONE, true);
162: }
163: } catch (IOException e) {
164: if (!aborted) {
165: throw e;
166: }
167: } finally {
168: close();
169: if (imageHandle != 0)
170: disposeImage(imageHandle);
171: }
172: }
173:
174: /**
175: * Creates a byte array of the encoded/compressed image stream
176: */
177: protected byte[] getEncodedImage() throws IOException {
178: /* get the compressed data from the input stream */
179: BufferedInputStream bis = null;
180: if (input instanceof BufferedInputStream)
181: bis = (BufferedInputStream) input;
182: else
183: bis = new BufferedInputStream(input);
184:
185: /* get all the encoded bytes from the stream
186: */
187: byte[] buffer = null;
188: int bytes_read = 0;
189: int bytes_available = 0;
190: ByteArrayOutputStream baos = null;
191:
192: while ((bytes_available = bis.available()) > 0) {
193: if (buffer != null) {
194: /*
195: * If we are here, then we have more data from the
196: * stream, let us copy the previous buffer of data to
197: * the byte array output stream
198: */
199: if (baos == null) {
200: baos = new ByteArrayOutputStream(bytes_read
201: + bytes_available);
202: }
203: baos.write(buffer, 0, bytes_read);
204: }
205: buffer = new byte[bytes_available];
206: bytes_read = bis.read(buffer);
207: if (bytes_read == -1) {
208: break;
209: }
210: }
211:
212: if (baos != null) {
213: if (bytes_read > 0) {
214: baos.write(buffer, 0, bytes_read);
215: }
216: buffer = baos.toByteArray();
217: }
218:
219: return buffer;
220: }
221:
222: protected int setPixels(int x, int y, int w, int h,
223: ColorModel model, byte pix[], int off, int scansize) {
224: source.latchConsumers(this );
225: ImageConsumerQueue cq = null;
226: int count = 0;
227: ImageConsumer consumer = null;
228: while ((cq = nextConsumer(cq)) != null) {
229: consumer = cq.getConsumer();
230: if (!(consumer instanceof QtImageRepresentation)) {
231: consumer.setPixels(x, y, w, h, model, pix, off,
232: scansize);
233: count++;
234: }
235: }
236: return count;
237: }
238:
239: protected int setPixels(int x, int y, int w, int h,
240: ColorModel model, int pix[], int off, int scansize) {
241: source.latchConsumers(this );
242: ImageConsumerQueue cq = null;
243: int count = 0;
244: ImageConsumer consumer = null;
245: while ((cq = nextConsumer(cq)) != null) {
246: consumer = cq.getConsumer();
247: if (!(consumer instanceof QtImageRepresentation)) {
248: consumer.setPixels(x, y, w, h, model, pix, off,
249: scansize);
250: count++;
251: }
252: }
253: return count;
254: }
255:
256: protected int imageComplete(int imageHandle, int status,
257: boolean done) {
258: source.latchConsumers(this );
259: if (done) {
260: finished = true;
261: source.doneDecoding(this );
262: }
263: ImageConsumerQueue cq = null;
264: int count = 0;
265: ImageConsumer consumer = null;
266: while ((cq = nextConsumer(cq)) != null) {
267: consumer = cq.getConsumer();
268: if (consumer instanceof QtImageRepresentation) {
269: ((QtImageRepresentation) consumer)
270: .setNativeImage(imageHandle);
271: consumer.imageComplete(status);
272: } else {
273: count++;
274: }
275: }
276: return count; /* count of all consumers */
277: }
278:
279: protected int imageComplete(int status, boolean done) {
280: source.latchConsumers(this );
281: if (done) {
282: finished = true;
283: source.doneDecoding(this );
284: }
285: ImageConsumerQueue cq = null;
286: int count = 0;
287: ImageConsumer consumer = null;
288: while ((cq = nextConsumer(cq)) != null) {
289: consumer = cq.getConsumer();
290: if (!(consumer instanceof QtImageRepresentation)) {
291: consumer.imageComplete(status);
292: count++;
293: }
294: }
295: return count;
296: }
297:
298: /**
299: * Decode the image data and return a native representation of the
300: * decoded pixels. The method should also set the basic attributes of
301: * the image like image dimensions and color model.
302: *
303: * @param data encoded image stream
304: * @param length number of bytes in the image stream, that should be
305: * used to decode
306: *
307: * @return a handle that represents the decoded image
308: */
309: private native int decodeImage(byte[] data, int length);
310:
311: /**
312: * Disposes the refernce to the image handle. This should freeup any
313: * resources allocated on the native side.
314: *
315: * @param imageHandle image handle
316: */
317: private native void disposeImage(int imageHandle);
318:
319: /**
320: * Get the image pixels. This is called for indexed color model images
321: *
322: * @param imageHandle image handle
323: * @param y the scan line. This should be a zero based index and should be
324: * less than the height of the image
325: * @param pixels array to hold the pixels. This length of the should be
326: * atleast the image's width.
327: */
328: private static native void getPixels(int imageHandle, int y,
329: byte[] pixels);
330:
331: /**
332: * Get the image pixels. This is called for direct color model images
333: *
334: * @param imageHandle image handle
335: * @param y the scan line. This should be a zero based index and should be
336: * less than the height of the image
337: * @param pixels array to hold the pixels. This length of the should be
338: * atleast the image's width.
339: */
340: private static native void getPixels(int imageHandle, int y,
341: int[] pixels);
342:
343: /**
344: * Perform class initializations
345: */
346: private static native void initIDs();
347: }
|