001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: /**
018: * @author Oleg V. Khaschansky
019: * @version $Revision$
020: */package org.apache.harmony.awt.gl.image;
021:
022: import java.awt.image.*;
023: import java.awt.color.ColorSpace;
024: import java.awt.*;
025: import java.io.IOException;
026: import java.io.InputStream;
027: import java.util.Hashtable;
028:
029: import org.apache.harmony.awt.internal.nls.Messages;
030:
031: public class JpegDecoder extends ImageDecoder {
032: // Only 2 output colorspaces expected. Others are converted into
033: // these ones.
034: // 1. Grayscale
035: public static final int JCS_GRAYSCALE = 1;
036: // 2. RGB
037: public static final int JCS_RGB = 2;
038:
039: // Flags for the consumer, progressive JPEG
040: private static final int hintflagsProgressive = ImageConsumer.SINGLEFRAME
041: | // JPEG is a static image
042: ImageConsumer.TOPDOWNLEFTRIGHT | // This order is only one possible
043: ImageConsumer.COMPLETESCANLINES; // Don't deliver incomplete scanlines
044: // Flags for the consumer, singlepass JPEG
045: private static final int hintflagsSingle = ImageConsumer.SINGLEPASS
046: | hintflagsProgressive;
047:
048: // Buffer for the stream
049: private static final int MIN_BUFFER_SIZE = 1024;
050: private static final int MAX_BUFFER_SIZE = 2097152;
051: private int buffer_size;
052: private byte buffer[];
053:
054: // 3 possible color models only
055: private static ColorModel cmRGB;
056: private static ColorModel cmGray;
057:
058: // initializes proper field IDs
059: private static native void initIDs();
060:
061: // Pointer to native structure which store decoding state
062: // between subsequent decoding/IO-suspension cycles
063: private long hNativeDecoder = 0; // NULL initially
064:
065: private boolean headerDone = false;
066:
067: // Next 4 members are filled by the native method (decompress).
068: // We can simply check if imageWidth is still negative to find
069: // out if they are already filled.
070: private int imageWidth = -1;
071: private int imageHeight = -1;
072: private boolean progressive = false;
073: private int jpegColorSpace = 0;
074:
075: // Stores number of bytes consumed by the native decoder
076: private int bytesConsumed = 0;
077: // Stores current scanline returned by the decoder
078: private int currScanline = 0;
079:
080: private ColorModel cm = null;
081:
082: static {
083: System.loadLibrary("jpegdecoder"); //$NON-NLS-1$
084:
085: cmGray = new ComponentColorModel(ColorSpace
086: .getInstance(ColorSpace.CS_GRAY), false, false,
087: Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
088:
089: // Create RGB color model
090: cmRGB = new DirectColorModel(24, 0xFF0000, 0xFF00, 0xFF);
091:
092: initIDs();
093: }
094:
095: public JpegDecoder(DecodingImageSource src, InputStream is) {
096: super (src, is);
097: try {
098: int available_bytes = is.available();
099: if (available_bytes < MIN_BUFFER_SIZE) {
100: buffer_size = MIN_BUFFER_SIZE;
101: } else if (available_bytes > MAX_BUFFER_SIZE) {
102: buffer_size = MAX_BUFFER_SIZE;
103: } else {
104: buffer_size = available_bytes;
105: }
106: } catch (IOException e) {
107: buffer_size = MIN_BUFFER_SIZE;
108: }
109: buffer = new byte[buffer_size];
110: }
111:
112: /*
113: public JpegDecoder(InputStream iStream, ImageConsumer iConsumer) {
114: inputStream = iStream;
115: consumer = iConsumer;
116: }
117: */
118:
119: /**
120: * @return - not NULL if call is successful
121: */
122: private native Object decode(byte[] input, int bytesInBuffer,
123: long hDecoder);
124:
125: private static native void releaseNativeDecoder(long hDecoder);
126:
127: @Override
128: public void decodeImage() throws IOException {
129: try {
130: int bytesRead = 0, dataLength = 0;
131: boolean eosReached = false;
132: int needBytes, offset, bytesInBuffer = 0;
133: byte byteOut[] = null;
134: int intOut[] = null;
135: // Read from the input stream
136: for (;;) {
137: needBytes = buffer_size - bytesInBuffer;
138: offset = bytesInBuffer;
139:
140: bytesRead = inputStream.read(buffer, offset, needBytes);
141:
142: if (bytesRead < 0) {
143: bytesRead = 0;//break;
144: eosReached = true;
145: } // Don't break, maybe something left in buffer
146:
147: // Keep track on how much bytes left in buffer
148: bytesInBuffer += bytesRead;
149:
150: // Here we pass overall number of bytes left in the java buffer
151: // (bytesInBuffer) since jpeg decoder has its own buffer and consumes
152: // as many bytes as it can. If there are any unconsumed bytes
153: // it didn't add them to its buffer...
154: Object arr = decode(buffer, bytesInBuffer,
155: hNativeDecoder);
156:
157: // Keep track on how much bytes left in buffer
158: bytesInBuffer -= bytesConsumed;
159:
160: if (!headerDone && imageWidth != -1) {
161: returnHeader();
162: headerDone = true;
163: }
164:
165: if (bytesConsumed < 0) {
166: break; // Error exit
167: }
168:
169: if (arr instanceof byte[]) {
170: byteOut = (byte[]) arr;
171: dataLength = byteOut.length;
172: returnData(byteOut, currScanline);
173: } else if (arr instanceof int[]) {
174: intOut = (int[]) arr;
175: dataLength = intOut.length;
176: returnData(intOut, currScanline);
177: } else {
178: dataLength = 0;
179: }
180:
181: if (hNativeDecoder == 0) {
182: break;
183: }
184:
185: if (dataLength == 0 && eosReached) {
186: releaseNativeDecoder(hNativeDecoder);
187: break; // Probably image is truncated
188: }
189: }
190: imageComplete(ImageConsumer.STATICIMAGEDONE);
191: } catch (IOException e) {
192: throw e;
193: } finally {
194: closeStream();
195: }
196: }
197:
198: public void returnHeader() {
199: setDimensions(imageWidth, imageHeight);
200:
201: switch (jpegColorSpace) {
202: case JCS_GRAYSCALE:
203: cm = cmGray;
204: break;
205: case JCS_RGB:
206: cm = cmRGB;
207: break;
208: default:
209: // awt.3D=Unknown colorspace
210: throw new IllegalArgumentException(Messages
211: .getString("awt.3D")); //$NON-NLS-1$
212: }
213: setColorModel(cm);
214:
215: setHints(progressive ? hintflagsProgressive : hintflagsSingle);
216:
217: setProperties(new Hashtable<Object, Object>()); // Empty
218: }
219:
220: // Send the data to the consumer
221: public void returnData(int data[], int currScanLine) {
222: // Send 1 or more scanlines to the consumer.
223: int numScanlines = data.length / imageWidth;
224: if (numScanlines > 0) {
225: setPixels(0, currScanLine - numScanlines, imageWidth,
226: numScanlines, cm, data, 0, imageWidth);
227: }
228: }
229:
230: public void returnData(byte data[], int currScanLine) {
231: int numScanlines = data.length / imageWidth;
232: if (numScanlines > 0) {
233: setPixels(0, currScanLine - numScanlines, imageWidth,
234: numScanlines, cm, data, 0, imageWidth);
235: }
236: }
237: }
|