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 * 21 * @date: Jul 22, 2005 22 */ 23 24package org.apache.harmony.awt.gl.image; 25 26import java.io.IOException; 27import java.io.InputStream; 28import java.util.Hashtable; 29import java.awt.color.ColorSpace; 30import java.awt.image.*; 31import java.awt.*; 32 33import org.apache.harmony.awt.internal.nls.Messages; 34 35public class PngDecoder extends ImageDecoder { 36 // initializes proper field IDs 37 private static native void initIDs(); 38 39 static { 40 System.loadLibrary("gl"); //$NON-NLS-1$ 41 initIDs(); 42 } 43 44 private static final int hintflags = 45 ImageConsumer.SINGLEFRAME | // PNG is a static image 46 ImageConsumer.TOPDOWNLEFTRIGHT | // This order is only one possible 47 ImageConsumer.COMPLETESCANLINES; // Don't deliver incomplete scanlines 48 49 // Each pixel is a grayscale sample. 50 private static final int PNG_COLOR_TYPE_GRAY = 0; 51 // Each pixel is an R,G,B triple. 52 private static final int PNG_COLOR_TYPE_RGB = 2; 53 // Each pixel is a palette index, a PLTE chunk must appear. 54 private static final int PNG_COLOR_TYPE_PLTE = 3; 55 // Each pixel is a grayscale sample, followed by an alpha sample. 56 private static final int PNG_COLOR_TYPE_GRAY_ALPHA = 4; 57 // Each pixel is an R,G,B triple, followed by an alpha sample. 58 private static final int PNG_COLOR_TYPE_RGBA = 6; 59 60 private static final int INPUT_BUFFER_SIZE = 4096; 61 private byte buffer[] = new byte[INPUT_BUFFER_SIZE]; 62 63 // Buffers for decoded image data 64 byte byteOut[]; 65 int intOut[]; 66 67 // Native pointer to png decoder data 68 private long hNativeDecoder; 69 70 int imageWidth, imageHeight; 71 int colorType; 72 int bitDepth; 73 byte cmap[]; 74 75 boolean transferInts; // Is transfer type int?.. or byte? 76 int dataElementsPerPixel = 1; 77 78 ColorModel cm; 79 80 int updateFromScanline; // First scanline to update 81 int numScanlines; // Number of scanlines to update 82 83 private native long decode(byte[] input, int bytesInBuffer, long hDecoder); 84 85 private static native void releaseNativeDecoder(long hDecoder); 86 87 public PngDecoder(DecodingImageSource src, InputStream is) { 88 super(src, is); 89 } 90 91 @Override 92 public void decodeImage() throws IOException { 93 try { 94 int bytesRead = 0; 95 int needBytes, offset, bytesInBuffer = 0; 96 // Read from the input stream 97 for (;;) { 98 needBytes = INPUT_BUFFER_SIZE - bytesInBuffer; 99 offset = bytesInBuffer; 100 101 bytesRead = inputStream.read(buffer, offset, needBytes); 102 103 if (bytesRead < 0) { // Break, nothing to read from buffer, image truncated? 104 releaseNativeDecoder(hNativeDecoder); 105 break; 106 } 107 108 // Keep track on how much bytes left in buffer 109 bytesInBuffer += bytesRead; 110 hNativeDecoder = decode(buffer, bytesInBuffer, hNativeDecoder); 111 // PNG decoder always consumes all bytes at once 112 bytesInBuffer = 0; 113 114 // if (bytesConsumed < 0) 115 //break; // Error exit 116 117 returnData(); 118 119 // OK, we decoded all the picture in the right way... 120 if (hNativeDecoder == 0) { 121 break; 122 } 123 } 124 125 imageComplete(ImageConsumer.STATICIMAGEDONE); 126 } catch (IOException e) { 127 throw e; 128 } catch (RuntimeException e) { 129 imageComplete(ImageConsumer.IMAGEERROR); 130 throw e; 131 } finally { 132 closeStream(); 133 } 134 } 135 136 @SuppressWarnings("unused") 137 private void returnHeader() { // Called from native code 138 setDimensions(imageWidth, imageHeight); 139 140 switch (colorType) { 141 case PNG_COLOR_TYPE_GRAY: { 142 if (bitDepth != 8 && bitDepth != 4 && bitDepth != 2 && bitDepth != 1) { 143 // awt.3C=Unknown PNG color type 144 throw new IllegalArgumentException(Messages.getString("awt.3C")); //$NON-NLS-1$ 145 } 146 147 // Create gray color model 148 int numEntries = 1 << bitDepth; 149 int scaleFactor = 255 / (numEntries-1); 150 byte comps[] = new byte[numEntries]; 151 for (int i = 0; i < numEntries; i++) { 152 comps[i] = (byte) (i * scaleFactor); 153 } 154 cm = new IndexColorModel(/*bitDepth*/8, numEntries, comps, comps, comps); 155 156 transferInts = false; 157 break; 158 } 159 160 case PNG_COLOR_TYPE_RGB: { 161 if (bitDepth != 8) { 162 // awt.3C=Unknown PNG color type 163 throw new IllegalArgumentException(Messages.getString("awt.3C")); //$NON-NLS-1$ 164 } 165 166 cm = new DirectColorModel(24, 0xFF0000, 0xFF00, 0xFF); 167 168 transferInts = true; 169 break; 170 } 171 172 case PNG_COLOR_TYPE_PLTE: { 173 if (bitDepth != 8 && bitDepth != 4 && bitDepth != 2 && bitDepth != 1) { 174 // awt.3C=Unknown PNG color type 175 throw new IllegalArgumentException(Messages.getString("awt.3C")); //$NON-NLS-1$ 176 } 177 178 cm = new IndexColorModel(/*bitDepth*/8, cmap.length / 3, cmap, 0, false); 179 180 transferInts = false; 181 break; 182 } 183 184 case PNG_COLOR_TYPE_GRAY_ALPHA: { 185 if (bitDepth != 8) { 186 // awt.3C=Unknown PNG color type 187 throw new IllegalArgumentException(Messages.getString("awt.3C")); //$NON-NLS-1$ 188 } 189 190 cm = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_GRAY), 191 true, false, 192 Transparency.TRANSLUCENT, 193 DataBuffer.TYPE_BYTE); 194 195 transferInts = false; 196 dataElementsPerPixel = 2; 197 break; 198 } 199 200 case PNG_COLOR_TYPE_RGBA: { 201 if (bitDepth != 8) { 202 // awt.3C=Unknown PNG color type 203 throw new IllegalArgumentException(Messages.getString("awt.3C")); //$NON-NLS-1$ 204 } 205 206 cm = ColorModel.getRGBdefault(); 207 208 transferInts = true; 209 break; 210 } 211 default: 212 // awt.3C=Unknown PNG color type 213 throw new IllegalArgumentException(Messages.getString("awt.3C")); //$NON-NLS-1$ 214 } 215 216 // Create output buffer 217 if (transferInts) { 218 intOut = new int[imageWidth * imageHeight]; 219 } else { 220 byteOut = new byte[imageWidth * imageHeight * dataElementsPerPixel]; 221 } 222 223 setColorModel(cm); 224 225 setHints(hintflags); 226 setProperties(new Hashtable<Object, Object>()); // Empty 227 } 228 229 // Send the data to the consumer 230 private void returnData() { 231 // Send 1 or more scanlines to the consumer. 232 if (numScanlines > 0) { 233 // Native decoder could have returned 234 // some data from the next pass, handle it here 235 int pass1, pass2; 236 if (updateFromScanline + numScanlines > imageHeight) { 237 pass1 = imageHeight - updateFromScanline; 238 pass2 = updateFromScanline + numScanlines - imageHeight; 239 } else { 240 pass1 = numScanlines; 241 pass2 = 0; 242 } 243 244 transfer(updateFromScanline, pass1); 245 if (pass2 != 0) { 246 transfer(0, pass2); 247 } 248 } 249 } 250 251 private void transfer(int updateFromScanline, int numScanlines) { 252 if (transferInts) { 253 setPixels( 254 0, updateFromScanline, 255 imageWidth, numScanlines, 256 cm, intOut, 257 updateFromScanline * imageWidth, 258 imageWidth 259 ); 260 } else { 261 setPixels( 262 0, updateFromScanline, 263 imageWidth, numScanlines, 264 cm, byteOut, 265 updateFromScanline * imageWidth * dataElementsPerPixel, 266 imageWidth * dataElementsPerPixel 267 ); 268 } 269 } 270} 271