blob: d16128e55fc8369571f02517bdbf103d4ad7b878 [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$
*/
/*
* Created on 18.01.2005
*/
package org.apache.harmony.awt.gl.image;
import com.android.internal.awt.AndroidImageDecoder;
import java.awt.image.ColorModel;
import java.awt.image.ImageConsumer;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ConcurrentModificationException;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
/**
* This class contains common functionality for all image decoders.
*/
public abstract class ImageDecoder {
/** Image types */
public static final int GENERIC_DECODER = 0;
public static final int JPG_DECODER = 1;
public static final int GIF_DECODER = 2;
public static final int PNG_DECODER = 3;
private static final int MAX_BYTES_IN_SIGNATURE = 8;
protected List<ImageConsumer> consumers;
protected InputStream inputStream;
protected DecodingImageSource src;
protected boolean terminated;
/**
* Chooses appropriate image decoder by looking into input stream and checking
* the image signature.
* @param src - image producer, required for passing data to it from the
* created decoder via callbacks
* @param is - stream
* @return decoder
*/
static ImageDecoder createDecoder(DecodingImageSource src, InputStream is) {
InputStream markable;
if (!is.markSupported()) {
// BEGIN android-modified
markable = new BufferedInputStream(is, 8192);
// END android-modified
} else {
markable = is;
}
// Read the signature from the stream and then reset it back
try {
markable.mark(MAX_BYTES_IN_SIGNATURE);
byte[] signature = new byte[MAX_BYTES_IN_SIGNATURE];
markable.read(signature, 0, MAX_BYTES_IN_SIGNATURE);
markable.reset();
if ((signature[0] & 0xFF) == 0xFF &&
(signature[1] & 0xFF) == 0xD8 &&
(signature[2] & 0xFF) == 0xFF) { // JPEG
return loadDecoder(PNG_DECODER, src, is);
} else if ((signature[0] & 0xFF) == 0x47 && // G
(signature[1] & 0xFF) == 0x49 && // I
(signature[2] & 0xFF) == 0x46) { // F
return loadDecoder(GIF_DECODER, src, is);
} else if ((signature[0] & 0xFF) == 137 && // PNG signature: 137 80 78 71 13 10 26 10
(signature[1] & 0xFF) == 80 &&
(signature[2] & 0xFF) == 78 &&
(signature[3] & 0xFF) == 71 &&
(signature[4] & 0xFF) == 13 &&
(signature[5] & 0xFF) == 10 &&
(signature[6] & 0xFF) == 26 &&
(signature[7] & 0xFF) == 10) {
return loadDecoder(JPG_DECODER, src, is);
}
return loadDecoder(GENERIC_DECODER, src, is);
} catch (IOException e) { // Silently
}
return null;
}
/*
* In the future, we might return different decoders for differen image types.
* But for now, we always return the generic one.
* Also: we could add a factory to load image decoder.
*/
private static ImageDecoder loadDecoder(int type, DecodingImageSource src,
InputStream is) {
return new AndroidImageDecoder(src, is);
}
protected ImageDecoder(DecodingImageSource _src, InputStream is) {
src = _src;
consumers = src.consumers;
inputStream = is;
}
public abstract void decodeImage() throws IOException;
public synchronized void closeStream() {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
}
}
}
/**
* Stops the decoding by interrupting the current decoding thread.
* Used when all consumers are removed and there's no more need to
* run the decoder.
*/
public void terminate() {
src.lockDecoder(this);
closeStream();
AccessController.doPrivileged(
new PrivilegedAction<Void>() {
public Void run() {
Thread.currentThread().interrupt();
return null;
}
}
);
terminated = true;
}
protected void setDimensions(int w, int h) {
if (terminated) {
return;
}
for (ImageConsumer ic : consumers) {
ic.setDimensions(w, h);
}
}
protected void setProperties(Hashtable<?, ?> props) {
if (terminated) {
return;
}
for (ImageConsumer ic : consumers) {
ic.setProperties(props);
}
}
protected void setColorModel(ColorModel cm) {
if (terminated) {
return;
}
for (ImageConsumer ic : consumers) {
ic.setColorModel(cm);
}
}
protected void setHints(int hints) {
if (terminated) {
return;
}
for (ImageConsumer ic : consumers) {
ic.setHints(hints);
}
}
protected void setPixels(
int x, int y,
int w, int h,
ColorModel model,
byte pix[],
int off, int scansize
) {
if (terminated) {
return;
}
src.lockDecoder(this);
for (ImageConsumer ic : consumers) {
ic.setPixels(x, y, w, h, model, pix, off, scansize);
}
}
protected void setPixels(
int x, int y,
int w, int h,
ColorModel model,
int pix[],
int off, int scansize
) {
if (terminated) {
return;
}
src.lockDecoder(this);
for (ImageConsumer ic : consumers) {
ic.setPixels(x, y, w, h, model, pix, off, scansize);
}
}
protected void imageComplete(int status) {
if (terminated) {
return;
}
src.lockDecoder(this);
ImageConsumer ic = null;
for (Iterator<ImageConsumer> i = consumers.iterator(); i.hasNext();) {
try {
ic = i.next();
} catch (ConcurrentModificationException e) {
i = consumers.iterator();
continue;
}
ic.imageComplete(status);
}
}
}