FileDocCategorySizeDatePackage
PNGImageWriter.javaAPI DocAndroid 1.5 API9416Wed May 06 22:41:54 BST 2009org.apache.harmony.x.imageio.plugins.png

PNGImageWriter.java

/*
 *  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 Viskov Nikolay
 * @version $Revision$
 */
package org.apache.harmony.x.imageio.plugins.png;

import com.android.internal.awt.ImageOutputStreamWrapper;

import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.DataBufferByte;
import java.awt.image.DataBufferInt;
import java.awt.image.IndexColorModel;
import java.awt.image.Raster;
import java.awt.image.RenderedImage;
import java.awt.image.SampleModel;
import java.awt.image.SinglePixelPackedSampleModel;
import java.awt.image.WritableRaster;
import java.io.IOException;

import javax.imageio.IIOImage;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.spi.ImageWriterSpi;
import javax.imageio.stream.ImageOutputStream;

import org.apache.harmony.x.imageio.internal.nls.Messages;

import org.apache.harmony.luni.util.NotImplementedException;

import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;

public class PNGImageWriter extends ImageWriter {
    
    // /* ???AWT: Debugging
    private static final boolean DEBUG = false;
    private static Bitmap bm;
    public static Bitmap getBitmap() {
        return bm;
    }
    // */
    
    private static int[][] BAND_OFFSETS = {
            {}, {
                0 }, {
                    0, 1 }, {
                    0, 1, 2 }, {
                    0, 1, 2, 3 } };

    // 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;
    
    //???AWT: private static native void initIDs(Class<ImageOutputStream> iosClass);

    static {
        //???AWT
        /*
        System.loadLibrary("pngencoder"); //$NON-NLS-1$
        initIDs(ImageOutputStream.class);
        */
    }
    
    /*
    private native int encode(byte[] input, int bytesInBuffer, int bytePixelSize, Object ios, int imageWidth,
            int imageHeight, int bitDepth, int colorType, int[] palette, int i, boolean b);
    */
    
    protected PNGImageWriter(ImageWriterSpi iwSpi) {
        super(iwSpi);
    }

    @Override
    public IIOMetadata convertStreamMetadata(IIOMetadata arg0, ImageWriteParam arg1) {
        throw new NotImplementedException();
    }

    @Override
    public IIOMetadata convertImageMetadata(IIOMetadata arg0, ImageTypeSpecifier arg1, ImageWriteParam arg2) {
        throw new NotImplementedException();
    }

    @Override
    public IIOMetadata getDefaultImageMetadata(ImageTypeSpecifier arg0, ImageWriteParam arg1) {
        throw new NotImplementedException();
    }

    @Override
    public IIOMetadata getDefaultStreamMetadata(ImageWriteParam arg0) {
        throw new NotImplementedException();
    }

