/* * Copyright 2007, The Android Open Source Project * * Licensed 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. */ package com.android.internal.awt; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import java.awt.Transparency; import java.awt.color.ColorSpace; //import java.awt.image.BufferedImage; import java.awt.image.ColorModel; import java.awt.image.ComponentColorModel; import java.awt.image.DataBuffer; import java.awt.image.DirectColorModel; import java.awt.image.ImageConsumer; import java.awt.image.IndexColorModel; import java.io.IOException; import java.io.InputStream; import java.util.Hashtable; import org.apache.harmony.awt.gl.image.DecodingImageSource; import org.apache.harmony.awt.gl.image.ImageDecoder; import org.apache.harmony.awt.internal.nls.Messages; public class AndroidImageDecoder extends ImageDecoder { 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 NB_OF_LINES_PER_CHUNK = 1; // 0 = full image Bitmap bm; // The image as decoded by Android // Header information int imageWidth; // Image size int imageHeight; int colorType; // One of the PNG_ constants from above int bitDepth; // Number of bits per color byte cmap[]; // The color palette for index color models ColorModel model; // The corresponding AWT color model boolean transferInts; // Is transfer of type int or byte? int dataElementsPerPixel; // Buffers for decoded image data byte byteOut[]; int intOut[]; public AndroidImageDecoder(DecodingImageSource src, InputStream is) { super(src, is); dataElementsPerPixel = 1; } @Override /** * All the decoding is done in Android * * AWT???: Method returns only once the image is completly * decoded; decoding is not done asynchronously */ public void decodeImage() throws IOException { try { bm = BitmapFactory.decodeStream(inputStream); if (bm == null) { throw new IOException("Input stream empty and no image cached"); } // Check size imageWidth = bm.getWidth(); imageHeight = bm.getHeight(); if (imageWidth < 0 || imageHeight < 0 ) { throw new RuntimeException("Illegal image size: " + imageWidth + ", " + imageHeight); } // We got the image fully decoded; now send all image data to AWT setDimensions(imageWidth, imageHeight); model = createColorModel(); setColorModel(model); setHints(hintflags); setProperties(new Hashtable<Object, Object>()); // Empty sendPixels(NB_OF_LINES_PER_CHUNK != 0 ? NB_OF_LINES_PER_CHUNK : imageHeight); imageComplete(ImageConsumer.STATICIMAGEDONE); } catch (IOException e) { throw e; } catch (RuntimeException e) { imageComplete(ImageConsumer.IMAGEERROR); throw e; } finally { closeStream(); } } /** * Create the AWT color model * * ???AWT: Android Bitmaps are always of type: ARGB-8888-Direct color model * * However, we leave the code here for a more powerfull decoder * that returns a native model, and the conversion is then handled * in AWT. With such a decoder, we would need to get the colorType, * the bitDepth, (and the color palette for an index color model) * from the image and construct the correct color model here. */ private ColorModel createColorModel() { ColorModel cm = null; int bmModel = 5; // TODO This doesn't exist: bm.getColorModel(); cmap = null; switch (bmModel) { // A1_MODEL case 1: colorType = PNG_COLOR_TYPE_GRAY; bitDepth = 1; break; // A8_MODEL case 2: colorType = PNG_COLOR_TYPE_GRAY_ALPHA; bitDepth = 8; break; // INDEX8_MODEL // RGB_565_MODEL // ARGB_8888_MODEL case 3: case 4: case 5: colorType = bm.hasAlpha() ? PNG_COLOR_TYPE_RGBA : PNG_COLOR_TYPE_RGB; bitDepth = 8; break; default: // awt.3C=Unknown PNG color type throw new IllegalArgumentException(Messages.getString("awt.3C")); //$NON-NLS-1$ } 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, 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$ } if (cmap == null) { throw new IllegalStateException("Palette color type is not supported"); } cm = new IndexColorModel(bitDepth, 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$ } return cm; } private void sendPixels(int nbOfLinesPerChunk) { int w = imageWidth; int h = imageHeight; int n = 1; if (nbOfLinesPerChunk > 0 && nbOfLinesPerChunk <= h) { n = nbOfLinesPerChunk; } if (transferInts) { // Create output buffer intOut = new int[w * n]; for (int yi = 0; yi < h; yi += n) { // Last chunk might contain less liness if (n > 1 && h - yi < n ) { n = h - yi; } bm.getPixels(intOut, 0, w, 0, yi, w, n); setPixels(0, yi, w, n, model, intOut, 0, w); } } else { // Android bitmaps always store ints (ARGB-8888 direct model) throw new RuntimeException("Byte transfer not supported"); } } }