1/*
2 *  Licensed to the Apache Software Foundation (ASF) under one or more
3 *  contributor license agreements.  See the NOTICE file distributed with
4 *  this work for additional information regarding copyright ownership.
5 *  The ASF licenses this file to You under the Apache License, Version 2.0
6 *  (the "License"); you may not use this file except in compliance with
7 *  the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 */
17/**
18 * @author Viskov Nikolay
19 * @version $Revision$
20 */
21package org.apache.harmony.x.imageio.plugins.png;
22
23import com.android.internal.awt.ImageOutputStreamWrapper;
24
25import java.awt.image.BufferedImage;
26import java.awt.image.ColorModel;
27import java.awt.image.DataBufferByte;
28import java.awt.image.DataBufferInt;
29import java.awt.image.IndexColorModel;
30import java.awt.image.Raster;
31import java.awt.image.RenderedImage;
32import java.awt.image.SampleModel;
33import java.awt.image.SinglePixelPackedSampleModel;
34import java.awt.image.WritableRaster;
35import java.io.IOException;
36
37import javax.imageio.IIOImage;
38import javax.imageio.ImageTypeSpecifier;
39import javax.imageio.ImageWriteParam;
40import javax.imageio.ImageWriter;
41import javax.imageio.metadata.IIOMetadata;
42import javax.imageio.spi.ImageWriterSpi;
43import javax.imageio.stream.ImageOutputStream;
44
45import org.apache.harmony.x.imageio.internal.nls.Messages;
46
47import org.apache.harmony.luni.util.NotImplementedException;
48
49import android.graphics.Bitmap;
50import android.graphics.Bitmap.CompressFormat;
51
52public class PNGImageWriter extends ImageWriter {
53
54    // /* ???AWT: Debugging
55    private static final boolean DEBUG = false;
56    private static Bitmap bm;
57    public static Bitmap getBitmap() {
58        return bm;
59    }
60    // */
61
62    private static int[][] BAND_OFFSETS = {
63            {}, {
64                0 }, {
65                    0, 1 }, {
66                    0, 1, 2 }, {
67                    0, 1, 2, 3 } };
68
69    // Each pixel is a grayscale sample.
70    private static final int PNG_COLOR_TYPE_GRAY = 0;
71    // Each pixel is an R,G,B triple.
72    private static final int PNG_COLOR_TYPE_RGB = 2;
73    // Each pixel is a palette index, a PLTE chunk must appear.
74    private static final int PNG_COLOR_TYPE_PLTE = 3;
75    // Each pixel is a grayscale sample, followed by an alpha sample.
76    private static final int PNG_COLOR_TYPE_GRAY_ALPHA = 4;
77    // Each pixel is an R,G,B triple, followed by an alpha sample.
78    private static final int PNG_COLOR_TYPE_RGBA = 6;
79
80    //???AWT: private static native void initIDs(Class<ImageOutputStream> iosClass);
81
82    static {
83        //???AWT
84        /*
85        System.loadLibrary("pngencoder"); //$NON-NLS-1$
86        initIDs(ImageOutputStream.class);
87        */
88    }
89
90    /*
91    private native int encode(byte[] input, int bytesInBuffer, int bytePixelSize, Object ios, int imageWidth,
92            int imageHeight, int bitDepth, int colorType, int[] palette, int i, boolean b);
93    */
94
95    protected PNGImageWriter(ImageWriterSpi iwSpi) {
96        super(iwSpi);
97    }
98
99    @Override
100    public IIOMetadata convertStreamMetadata(IIOMetadata arg0, ImageWriteParam arg1) {
101        throw new NotImplementedException();
102    }
103
104    @Override
105    public IIOMetadata convertImageMetadata(IIOMetadata arg0, ImageTypeSpecifier arg1, ImageWriteParam arg2) {
106        throw new NotImplementedException();
107    }
108
109    @Override
110    public IIOMetadata getDefaultImageMetadata(ImageTypeSpecifier arg0, ImageWriteParam arg1) {
111        throw new NotImplementedException();
112    }
113
114    @Override
115    public IIOMetadata getDefaultStreamMetadata(ImageWriteParam arg0) {
116        throw new NotImplementedException();
117    }
118
119    @Override
120    public void write(IIOMetadata streamMetadata, IIOImage iioImage, ImageWriteParam param) throws IOException {
121        if (output == null) {
122            throw new IllegalStateException("Output not been set");
123        }
124        if (iioImage == null) {
125            throw new IllegalArgumentException("Image equals null");
126        }
127        // AWT???: I think this is not needed anymore
128        // if (iioImage.hasRaster() && !canWriteRasters()) {
129        //    throw new UnsupportedOperationException("Can't write raster");
130        //}// ImageOutputStreamImpl
131
132        Raster sourceRaster;
133        RenderedImage img = null;
134        if (!iioImage.hasRaster()) {
135            img = iioImage.getRenderedImage();
136            if (img instanceof BufferedImage) {
137                sourceRaster = ((BufferedImage) img).getRaster();
138            } else {
139                sourceRaster = img.getData();
140            }
141        } else {
142            sourceRaster = iioImage.getRaster();
143        }
144
145        SampleModel model = sourceRaster.getSampleModel();
146        int srcWidth = sourceRaster.getWidth();
147        int srcHeight = sourceRaster.getHeight();
148        int numBands = model.getNumBands();
149
150        ColorModel colorModel = img.getColorModel();
151        int pixelSize = colorModel.getPixelSize();
152        int bytePixelSize = pixelSize / 8;
153        int bitDepth = pixelSize / numBands;
154
155        // byte per band
156        int bpb = bitDepth > 8 ? 2 : 1;
157
158        boolean isInterlace = true;
159        if (param instanceof PNGImageWriterParam) {
160            isInterlace = ((PNGImageWriterParam) param).getInterlace();
161        }
162
163        int colorType = PNG_COLOR_TYPE_GRAY;
164        int[] palette = null;
165
166        if (colorModel instanceof IndexColorModel) {
167            if (bitDepth != 1 && bitDepth != 2 && bitDepth != 4 && bitDepth != 8) {
168//              Wrong bitDepth-numBands composition
169                throw new IllegalArgumentException(Messages.getString("imageio.1"));//$NON-NLS-1$
170            }
171            if (numBands != 1) {
172//              Wrong bitDepth-numBands composition
173                throw new IllegalArgumentException(Messages.getString("imageio.1"));//$NON-NLS-1$
174            }
175
176            IndexColorModel icm = (IndexColorModel) colorModel;
177            palette = new int[icm.getMapSize()];
178            icm.getRGBs(palette);
179            colorType = PNG_COLOR_TYPE_PLTE;
180        }
181        else if (numBands == 1) {
182            if (bitDepth != 1 && bitDepth != 2 && bitDepth != 4 && bitDepth != 8 && bitDepth != 16) {
183//              Wrong bitDepth-numBands composition
184                throw new IllegalArgumentException(Messages.getString("imageio.1"));//$NON-NLS-1$
185            }
186            colorType = PNG_COLOR_TYPE_GRAY;
187        }
188        else if (numBands == 2) {
189            if (bitDepth != 8 && bitDepth != 16) {
190//              Wrong bitDepth-numBands composition
191                throw new IllegalArgumentException(Messages.getString("imageio.1"));//$NON-NLS-1$
192            }
193            colorType = PNG_COLOR_TYPE_GRAY_ALPHA;
194        }
195        else if (numBands == 3) {
196            if (bitDepth != 8 && bitDepth != 16) {
197//              Wrong bitDepth-numBands composition
198                throw new IllegalArgumentException(Messages.getString("imageio.1")); //$NON-NLS-1$
199            }
200            colorType = PNG_COLOR_TYPE_RGB;
201        }
202        else if (numBands == 4) {
203            if (bitDepth != 8 && bitDepth != 16) {
204                //Wrong bitDepth-numBands composition
205                throw new IllegalArgumentException(Messages.getString("imageio.1")); //$NON-NLS-1$
206            }
207            colorType = PNG_COLOR_TYPE_RGBA;
208        }
209
210        /* ???AWT: I think this is not needed anymore
211        int dbufferLenght = bytePixelSize * imageHeight * imageWidth;
212        DataBufferByte dbuffer = new DataBufferByte(dbufferLenght);
213
214        WritableRaster scanRaster = Raster.createInterleavedRaster(dbuffer, imageWidth, imageHeight, bpb * numBands
215                * imageWidth, bpb * numBands, BAND_OFFSETS[numBands], null);
216
217        scanRaster.setRect(((BufferedImage) image).getRaster()// image.getData()
218                .createChild(0, 0, imageWidth, imageHeight, 0, 0, null));
219        */
220
221        if (DEBUG) {
222            System.out.println("**** raster:" + sourceRaster);
223            System.out.println("**** model:" + model);
224            System.out.println("**** type:" + colorType);
225        }
226
227        if (model instanceof SinglePixelPackedSampleModel) {
228            DataBufferInt ibuf = (DataBufferInt)sourceRaster.getDataBuffer();
229            int[] pixels = ibuf.getData();
230
231            // Create a bitmap with the pixel
232            bm = Bitmap.createBitmap(pixels, srcWidth, srcHeight, Bitmap.Config.ARGB_8888);
233
234            // Use Bitmap.compress() to write the image
235            ImageOutputStream ios = (ImageOutputStream) getOutput();
236            ImageOutputStreamWrapper iosw = new ImageOutputStreamWrapper(ios);
237            bm.compress(CompressFormat.PNG, 100, iosw);
238        } else {
239            // ???AWT: Add support for other color models
240            throw new RuntimeException("Color model not supported yet");
241        }
242    }
243
244    public ImageWriteParam getDefaultWriteParam() {
245        return new PNGImageWriterParam();
246    }
247}
248