blob: 7e85600ac7106435faf2f4c276628ca5ad254b54 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @author Oleg V. Khaschansky
* @version $Revision$
*
* @date: Jul 22, 2005
*/
package org.apache.harmony.awt.gl.image;
import java.io.IOException;
import java.io.InputStream;
import java.util.Hashtable;
import java.awt.color.ColorSpace;
import java.awt.image.*;
import java.awt.*;
import org.apache.harmony.awt.internal.nls.Messages;
public class PngDecoder extends ImageDecoder {
// initializes proper field IDs
private static native void initIDs();
static {
System.loadLibrary("gl"); //$NON-NLS-1$
initIDs();
}
private static final int hintflags =
ImageConsumer.SINGLEFRAME | // PNG is a static image
ImageConsumer.TOPDOWNLEFTRIGHT | // This order is only one possible
ImageConsumer.COMPLETESCANLINES; // Don't deliver incomplete scanlines
// Each pixel is a grayscale sample.
private static final int PNG_COLOR_TYPE_GRAY = 0;
// Each pixel is an R,G,B triple.
private static final int PNG_COLOR_TYPE_RGB = 2;
// Each pixel is a palette index, a PLTE chunk must appear.
private static final int PNG_COLOR_TYPE_PLTE = 3;
// Each pixel is a grayscale sample, followed by an alpha sample.
private static final int PNG_COLOR_TYPE_GRAY_ALPHA = 4;
// Each pixel is an R,G,B triple, followed by an alpha sample.
private static final int PNG_COLOR_TYPE_RGBA = 6;
private static final int INPUT_BUFFER_SIZE = 4096;
private byte buffer[] = new byte[INPUT_BUFFER_SIZE];
// Buffers for decoded image data
byte byteOut[];
int intOut[];
// Native pointer to png decoder data
private long hNativeDecoder;
int imageWidth, imageHeight;
int colorType;
int bitDepth;
byte cmap[];
boolean transferInts; // Is transfer type int?.. or byte?
int dataElementsPerPixel = 1;
ColorModel cm;
int updateFromScanline; // First scanline to update
int numScanlines; // Number of scanlines to update
private native long decode(byte[] input, int bytesInBuffer, long hDecoder);
private static native void releaseNativeDecoder(long hDecoder);
public PngDecoder(DecodingImageSource src, InputStream is) {
super(src, is);
}
@Override
public void decodeImage() throws IOException {
try {
int bytesRead = 0;
int needBytes, offset, bytesInBuffer = 0;
// Read from the input stream
for (;;) {
needBytes = INPUT_BUFFER_SIZE - bytesInBuffer;
offset = bytesInBuffer;
bytesRead = inputStream.read(buffer, offset, needBytes);
if (bytesRead < 0) { // Break, nothing to read from buffer, image truncated?
releaseNativeDecoder(hNativeDecoder);
break;
}
// Keep track on how much bytes left in buffer
bytesInBuffer += bytesRead;
hNativeDecoder = decode(buffer, bytesInBuffer, hNativeDecoder);
// PNG decoder always consumes all bytes at once
bytesInBuffer = 0;
// if (bytesConsumed < 0)
//break; // Error exit
returnData();
// OK, we decoded all the picture in the right way...
if (hNativeDecoder == 0) {
break;
}
}
imageComplete(ImageConsumer.STATICIMAGEDONE);
} catch (IOException e) {
throw e;
} catch (RuntimeException e) {
imageComplete(ImageConsumer.IMAGEERROR);
throw e;
} finally {
closeStream();
}
}
@SuppressWarnings("unused")
private void returnHeader() { // Called from native code
setDimensions(imageWidth, imageHeight);
switch (colorType) {
case PNG_COLOR_TYPE_GRAY: {
if (bitDepth != 8 && bitDepth != 4 && bitDepth != 2 && bitDepth != 1) {
// awt.3C=Unknown PNG color type
throw new IllegalArgumentException(Messages.getString("awt.3C")); //$NON-NLS-1$
}
// Create gray color model
int numEntries = 1 << bitDepth;
int scaleFactor = 255 / (numEntries-1);
byte comps[] = new byte[numEntries];
for (int i = 0; i < numEntries; i++) {
comps[i] = (byte) (i * scaleFactor);
}
cm = new IndexColorModel(/*bitDepth*/8, numEntries, comps, comps, comps);
transferInts = false;
break;
}
case PNG_COLOR_TYPE_RGB: {
if (bitDepth != 8) {
// awt.3C=Unknown PNG color type
throw new IllegalArgumentException(Messages.getString("awt.3C")); //$NON-NLS-1$
}
cm = new DirectColorModel(24, 0xFF0000, 0xFF00, 0xFF);
transferInts = true;
break;
}
case PNG_COLOR_TYPE_PLTE: {
if (bitDepth != 8 && bitDepth != 4 && bitDepth != 2 && bitDepth != 1) {
// awt.3C=Unknown PNG color type
throw new IllegalArgumentException(Messages.getString("awt.3C")); //$NON-NLS-1$
}
cm = new IndexColorModel(/*bitDepth*/8, cmap.length / 3, cmap, 0, false);
transferInts = false;
break;
}
case PNG_COLOR_TYPE_GRAY_ALPHA: {
if (bitDepth != 8) {
// awt.3C=Unknown PNG color type
throw new IllegalArgumentException(Messages.getString("awt.3C")); //$NON-NLS-1$
}
cm = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_GRAY),
true, false,
Transparency.TRANSLUCENT,
DataBuffer.TYPE_BYTE);
transferInts = false;
dataElementsPerPixel = 2;
break;
}
case PNG_COLOR_TYPE_RGBA: {
if (bitDepth != 8) {
// awt.3C=Unknown PNG color type
throw new IllegalArgumentException(Messages.getString("awt.3C")); //$NON-NLS-1$
}
cm = ColorModel.getRGBdefault();
transferInts = true;
break;
}
default:
// awt.3C=Unknown PNG color type
throw new IllegalArgumentException(Messages.getString("awt.3C")); //$NON-NLS-1$
}
// Create output buffer
if (transferInts) {
intOut = new int[imageWidth * imageHeight];
} else {
byteOut = new byte[imageWidth * imageHeight * dataElementsPerPixel];
}
setColorModel(cm);
setHints(hintflags);
setProperties(new Hashtable<Object, Object>()); // Empty
}
// Send the data to the consumer
private void returnData() {
// Send 1 or more scanlines to the consumer.
if (numScanlines > 0) {
// Native decoder could have returned
// some data from the next pass, handle it here
int pass1, pass2;
if (updateFromScanline + numScanlines > imageHeight) {
pass1 = imageHeight - updateFromScanline;
pass2 = updateFromScanline + numScanlines - imageHeight;
} else {
pass1 = numScanlines;
pass2 = 0;
}
transfer(updateFromScanline, pass1);
if (pass2 != 0) {
transfer(0, pass2);
}
}
}
private void transfer(int updateFromScanline, int numScanlines) {
if (transferInts) {
setPixels(
0, updateFromScanline,
imageWidth, numScanlines,
cm, intOut,
updateFromScanline * imageWidth,
imageWidth
);
} else {
setPixels(
0, updateFromScanline,
imageWidth, numScanlines,
cm, byteOut,
updateFromScanline * imageWidth * dataElementsPerPixel,
imageWidth * dataElementsPerPixel
);
}
}
}