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