1/* 2 * Copyright 2007, The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16package com.android.internal.awt; 17 18import android.graphics.Bitmap; 19import android.graphics.BitmapFactory; 20 21import java.awt.Transparency; 22import java.awt.color.ColorSpace; 23//import java.awt.image.BufferedImage; 24import java.awt.image.ColorModel; 25import java.awt.image.ComponentColorModel; 26import java.awt.image.DataBuffer; 27import java.awt.image.DirectColorModel; 28import java.awt.image.ImageConsumer; 29import java.awt.image.IndexColorModel; 30import java.io.IOException; 31import java.io.InputStream; 32import java.util.Hashtable; 33 34import org.apache.harmony.awt.gl.image.DecodingImageSource; 35import org.apache.harmony.awt.gl.image.ImageDecoder; 36import org.apache.harmony.awt.internal.nls.Messages; 37 38public class AndroidImageDecoder extends ImageDecoder { 39 40 private static final int hintflags = 41 ImageConsumer.SINGLEFRAME | // PNG is a static image 42 ImageConsumer.TOPDOWNLEFTRIGHT | // This order is only one possible 43 ImageConsumer.COMPLETESCANLINES; // Don't deliver incomplete scanlines 44 45 // Each pixel is a grayscale sample. 46 private static final int PNG_COLOR_TYPE_GRAY = 0; 47 // Each pixel is an R,G,B triple. 48 private static final int PNG_COLOR_TYPE_RGB = 2; 49 // Each pixel is a palette index, a PLTE chunk must appear. 50 private static final int PNG_COLOR_TYPE_PLTE = 3; 51 // Each pixel is a grayscale sample, followed by an alpha sample. 52 private static final int PNG_COLOR_TYPE_GRAY_ALPHA = 4; 53 // Each pixel is an R,G,B triple, followed by an alpha sample. 54 private static final int PNG_COLOR_TYPE_RGBA = 6; 55 56 private static final int NB_OF_LINES_PER_CHUNK = 1; // 0 = full image 57 58 Bitmap bm; // The image as decoded by Android 59 60 // Header information 61 int imageWidth; // Image size 62 int imageHeight; 63 int colorType; // One of the PNG_ constants from above 64 int bitDepth; // Number of bits per color 65 byte cmap[]; // The color palette for index color models 66 ColorModel model; // The corresponding AWT color model 67 68 boolean transferInts; // Is transfer of type int or byte? 69 int dataElementsPerPixel; 70 71 // Buffers for decoded image data 72 byte byteOut[]; 73 int intOut[]; 74 75 76 public AndroidImageDecoder(DecodingImageSource src, InputStream is) { 77 super(src, is); 78 dataElementsPerPixel = 1; 79 } 80 81 @Override 82 /** 83 * All the decoding is done in Android 84 * 85 * AWT???: Method returns only once the image is completly 86 * decoded; decoding is not done asynchronously 87 */ 88 public void decodeImage() throws IOException { 89 try { 90 bm = BitmapFactory.decodeStream(inputStream); 91 if (bm == null) { 92 throw new IOException("Input stream empty and no image cached"); 93 } 94 95 // Check size 96 imageWidth = bm.getWidth(); 97 imageHeight = bm.getHeight(); 98 if (imageWidth < 0 || imageHeight < 0 ) { 99 throw new RuntimeException("Illegal image size: " 100 + imageWidth + ", " + imageHeight); 101 } 102 103 // We got the image fully decoded; now send all image data to AWT 104 setDimensions(imageWidth, imageHeight); 105 model = createColorModel(); 106 setColorModel(model); 107 setHints(hintflags); 108 setProperties(new Hashtable<Object, Object>()); // Empty 109 sendPixels(NB_OF_LINES_PER_CHUNK != 0 ? NB_OF_LINES_PER_CHUNK : imageHeight); 110 imageComplete(ImageConsumer.STATICIMAGEDONE); 111 } catch (IOException e) { 112 throw e; 113 } catch (RuntimeException e) { 114 imageComplete(ImageConsumer.IMAGEERROR); 115 throw e; 116 } finally { 117 closeStream(); 118 } 119 } 120 121 /** 122 * Create the AWT color model 123 * 124 * ???AWT: Android Bitmaps are always of type: ARGB-8888-Direct color model 125 * 126 * However, we leave the code here for a more powerfull decoder 127 * that returns a native model, and the conversion is then handled 128 * in AWT. With such a decoder, we would need to get the colorType, 129 * the bitDepth, (and the color palette for an index color model) 130 * from the image and construct the correct color model here. 131 */ 132 private ColorModel createColorModel() { 133 ColorModel cm = null; 134 int bmModel = 5; // TODO This doesn't exist: bm.getColorModel(); 135 cmap = null; 136 137 switch (bmModel) { 138 // A1_MODEL 139 case 1: 140 colorType = PNG_COLOR_TYPE_GRAY; 141 bitDepth = 1; 142 break; 143 144 // A8_MODEL 145 case 2: 146 colorType = PNG_COLOR_TYPE_GRAY_ALPHA; 147 bitDepth = 8; 148 break; 149 150 // INDEX8_MODEL 151 // RGB_565_MODEL 152 // ARGB_8888_MODEL 153 case 3: 154 case 4: 155 case 5: 156 colorType = bm.hasAlpha() ? PNG_COLOR_TYPE_RGBA : PNG_COLOR_TYPE_RGB; 157 bitDepth = 8; 158 break; 159 160 default: 161 // awt.3C=Unknown PNG color type 162 throw new IllegalArgumentException(Messages.getString("awt.3C")); //$NON-NLS-1$ 163 } 164 165 switch (colorType) { 166 167 case PNG_COLOR_TYPE_GRAY: { 168 if (bitDepth != 8 && bitDepth != 4 && bitDepth != 2 && bitDepth != 1) { 169 // awt.3C=Unknown PNG color type 170 throw new IllegalArgumentException(Messages.getString("awt.3C")); //$NON-NLS-1$ 171 } 172 173 // Create gray color model 174 int numEntries = 1 << bitDepth; 175 int scaleFactor = 255 / (numEntries-1); 176 byte comps[] = new byte[numEntries]; 177 for (int i = 0; i < numEntries; i++) { 178 comps[i] = (byte) (i * scaleFactor); 179 } 180 cm = new IndexColorModel(bitDepth, numEntries, comps, comps, comps); 181 182 transferInts = false; 183 break; 184 } 185 186 case PNG_COLOR_TYPE_RGB: { 187 if (bitDepth != 8) { 188 // awt.3C=Unknown PNG color type 189 throw new IllegalArgumentException(Messages.getString("awt.3C")); //$NON-NLS-1$ 190 } 191 192 cm = new DirectColorModel(24, 0xff0000, 0xFF00, 0xFF); 193 194 transferInts = true; 195 break; 196 } 197 198 case PNG_COLOR_TYPE_PLTE: { 199 if (bitDepth != 8 && bitDepth != 4 && bitDepth != 2 && bitDepth != 1) { 200 // awt.3C=Unknown PNG color type 201 throw new IllegalArgumentException(Messages.getString("awt.3C")); //$NON-NLS-1$ 202 } 203 204 if (cmap == null) { 205 throw new IllegalStateException("Palette color type is not supported"); 206 } 207 208 cm = new IndexColorModel(bitDepth, cmap.length / 3, cmap, 0, false); 209 210 transferInts = false; 211 break; 212 } 213 214 case PNG_COLOR_TYPE_GRAY_ALPHA: { 215 if (bitDepth != 8) { 216 // awt.3C=Unknown PNG color type 217 throw new IllegalArgumentException(Messages.getString("awt.3C")); //$NON-NLS-1$ 218 } 219 220 cm = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_GRAY), 221 true, false, 222 Transparency.TRANSLUCENT, 223 DataBuffer.TYPE_BYTE); 224 225 transferInts = false; 226 dataElementsPerPixel = 2; 227 break; 228 } 229 230 case PNG_COLOR_TYPE_RGBA: { 231 if (bitDepth != 8) { 232 // awt.3C=Unknown PNG color type 233 throw new IllegalArgumentException(Messages.getString("awt.3C")); //$NON-NLS-1$ 234 } 235 236 cm = ColorModel.getRGBdefault(); 237 238 transferInts = true; 239 break; 240 } 241 default: 242 // awt.3C=Unknown PNG color type 243 throw new IllegalArgumentException(Messages.getString("awt.3C")); //$NON-NLS-1$ 244 } 245 246 return cm; 247 } 248 249 private void sendPixels(int nbOfLinesPerChunk) { 250 int w = imageWidth; 251 int h = imageHeight; 252 int n = 1; 253 if (nbOfLinesPerChunk > 0 && nbOfLinesPerChunk <= h) { 254 n = nbOfLinesPerChunk; 255 } 256 257 if (transferInts) { 258 // Create output buffer 259 intOut = new int[w * n]; 260 for (int yi = 0; yi < h; yi += n) { 261 // Last chunk might contain less liness 262 if (n > 1 && h - yi < n ) { 263 n = h - yi; 264 } 265 bm.getPixels(intOut, 0, w, 0, yi, w, n); 266 setPixels(0, yi, w, n, model, intOut, 0, w); 267 } 268 } else { 269 // Android bitmaps always store ints (ARGB-8888 direct model) 270 throw new RuntimeException("Byte transfer not supported"); 271 } 272 } 273 274} 275