1/* 2* Licensed to the Apache Software Foundation (ASF) under one or more 3* contributor license agreements. See the NOTICE file distributed with 4* this work for additional information regarding copyright ownership. 5* The ASF licenses this file to You under the Apache License, Version 2.0 6* (the "License"); you may not use this file except in compliance with 7* the License. You may obtain a copy of the License at 8* 9* http://www.apache.org/licenses/LICENSE-2.0 10* 11* Unless required by applicable law or agreed to in writing, software 12* distributed under the License is distributed on an "AS IS" BASIS, 13* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14* See the License for the specific language governing permissions and 15* limitations under the License. 16*/ 17/** 18 * @author Oleg V. Khaschansky 19 * @version $Revision$ 20 */ 21package org.apache.harmony.awt.gl.image; 22 23import java.awt.image.*; 24import java.awt.color.ColorSpace; 25import java.awt.*; 26import java.io.IOException; 27import java.io.InputStream; 28import java.util.Hashtable; 29 30import org.apache.harmony.awt.internal.nls.Messages; 31 32public class JpegDecoder extends ImageDecoder { 33 // Only 2 output colorspaces expected. Others are converted into 34 // these ones. 35 // 1. Grayscale 36 public static final int JCS_GRAYSCALE = 1; 37 // 2. RGB 38 public static final int JCS_RGB = 2; 39 40 // Flags for the consumer, progressive JPEG 41 private static final int hintflagsProgressive = 42 ImageConsumer.SINGLEFRAME | // JPEG is a static image 43 ImageConsumer.TOPDOWNLEFTRIGHT | // This order is only one possible 44 ImageConsumer.COMPLETESCANLINES; // Don't deliver incomplete scanlines 45 // Flags for the consumer, singlepass JPEG 46 private static final int hintflagsSingle = 47 ImageConsumer.SINGLEPASS | 48 hintflagsProgressive; 49 50 // Buffer for the stream 51 private static final int BUFFER_SIZE = 1024; 52 private byte buffer[] = new byte[BUFFER_SIZE]; 53 54 // 3 possible color models only 55 private static ColorModel cmRGB; 56 private static ColorModel cmGray; 57 58 // initializes proper field IDs 59 private static native void initIDs(); 60 61 // Pointer to native structure which store decoding state 62 // between subsequent decoding/IO-suspension cycles 63 private long hNativeDecoder = 0; // NULL initially 64 65 private boolean headerDone = false; 66 67 // Next 4 members are filled by the native method (decompress). 68 // We can simply check if imageWidth is still negative to find 69 // out if they are already filled. 70 private int imageWidth = -1; 71 private int imageHeight = -1; 72 private boolean progressive = false; 73 private int jpegColorSpace = 0; 74 75 // Stores number of bytes consumed by the native decoder 76 private int bytesConsumed = 0; 77 // Stores current scanline returned by the decoder 78 private int currScanline = 0; 79 80 private ColorModel cm = null; 81 82 static { 83 System.loadLibrary("jpegdecoder"); //$NON-NLS-1$ 84 85 cmGray = new ComponentColorModel( 86 ColorSpace.getInstance(ColorSpace.CS_GRAY), 87 false, false, 88 Transparency.OPAQUE, DataBuffer.TYPE_BYTE 89 ); 90 91 // Create RGB color model 92 cmRGB = new DirectColorModel(24, 0xFF0000, 0xFF00, 0xFF); 93 94 initIDs(); 95 } 96 97 public JpegDecoder(DecodingImageSource src, InputStream is) { 98 super(src, is); 99 } 100 101 /* 102 public JpegDecoder(InputStream iStream, ImageConsumer iConsumer) { 103 inputStream = iStream; 104 consumer = iConsumer; 105 } 106 */ 107 108 /** 109 * @return - not NULL if call is successful 110 */ 111 private native Object decode( 112 byte[] input, 113 int bytesInBuffer, 114 long hDecoder); 115 116 private static native void releaseNativeDecoder(long hDecoder); 117 118 @Override 119 public void decodeImage() throws IOException { 120 try { 121 int bytesRead = 0, dataLength = 0; 122 boolean eosReached = false; 123 int needBytes, offset, bytesInBuffer = 0; 124 byte byteOut[] = null; 125 int intOut[] = null; 126 // Read from the input stream 127 for (;;) { 128 needBytes = BUFFER_SIZE - bytesInBuffer; 129 offset = bytesInBuffer; 130 131 bytesRead = inputStream.read(buffer, offset, needBytes); 132 133 if (bytesRead < 0) { 134 bytesRead = 0;//break; 135 eosReached = true; 136 } // Don't break, maybe something left in buffer 137 138 // Keep track on how much bytes left in buffer 139 bytesInBuffer += bytesRead; 140 141 // Here we pass overall number of bytes left in the java buffer 142 // (bytesInBuffer) since jpeg decoder has its own buffer and consumes 143 // as many bytes as it can. If there are any unconsumed bytes 144 // it didn't add them to its buffer... 145 Object arr = decode( 146 buffer, 147 bytesInBuffer, 148 hNativeDecoder); 149 150 // Keep track on how much bytes left in buffer 151 bytesInBuffer -= bytesConsumed; 152 153 if (!headerDone && imageWidth != -1) { 154 returnHeader(); 155 headerDone = true; 156 } 157 158 if (bytesConsumed < 0) { 159 break; // Error exit 160 } 161 162 if (arr instanceof byte[]) { 163 byteOut = (byte[]) arr; 164 dataLength = byteOut.length; 165 returnData(byteOut, currScanline); 166 } else if (arr instanceof int[]) { 167 intOut = (int[]) arr; 168 dataLength = intOut.length; 169 returnData(intOut, currScanline); 170 } else { 171 dataLength = 0; 172 } 173 174 if (hNativeDecoder == 0) { 175 break; 176 } 177 178 if (dataLength == 0 && eosReached) { 179 releaseNativeDecoder(hNativeDecoder); 180 break; // Probably image is truncated 181 } 182 } 183 imageComplete(ImageConsumer.STATICIMAGEDONE); 184 } catch (IOException e) { 185 throw e; 186 } finally { 187 closeStream(); 188 } 189 } 190 191 public void returnHeader() { 192 setDimensions(imageWidth, imageHeight); 193 194 switch (jpegColorSpace) { 195 case JCS_GRAYSCALE: cm = cmGray; break; 196 case JCS_RGB: cm = cmRGB; break; 197 default: 198 // awt.3D=Unknown colorspace 199 throw new IllegalArgumentException(Messages.getString("awt.3D")); //$NON-NLS-1$ 200 } 201 setColorModel(cm); 202 203 setHints(progressive ? hintflagsProgressive : hintflagsSingle); 204 205 setProperties(new Hashtable<Object, Object>()); // Empty 206 } 207 208 // Send the data to the consumer 209 public void returnData(int data[], int currScanLine) { 210 // Send 1 or more scanlines to the consumer. 211 int numScanlines = data.length / imageWidth; 212 if (numScanlines > 0) { 213 setPixels( 214 0, currScanLine - numScanlines, 215 imageWidth, numScanlines, 216 cm, data, 0, imageWidth 217 ); 218 } 219 } 220 221 public void returnData(byte data[], int currScanLine) { 222 int numScanlines = data.length / imageWidth; 223 if (numScanlines > 0) { 224 setPixels( 225 0, currScanLine - numScanlines, 226 imageWidth, numScanlines, 227 cm, data, 0, imageWidth 228 ); 229 } 230 } 231} 232