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: *
021: * @date: Jul 22, 2005
022: */package org.apache.harmony.awt.gl.image;
023:
024: import java.io.IOException;
025: import java.io.InputStream;
026: import java.util.Hashtable;
027: import java.awt.color.ColorSpace;
028: import java.awt.image.*;
029: import java.awt.*;
030:
031: import org.apache.harmony.awt.internal.nls.Messages;
032:
033: public class PngDecoder extends ImageDecoder {
034: // initializes proper field IDs
035: private static native void initIDs();
036:
037: static {
038: System.loadLibrary("gl"); //$NON-NLS-1$
039: initIDs();
040: }
041:
042: private static final int hintflags = ImageConsumer.SINGLEFRAME | // PNG is a static image
043: ImageConsumer.TOPDOWNLEFTRIGHT | // This order is only one possible
044: ImageConsumer.COMPLETESCANLINES; // Don't deliver incomplete scanlines
045:
046: // Each pixel is a grayscale sample.
047: private static final int PNG_COLOR_TYPE_GRAY = 0;
048: // Each pixel is an R,G,B triple.
049: private static final int PNG_COLOR_TYPE_RGB = 2;
050: // Each pixel is a palette index, a PLTE chunk must appear.
051: private static final int PNG_COLOR_TYPE_PLTE = 3;
052: // Each pixel is a grayscale sample, followed by an alpha sample.
053: private static final int PNG_COLOR_TYPE_GRAY_ALPHA = 4;
054: // Each pixel is an R,G,B triple, followed by an alpha sample.
055: private static final int PNG_COLOR_TYPE_RGBA = 6;
056:
057: private static final int MIN_BUFFER_SIZE = 4096;
058: private static final int MAX_BUFFER_SIZE = 2097152;
059: private int buffer_size;
060: private byte buffer[];
061:
062: // Buffers for decoded image data
063: byte byteOut[];
064: int intOut[];
065:
066: // Native pointer to png decoder data
067: private long hNativeDecoder;
068:
069: int imageWidth, imageHeight;
070: int colorType;
071: int bitDepth;
072: byte cmap[];
073:
074: boolean transferInts; // Is transfer type int?.. or byte?
075: int dataElementsPerPixel = 1;
076:
077: ColorModel cm;
078:
079: int updateFromScanline; // First scanline to update
080: int numScanlines; // Number of scanlines to update
081:
082: private native long decode(byte[] input, int bytesInBuffer,
083: long hDecoder);
084:
085: private static native void releaseNativeDecoder(long hDecoder);
086:
087: public PngDecoder(DecodingImageSource src, InputStream is) {
088: super (src, is);
089: try {
090: int available_bytes = is.available();
091: if (available_bytes < MIN_BUFFER_SIZE) {
092: buffer_size = MIN_BUFFER_SIZE;
093: } else if (available_bytes > MAX_BUFFER_SIZE) {
094: buffer_size = MAX_BUFFER_SIZE;
095: } else {
096: buffer_size = available_bytes;
097: }
098: } catch (IOException e) {
099: buffer_size = MIN_BUFFER_SIZE;
100: }
101: buffer = new byte[buffer_size];
102: }
103:
104: @Override
105: public void decodeImage() throws IOException {
106: try {
107: int bytesRead = 0;
108: int needBytes, offset, bytesInBuffer = 0;
109: // Read from the input stream
110: for (;;) {
111: needBytes = buffer_size - bytesInBuffer;
112: offset = bytesInBuffer;
113:
114: bytesRead = inputStream.read(buffer, offset, needBytes);
115:
116: if (bytesRead < 0) { // Break, nothing to read from buffer, image truncated?
117: releaseNativeDecoder(hNativeDecoder);
118: break;
119: }
120:
121: // Keep track on how much bytes left in buffer
122: bytesInBuffer += bytesRead;
123: hNativeDecoder = decode(buffer, bytesInBuffer,
124: hNativeDecoder);
125: // PNG decoder always consumes all bytes at once
126: bytesInBuffer = 0;
127:
128: // if (bytesConsumed < 0)
129: //break; // Error exit
130:
131: returnData();
132:
133: // OK, we decoded all the picture in the right way...
134: if (hNativeDecoder == 0) {
135: break;
136: }
137: }
138:
139: imageComplete(ImageConsumer.STATICIMAGEDONE);
140: } catch (IOException e) {
141: throw e;
142: } catch (RuntimeException e) {
143: imageComplete(ImageConsumer.IMAGEERROR);
144: throw e;
145: } finally {
146: closeStream();
147: }
148: }
149:
150: @SuppressWarnings("unused")
151: private void returnHeader() { // Called from native code
152: setDimensions(imageWidth, imageHeight);
153:
154: switch (colorType) {
155: case PNG_COLOR_TYPE_GRAY: {
156: if (bitDepth != 8 && bitDepth != 4 && bitDepth != 2
157: && bitDepth != 1) {
158: // awt.3C=Unknown PNG color type
159: throw new IllegalArgumentException(Messages
160: .getString("awt.3C")); //$NON-NLS-1$
161: }
162:
163: // Create gray color model
164: int numEntries = 1 << bitDepth;
165: int scaleFactor = 255 / (numEntries - 1);
166: byte comps[] = new byte[numEntries];
167: for (int i = 0; i < numEntries; i++) {
168: comps[i] = (byte) (i * scaleFactor);
169: }
170: cm = new IndexColorModel(/*bitDepth*/8, numEntries, comps,
171: comps, comps);
172:
173: transferInts = false;
174: break;
175: }
176:
177: case PNG_COLOR_TYPE_RGB: {
178: if (bitDepth != 8) {
179: // awt.3C=Unknown PNG color type
180: throw new IllegalArgumentException(Messages
181: .getString("awt.3C")); //$NON-NLS-1$
182: }
183:
184: cm = new DirectColorModel(24, 0xFF0000, 0xFF00, 0xFF);
185:
186: transferInts = true;
187: break;
188: }
189:
190: case PNG_COLOR_TYPE_PLTE: {
191: if (bitDepth != 8 && bitDepth != 4 && bitDepth != 2
192: && bitDepth != 1) {
193: // awt.3C=Unknown PNG color type
194: throw new IllegalArgumentException(Messages
195: .getString("awt.3C")); //$NON-NLS-1$
196: }
197:
198: cm = new IndexColorModel(/*bitDepth*/8, cmap.length / 3,
199: cmap, 0, false);
200:
201: transferInts = false;
202: break;
203: }
204:
205: case PNG_COLOR_TYPE_GRAY_ALPHA: {
206: if (bitDepth != 8) {
207: // awt.3C=Unknown PNG color type
208: throw new IllegalArgumentException(Messages
209: .getString("awt.3C")); //$NON-NLS-1$
210: }
211:
212: cm = new ComponentColorModel(ColorSpace
213: .getInstance(ColorSpace.CS_GRAY), true, false,
214: Transparency.TRANSLUCENT, DataBuffer.TYPE_BYTE);
215:
216: transferInts = false;
217: dataElementsPerPixel = 2;
218: break;
219: }
220:
221: case PNG_COLOR_TYPE_RGBA: {
222: if (bitDepth != 8) {
223: // awt.3C=Unknown PNG color type
224: throw new IllegalArgumentException(Messages
225: .getString("awt.3C")); //$NON-NLS-1$
226: }
227:
228: cm = ColorModel.getRGBdefault();
229:
230: transferInts = true;
231: break;
232: }
233: default:
234: // awt.3C=Unknown PNG color type
235: throw new IllegalArgumentException(Messages
236: .getString("awt.3C")); //$NON-NLS-1$
237: }
238:
239: // Create output buffer
240: if (transferInts) {
241: intOut = new int[imageWidth * imageHeight];
242: } else {
243: byteOut = new byte[imageWidth * imageHeight
244: * dataElementsPerPixel];
245: }
246:
247: setColorModel(cm);
248:
249: setHints(hintflags);
250: setProperties(new Hashtable<Object, Object>()); // Empty
251: }
252:
253: // Send the data to the consumer
254: private void returnData() {
255: // Send 1 or more scanlines to the consumer.
256: if (numScanlines > 0) {
257: // Native decoder could have returned
258: // some data from the next pass, handle it here
259: int pass1, pass2;
260: if (updateFromScanline + numScanlines > imageHeight) {
261: pass1 = imageHeight - updateFromScanline;
262: pass2 = updateFromScanline + numScanlines - imageHeight;
263: } else {
264: pass1 = numScanlines;
265: pass2 = 0;
266: }
267:
268: transfer(updateFromScanline, pass1);
269: if (pass2 != 0) {
270: transfer(0, pass2);
271: }
272: }
273: }
274:
275: private void transfer(int updateFromScanline, int numScanlines) {
276: if (transferInts) {
277: setPixels(0, updateFromScanline, imageWidth, numScanlines,
278: cm, intOut, updateFromScanline * imageWidth,
279: imageWidth);
280: } else {
281: setPixels(0, updateFromScanline, imageWidth, numScanlines,
282: cm, byteOut, updateFromScanline * imageWidth
283: * dataElementsPerPixel, imageWidth
284: * dataElementsPerPixel);
285: }
286: }
287: }
|