    @Override
    public void write(IIOMetadata streamMetadata, IIOImage iioImage, ImageWriteParam param) throws IOException {
        if (output == null) {
            throw new IllegalStateException("Output not been set");
        }
        if (iioImage == null) {
            throw new IllegalArgumentException("Image equals null");
        }
        // AWT???: I think this is not needed anymore
        // if (iioImage.hasRaster() && !canWriteRasters()) {
        //    throw new UnsupportedOperationException("Can't write raster");
        //}// ImageOutputStreamImpl
        
        Raster sourceRaster;
        RenderedImage img = null;
        if (!iioImage.hasRaster()) {
            img = iioImage.getRenderedImage();
            if (img instanceof BufferedImage) {
                sourceRaster = ((BufferedImage) img).getRaster();
            } else {
                sourceRaster = img.getData();
            }
        } else {
            sourceRaster = iioImage.getRaster();
        }

        SampleModel model = sourceRaster.getSampleModel();
        int srcWidth = sourceRaster.getWidth();
        int srcHeight = sourceRaster.getHeight();
        int numBands = model.getNumBands();
        
        ColorModel colorModel = img.getColorModel();
        int pixelSize = colorModel.getPixelSize();
        int bytePixelSize = pixelSize / 8;
        int bitDepth = pixelSize / numBands;
        
        // byte per band
        int bpb = bitDepth > 8 ? 2 : 1;
        
        boolean isInterlace = true;
        if (param instanceof PNGImageWriterParam) {
            isInterlace = ((PNGImageWriterParam) param).getInterlace();
        }
        
        int colorType = PNG_COLOR_TYPE_GRAY;
        int[] palette = null;
        
        if (colorModel instanceof IndexColorModel) {
            if (bitDepth != 1 && bitDepth != 2 && bitDepth != 4 && bitDepth != 8) {
//              Wrong bitDepth-numBands composition
                throw new IllegalArgumentException(Messages.getString("imageio.1"));//$NON-NLS-1$
            }
            if (numBands != 1) {
//              Wrong bitDepth-numBands composition
                throw new IllegalArgumentException(Messages.getString("imageio.1"));//$NON-NLS-1$
            }

            IndexColorModel icm = (IndexColorModel) colorModel;
            palette = new int[icm.getMapSize()];
            icm.getRGBs(palette);
            colorType = PNG_COLOR_TYPE_PLTE;
        }
        else if (numBands == 1) {
            if (bitDepth != 1 && bitDepth != 2 && bitDepth != 4 && bitDepth != 8 && bitDepth != 16) {
//              Wrong bitDepth-numBands composition
                throw new IllegalArgumentException(Messages.getString("imageio.1"));//$NON-NLS-1$
            }
            colorType = PNG_COLOR_TYPE_GRAY;
        }
        else if (numBands == 2) {
            if (bitDepth != 8 && bitDepth != 16) {
//              Wrong bitDepth-numBands composition
                throw new IllegalArgumentException(Messages.getString("imageio.1"));//$NON-NLS-1$
            }
            colorType = PNG_COLOR_TYPE_GRAY_ALPHA;
        }
        else if (numBands == 3) {
            if (bitDepth != 8 && bitDepth != 16) {
//              Wrong bitDepth-numBands composition
                throw new IllegalArgumentException(Messages.getString("imageio.1")); //$NON-NLS-1$
            }
            colorType = PNG_COLOR_TYPE_RGB;
        }
        else if (numBands == 4) {
            if (bitDepth != 8 && bitDepth != 16) {
                //Wrong bitDepth-numBands composition
                throw new IllegalArgumentException(Messages.getString("imageio.1")); //$NON-NLS-1$
            }
            colorType = PNG_COLOR_TYPE_RGBA;
        }
        
        /* ???AWT: I think this is not needed anymore
        int dbufferLenght = bytePixelSize * imageHeight * imageWidth;
        DataBufferByte dbuffer = new DataBufferByte(dbufferLenght);

        WritableRaster scanRaster = Raster.createInterleavedRaster(dbuffer, imageWidth, imageHeight, bpb * numBands
                * imageWidth, bpb * numBands, BAND_OFFSETS[numBands], null);

        scanRaster.setRect(((BufferedImage) image).getRaster()// image.getData()
                .createChild(0, 0, imageWidth, imageHeight, 0, 0, null));
        */

        if (DEBUG) {
            System.out.println("**** raster:" + sourceRaster);        
            System.out.println("**** model:" + model);
            System.out.println("**** type:" + colorType);
        }
        
        if (model instanceof SinglePixelPackedSampleModel) {
            DataBufferInt ibuf = (DataBufferInt)sourceRaster.getDataBuffer();
            int[] pixels = ibuf.getData();
            
            // Create a bitmap with the pixel
            bm = Bitmap.createBitmap(pixels, srcWidth, srcHeight, Bitmap.Config.ARGB_8888);
            
            // Use Bitmap.compress() to write the image
            ImageOutputStream ios = (ImageOutputStream) getOutput();
            ImageOutputStreamWrapper iosw = new ImageOutputStreamWrapper(ios);
            bm.compress(CompressFormat.PNG, 100, iosw);
        } else {
            // ???AWT: Add support for other color models
            throw new RuntimeException("Color model not supported yet");
        }
    }

    public ImageWriteParam getDefaultWriteParam() {
        return new PNGImageWriterParam();
    }
}