/* * 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); } } }