Java/2D Graphics GUI/PNG File

Материал из Java эксперт
Версия от 06:53, 1 июня 2010; Admin (обсуждение | вклад) (1 версия)
(разн.) ← Предыдущая | Текущая версия (разн.) | Следующая → (разн.)
Перейти к: навигация, поиск

Draw an Image and save to png

    
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.GradientPaint;
import java.awt.Graphics2D;
import java.awt.geom.Ellipse2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
public class WriteImageType {
  static public void main(String args[]) throws Exception {
    try {
      int width = 200, height = 200;
      // TYPE_INT_ARGB specifies the image format: 8-bit RGBA packed
      // into integer pixels
      BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
      Graphics2D ig2 = bi.createGraphics();

      Font font = new Font("TimesRoman", Font.BOLD, 20);
      ig2.setFont(font);
      String message = "www.jexp.ru!";
      FontMetrics fontMetrics = ig2.getFontMetrics();
      int stringWidth = fontMetrics.stringWidth(message);
      int stringHeight = fontMetrics.getAscent();
      ig2.setPaint(Color.black);
      ig2.drawString(message, (width - stringWidth) / 2, height / 2 + stringHeight / 4);
      ImageIO.write(bi, "PNG", new File("c:\\yourImageName.PNG"));
      ImageIO.write(bi, "JPEG", new File("c:\\yourImageName.JPG"));
      ImageIO.write(bi, "gif", new File("c:\\yourImageName.GIF"));
      ImageIO.write(bi, "BMP", new File("c:\\yourImageName.BMP"));
      
    } catch (IOException ie) {
      ie.printStackTrace();
    }
  }
}



Encodes a java.awt.Image into PNG format

   
/* 
 * This file is part of the Echo Web Application Framework (hereinafter "Echo").
 * Copyright (C) 2002-2009 NextApp, Inc.
 *
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (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.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 */

import java.awt.Graphics;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
import java.awt.image.IndexColorModel;
import java.awt.image.PixelGrabber;
import java.awt.image.Raster;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.zip.CheckedOutputStream;
import java.util.zip.Checksum;
import java.util.zip.CRC32;
import java.util.zip.Deflater;
import java.util.zip.DeflaterOutputStream;
import javax.swing.ImageIcon;
/**
 * Encodes a java.awt.Image into PNG format.
 * For more information on the PNG specification, see the W3C PNG page at 
 * .
 */
public class PngEncoder {
    /**
     * Utility class for converting <code>Image</code>s to <code>BufferedImage</code>s.
     */
    private static class ImageToBufferedImage {
    
        /**
         * Converts an <code>Image</code> to a <code>BufferedImage</code>.
         * If the image is already a <code>BufferedImage</code>, the original is returned.
         * 
         * @param image the image to convert
         * @return the image as a <code>BufferedImage</code>
         */
        static BufferedImage toBufferedImage(Image image) {
            if (image instanceof BufferedImage) {
                // Return image unchanged if it is already a BufferedImage.
                return (BufferedImage) image;
            }
            
            // Ensure image is loaded.
            image = new ImageIcon(image).getImage();        
            
            int type = hasAlpha(image) ? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB;
            BufferedImage bufferedImage = new BufferedImage(image.getWidth(null), image.getHeight(null), type);
            Graphics g = bufferedImage.createGraphics();
            g.drawImage(image, 0, 0, null);
            g.dispose();
            
            return bufferedImage;
        }
        
        /**
         * Determines if an image has an alpha channel.
         * 
         * @param image the <code>Image</code>
         * @return true if the image has an alpha channel
         */
        static boolean hasAlpha(Image image) {
            PixelGrabber pg = new PixelGrabber(image, 0, 0, 1, 1, false);
            try {
                pg.grabPixels();
            } catch (InterruptedException ex) { }
            return pg.getColorModel().hasAlpha();
        }
    }
    /** <code>SubFilter</code> singleton. */
    public static final Filter SUB_FILTER = new SubFilter();
    
    /** <code>UpFilter</code> singleton. */
    public static final Filter UP_FILTER = new UpFilter();
    
    /** <code>AverageFilter</code> singleton. */
    public static final Filter AVERAGE_FILTER = new AverageFilter();
    /** <code>PaethFilter</code> singleton. */
    public static final Filter PAETH_FILTER = new PaethFilter();
    
    /** PNG signature bytes. */
    private static final byte[] SIGNATURE = { (byte)0x89, (byte)0x50, (byte)0x4e, (byte)0x47, 
                                              (byte)0x0d, (byte)0x0a, (byte)0x1a, (byte)0x0a };
    
    /** Image header (IHDR) chunk header. */
    private static final byte[] IHDR = { (byte) "I", (byte) "H", (byte) "D", (byte) "R" };
    
    /** Palate (PLTE) chunk header. */
    private static final byte[] PLTE = { (byte) "P", (byte) "L", (byte) "T", (byte) "E" };
    
    /** Image Data (IDAT) chunk header. */
    private static final byte[] IDAT = { (byte) "I", (byte) "D", (byte) "A", (byte) "T" };
    
    /** End-of-file (IEND) chunk header. */
    private static final byte[] IEND = { (byte) "I", (byte) "E", (byte) "N", (byte) "D" };
    
    /** Sub filter type constant. */
    private static final int SUB_FILTER_TYPE = 1;
    /** Up filter type constant. */
    private static final int UP_FILTER_TYPE = 2;
    
    /** Average filter type constant. */
    private static final int AVERAGE_FILTER_TYPE = 3;
    /** Paeth filter type constant. */
    private static final int PAETH_FILTER_TYPE = 4;
    /** Image bit depth. */
    private static final byte BIT_DEPTH = (byte) 8;
    /** Indexed color type rendered value. */
    private static final byte COLOR_TYPE_INDEXED  = (byte) 3;
    
    /** RGB color type rendered value. */
    private static final byte COLOR_TYPE_RGB      = (byte) 2;
    
    /** RGBA color type rendered value. */
    private static final byte COLOR_TYPE_RGBA     = (byte) 6;
    /** Integer-to-integer map used for RGBA/ARGB conversion. */
    private static final int[] INT_TRANSLATOR_CHANNEL_MAP = new int[]{2, 1, 0, 3};
    
    /**
     * Writes an 32-bit integer value to the output stream.
     *
     * @param out the stream
     * @param i the value
     */
    private static void writeInt(OutputStream out, int i) 
    throws IOException {
        out.write(new byte[]{(byte) (i >> 24), 
                             (byte) ((i >> 16) & 0xff), 
                             (byte) ((i >> 8) & 0xff), 
                             (byte) (i & 0xff)});
    }
    /**
     * An interface for PNG filters.  Filters are used to modify the method in 
     * which pixels of the image are stored in ways that will achieve better
     * compression.
     */ 
    public interface Filter {
    
        /** 
         * Filters the data in a given row of the image.
         *
         * @param currentRow a byte array containing the data of the row of the
         *        image to be filtered
         * @param previousRow a byte array containing the data of the previous 
         *        row of the image to be filtered
         * @param filterOutput a byte array into which the filtered data will
         *        be placed
         */
        public void filter(byte[] filterOutput, byte[] currentRow, byte[] previousRow, int outputBpp);
        
        /**
         * Returns the PNG type code for the filter.
         */
        public int getType();
    }
    
    /**
     * An implementation of a "Sub" filter.
     */
    private static class SubFilter
    implements Filter {
    
        /**
         * @see nextapp.echo.webcontainer.util.PngEncoder.Filter#filter(byte[], byte[], byte[], int)
         */
        public void filter(byte[] filterOutput, byte[] currentRow, byte[] previousRow, int outputBpp) {
            for (int index = 0; index < filterOutput.length; ++index) {
                if (index < outputBpp) {
                    filterOutput[index] = currentRow[index];
                } else {
                    filterOutput[index] = (byte) (currentRow[index] - currentRow[index - outputBpp]);
                }
            }
        }
        /**
         * @see nextapp.echo.webcontainer.util.PngEncoder.Filter#getType()
         */
        public int getType() {
            return SUB_FILTER_TYPE;
        }
    }
        
    /**
     * An implementation of an "Up" filter.
     */
    private static class UpFilter
    implements Filter {
        /**
         * @see nextapp.echo.webcontainer.util.PngEncoder.Filter#filter(byte[], byte[], byte[], int)
         */
        public void filter(byte[] filterOutput, byte[] currentRow, byte[] previousRow, int outputBpp) {
            for (int index = 0; index < currentRow.length; ++index) {
                filterOutput[index] = (byte) (currentRow[index] - previousRow[index]);
            }
        }
        /**
         * @see nextapp.echo.webcontainer.util.PngEncoder.Filter#getType()
         */
        public int getType() {
            return UP_FILTER_TYPE;
        }
    }
    
    /**
     * An implementation of an "Average" filter.
     */
    private static class AverageFilter
    implements Filter {
        
        /**
         * @see nextapp.echo.webcontainer.util.PngEncoder.Filter#filter(byte[], byte[], byte[], int)
         */
        public void filter(byte[] filterOutput, byte[] currentRow, byte[] previousRow, int outputBpp) {
            int w, n;
            
            for (int index = 0; index < filterOutput.length; ++index) {
                n = (previousRow[index] + 0x100) & 0xff;
                if (index < outputBpp) {
                    w = 0;
                } else {
                    w = (currentRow[index - outputBpp] + 0x100) & 0xff;
                }
                filterOutput[index] = (byte) (currentRow[index] - (byte) ((w + n) / 2));
            }
        }
        /**
         * @see nextapp.echo.webcontainer.util.PngEncoder.Filter#getType()
         */
        public int getType() {
            return AVERAGE_FILTER_TYPE;
        }
    }
    /**
     * An implementation of a "Paeth" filter.
     */
    private static class PaethFilter
    implements Filter {
    
        /**
         * @see nextapp.echo.webcontainer.util.PngEncoder.Filter#filter(byte[], byte[], byte[], int)
         */
        public void filter(byte[] filterOutput, byte[] currentRow, byte[] previousRow, int outputBpp) {
            byte pv;
            int  n, w, nw, p, pn, pw, pnw;
            
            for (int index = 0; index < filterOutput.length; ++index) {
                n = (previousRow[index] + 0x100) & 0xff;
                if (index < outputBpp) {
                    w = 0;
                    nw = 0;
                } else {
                    w = (currentRow[index - outputBpp] + 0x100) & 0xff;
                    nw = (previousRow[index - outputBpp] + 0x100) & 0xff;
                }
                
                p = w + n - nw;
                pw = Math.abs(p - w);
                pn = Math.abs(p - n);
                pnw = Math.abs(p - w);
                if (pw <= pn && pw <= pnw) {
                    pv = (byte) w;
                } else if (pn <= pnw) {
                    pv = (byte) n;
                } else {
                    pv = (byte) nw;
                }
                
                filterOutput[index] = (byte) (currentRow[index] - pv);
            }
        }
        /**
         * @see nextapp.echo.webcontainer.util.PngEncoder.Filter#getType()
         */
        public int getType() {
            return PAETH_FILTER_TYPE;
        }
    }
    
    /**
     * An interface for translators, which translate pixel data from a 
     * writable raster into an R/G/B/A ordering required by the PNG
     * specification.  Pixel data in the raster might be available
     * in three bytes per pixel, four bytes per pixel, or as integers.
     */
    interface Translator {
    
        /**
         * Translates a row of the image into a byte array ordered
         * properly for a PNG image.
         *
         * @param outputPixelQueue the byte array in which to store the
         *        translated pixels
         * @param row the row index of the image to translate
         */
        public void translate(byte[] outputPixelQueue, int row);
    }
    
    /**
     * Translates byte-based rasters.
     */
    private class ByteTranslator 
    implements Translator {
    
        int rowWidth = width * outputBpp;                         // size of image data in a row in bytes.
        byte[] inputPixelQueue = new byte[rowWidth + outputBpp];
        int column;
        int channel;
        /**
         * @see nextapp.echo.webcontainer.util.PngEncoder.Translator#translate(byte[], int)
         */
        public void translate(byte[] outputPixelQueue, int row) {
            raster.getDataElements(0, row, width, 1, inputPixelQueue);
            for (column = 0; column < width; ++column) {
                for (channel = 0; channel < outputBpp; ++channel) {
                    outputPixelQueue[column * outputBpp + channel]
                            = inputPixelQueue[column * inputBpp + channel];
                }
            }
        }
    }
    
    /**
     * Translates integer-based rasters.
     */
    private class IntTranslator
    implements Translator  {
    
        int[] inputPixelQueue = new int[width];
        int column;
        int channel;
        /**
         * @see nextapp.echo.webcontainer.util.PngEncoder.Translator#translate(byte[], int)
         */
        public void translate(byte[] outputPixelQueue, int row) {
        
            image.getRGB(0, row, width, 1, inputPixelQueue, 0, width);
            // Line below (commented out) replaces line above, almost halving time to encode, but doesn"t work with certain pixel 
            // arrangements.  Need to find method of determining pixel order (BGR vs RGB, ARGB, etc)
            //
            // raster.getDataElements(0, row, width, 1, inputPixelQueue);
            for (column = 0; column < width; ++column) {
                for (channel = 0; channel < outputBpp; ++channel) {
                    outputPixelQueue[column * outputBpp + channel]
                            = (byte) (inputPixelQueue[column] >> (INT_TRANSLATOR_CHANNEL_MAP[channel] * 8));
                }
            }
        }
    }
    
    /** The image being encoded. */
    private BufferedImage image;
    
    /** The PNG encoding filter to be used. */
    private Filter filter;
    
    /** The the deflater compression level. */
    private int compressionLevel;
    
    /** The pixel width of the image. */
    private int width;
    
    /** The pixel height of the image. */
    private int height;
    
    /** The image <code>Raster</code> transfer type. */
    private int transferType;
    
    /** The image <code>Raster</code> data. */
    private Raster raster;
    
    /** The source image bits-per-pixel. */
    private int inputBpp;
    
    /** The encoded image bits-per-pixel. */
    private int outputBpp;
    
    /** The <code>Translator</code> being used for encoding. */
    private Translator translator;
    
    /**
     * Creates a PNG encoder for an image.
     *
     * @param image the image to be encoded
     * @param encodeAlpha true if the image"s alpha channel should be encoded
     * @param filter The filter to be applied to the image data, one of the 
     *        following values:
     *        <ul>
     *        <li>SUB_FILTER</li>
     *        <li>UP_FILTER</li>
     *        <li>AVERAGE_FILTER</li>
     *        <li>PAETH_FILTER</li>
     *        </ul>
     *        If a null value is specified, no filtering will be performed.
     * @param compressionLevel the deflater compression level that will be used
     *        for compressing the image data:  Valid values range from 0 to 9.
     *        Higher values result in smaller files and therefore decrease
     *        network traffic, but require more CPU time to encode.  The normal
     *        compromise value is 3.
     */
    public PngEncoder(Image image, boolean encodeAlpha, Filter filter, int compressionLevel) {
        super();
        
        this.image = ImageToBufferedImage.toBufferedImage(image);
        this.filter = filter;
        this.rupressionLevel = compressionLevel;
        
        width = this.image.getWidth(null);
        height = this.image.getHeight(null);
        raster = this.image.getRaster();
        transferType = raster.getTransferType();
        // Establish storage information
        int dataBytes = raster.getNumDataElements();
        if (transferType == DataBuffer.TYPE_BYTE && dataBytes == 4) {
            outputBpp = encodeAlpha ? 4 : 3;
            inputBpp = 4;
            translator = new ByteTranslator();
        } else if (transferType == DataBuffer.TYPE_BYTE && dataBytes == 3) {
            outputBpp = 3;
            inputBpp = 3;
            encodeAlpha = false;
            translator = new ByteTranslator();
        } else if (transferType == DataBuffer.TYPE_INT && dataBytes == 1) {
            outputBpp = encodeAlpha ? 4 : 3;
            inputBpp = 4;
            translator = new IntTranslator();
        } else if (transferType == DataBuffer.TYPE_BYTE && dataBytes == 1) {
            throw new UnsupportedOperationException("Encoding indexed-color images not yet supported.");
        } else {
            throw new IllegalArgumentException(
                    "Cannot determine appropriate bits-per-pixel for provided image.");
        }
    }
    
    /**
     * Encodes the image.
     *
     * @param out an OutputStream to which the encoded image will be
     *            written
     * @throws IOException if a problem is encountered writing the output
     */
    public synchronized void encode(OutputStream out) 
    throws IOException {
        Checksum csum = new CRC32();
        out = new CheckedOutputStream(out, csum);
    
        out.write(SIGNATURE);
        writeIhdrChunk(out, csum);
        if (outputBpp == 1) {
            writePlteChunk(out, csum);
        }
        
        writeIdatChunks(out, csum);
        
        writeIendChunk(out, csum);
    }
    
    /**
     * Writes the IDAT (Image data) chunks to the output stream.
     *
     * @param out the OutputStream to write the chunk to
     * @param csum the Checksum that is updated as data is written
     *             to the passed-in OutputStream
     * @throws IOException if a problem is encountered writing the output
     */
    private void writeIdatChunks(OutputStream out, Checksum csum)
    throws IOException {
        int rowWidth = width * outputBpp;                         // size of image data in a row in bytes.
        int row = 0;
                
        Deflater deflater = new Deflater(compressionLevel);
        ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
        DeflaterOutputStream defOut = new DeflaterOutputStream(byteOut, deflater);
        byte[] filteredPixelQueue = new byte[rowWidth];
        // Output Pixel Queues
        byte[][] outputPixelQueue = new byte[2][rowWidth];
        Arrays.fill(outputPixelQueue[1], (byte) 0);
        int outputPixelQueueRow = 0;
        int outputPixelQueuePrevRow = 1;
        while (row < height) {
            if (filter == null) {
                defOut.write(0);
                translator.translate(outputPixelQueue[outputPixelQueueRow], row);
                defOut.write(outputPixelQueue[outputPixelQueueRow], 0, rowWidth);
            } else {
                defOut.write(filter.getType());
                translator.translate(outputPixelQueue[outputPixelQueueRow], row);
                filter.filter(filteredPixelQueue, outputPixelQueue[outputPixelQueueRow], 
                        outputPixelQueue[outputPixelQueuePrevRow], outputBpp);
                defOut.write(filteredPixelQueue, 0, rowWidth);
            }
            
            ++row;
            outputPixelQueueRow = row & 1;
            outputPixelQueuePrevRow = outputPixelQueueRow ^ 1;
        }
        defOut.finish();
        byteOut.close();
        
        writeInt(out, byteOut.size());
        csum.reset();
        out.write(IDAT);
        byteOut.writeTo(out);
        writeInt(out, (int) csum.getValue());
    }
    
    /**
     * Writes the IEND (End-of-file) chunk to the output stream.
     *
     * @param out the OutputStream to write the chunk to
     * @param csum the Checksum that is updated as data is written
     *             to the passed-in OutputStream
     * @throws IOException if a problem is encountered writing the output
     */
    private void writeIendChunk(OutputStream out, Checksum csum)
    throws IOException {
        writeInt(out, 0);
        csum.reset();
        out.write(IEND);
        writeInt(out, (int) csum.getValue());
    }
    
    /**
     * writes the IHDR (Image Header) chunk to the output stream
     *
     * @param out the OutputStream to write the chunk to
     * @param csum the Checksum that is updated as data is written
     *             to the passed-in OutputStream
     * @throws IOException if a problem is encountered writing the output
     */ 
    private void writeIhdrChunk(OutputStream out, Checksum csum) 
    throws IOException {
        writeInt(out, 13); // Chunk Size
        csum.reset();
        out.write(IHDR);
        writeInt(out, width);
        writeInt(out, height);
        out.write(BIT_DEPTH);
        switch (outputBpp) {
        case 1:
            out.write(COLOR_TYPE_INDEXED);
            break;
        case 3:
            out.write(COLOR_TYPE_RGB);
            break;
        case 4:
            out.write(COLOR_TYPE_RGBA);
            break;
        default:
            throw new IllegalStateException("Invalid bytes per pixel");
        }
        out.write(0); // Compression Method
        out.write(0); // Filter Method
        out.write(0); // Interlace
        writeInt(out, (int) csum.getValue());
    }
    
    /**
     * Writes the PLTE (Palate) chunk to the output stream.
     *
     * @param out the OutputStream to write the chunk to
     * @param csum the Checksum that is updated as data is written
     *             to the passed-in OutputStream
     * @throws IOException if a problem is encountered writing the output
     */
    private void writePlteChunk(OutputStream out, Checksum csum) 
    throws IOException {
        IndexColorModel icm = (IndexColorModel) image.getColorModel();
        
        writeInt(out, 768); // Chunk Size
        csum.reset();
        out.write(PLTE);
        
        byte[] reds = new byte[256];
        icm.getReds(reds);
        
        byte[] greens = new byte[256];
        icm.getGreens(greens);
        
        byte[] blues = new byte[256];
        icm.getBlues(blues);
        
        for (int index = 0; index < 256; ++index) {
            out.write(reds[index]);
            out.write(greens[index]);
            out.write(blues[index]);
        }
                
        writeInt(out, (int) csum.getValue());
    }
}



PNG Decoder

 
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common
 * Development and Distribution License("CDDL") (collectively, the
 * "License"). You may not use this file except in compliance with the
 * License. You can obtain a copy of the License at
 * http://www.netbeans.org/cddl-gplv2.html
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
 * specific language governing permissions and limitations under the
 * License.  When distributing the software, include this License Header
 * Notice in each file and include the License file at
 * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Sun in the GPL Version 2 section of the License file that
 * accompanied this code. If applicable, add the following below the
 * License Header, with the fields enclosed by brackets [] replaced by
 * your own identifying information:
 * "Portions Copyrighted [year] [name of copyright owner]"
 *
 * Contributor(s): Alexandre Iline.
 *
 * The Original Software is the Jemmy library.
 * The Initial Developer of the Original Software is Alexandre Iline.
 * All Rights Reserved.
 *
 * If you wish your version of this file to be governed by only the CDDL
 * or only the GPL Version 2, indicate your decision by adding
 * "[Contributor] elects to include this software in this distribution
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
 * single choice of license, a recipient has the option to distribute
 * your version of this file under either the CDDL, the GPL Version 2 or
 * to extend the choice of license to its licensees as provided above.
 * However, if you add GPL Version 2 code and therefore, elected the GPL
 * Version 2 license, then the option applies only if the new code is
 * made subject to such option by the copyright holder.
 *
 *
 *
 * $Id$ $Revision$ $Date$
 *
 */

import java.awt.AWTException;
import java.awt.Color;
import java.awt.ruponent;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.zip.CRC32;
import java.util.zip.DataFormatException;
import java.util.zip.Deflater;
import java.util.zip.DeflaterOutputStream;
import java.util.zip.Inflater;
/**
 * Allows to load PNG graphical file.
 * @author Alexandre Iline
 */
public class PNGDecoder extends Object {
    InputStream in;
    /**
     * Constructs a PNGDecoder object.
     * @param in input stream to read PNG image from.
     */    
    public PNGDecoder(InputStream in) {
        this.in = in;
    }
    byte read() throws IOException {
        byte b = (byte)in.read();
        return(b);
    }
    int readInt() throws IOException {
        byte b[] = read(4);
        return(((b[0]&0xff)<<24) +
               ((b[1]&0xff)<<16) +
               ((b[2]&0xff)<<8) +
               ((b[3]&0xff)));
    }
    byte[] read(int count) throws IOException {
        byte[] result = new byte[count];
        for(int i = 0; i < count; i++) {
            result[i] = read();
        }
        return(result);
    }
    boolean compare(byte[] b1, byte[] b2) {
        if(b1.length != b2.length) {
            return(false);
        }
        for(int i = 0; i < b1.length; i++) {
            if(b1[i] != b2[i]) {
                return(false);
            }
        }
        return(true);
    }
    void checkEquality(byte[] b1, byte[] b2) {
        if(!compare(b1, b2)) {
            throw(new RuntimeException("Format error"));
        }
    }
    /**
     * Decodes image from an input stream passed into constructor.
     * @return a BufferedImage object
     * @throws IOException
     */
    public BufferedImage decode() throws IOException {
        byte[] id = read(12);
        checkEquality(id, new byte[] {-119, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13});
        byte[] ihdr = read(4);
        checkEquality(ihdr, "IHDR".getBytes());
        int width = readInt();
        int height = readInt();
        BufferedImage result = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        byte[] head = read(5);
        int mode;
        if(compare(head, new byte[]{1, 0, 0, 0, 0})) {
            mode = PNGEncoder.BW_MODE;
        } else if(compare(head, new byte[]{8, 0, 0, 0, 0})) {
            mode = PNGEncoder.GREYSCALE_MODE;
        } else if(compare(head, new byte[]{8, 2, 0, 0, 0})) {
            mode = PNGEncoder.COLOR_MODE;
        } else {
            throw(new RuntimeException("Format error"));
        }
        readInt();//!!crc
        int size = readInt();
        byte[] idat = read(4);
        checkEquality(idat, "IDAT".getBytes());
        byte[] data = read(size);

        Inflater inflater = new Inflater();
        inflater.setInput(data, 0, size);
        int color;
        try {
            switch (mode) {
            case PNGEncoder.BW_MODE: 
                {
                    int bytes = (int)(width / 8);
                    if((width % 8) != 0) {
                        bytes++;
                    }
                    byte colorset;
                    byte[] row = new byte[bytes];
                    for (int y = 0; y < height; y++) {
                        inflater.inflate(new byte[1]);
                        inflater.inflate(row);
                        for (int x = 0; x < bytes; x++) {
                            colorset = row[x];
                            for (int sh = 0; sh < 8; sh++) {
                                if(x * 8 + sh >= width) {
                                    break;
                                }
                                if((colorset & 0x80) == 0x80) {
                                    result.setRGB(x * 8 + sh, y, Color.white.getRGB());
                                } else {
                                    result.setRGB(x * 8 + sh, y, Color.black.getRGB());
                                }
                                colorset <<= 1;
                            }
                        }
                    }
                }
                break;
            case PNGEncoder.GREYSCALE_MODE: 
                {
                    byte[] row = new byte[width];
                    for (int y = 0; y < height; y++) {
                        inflater.inflate(new byte[1]);
                        inflater.inflate(row);
                        for (int x = 0; x < width; x++) {
                            color = row[x];
                            result.setRGB(x, y, (color << 16) + (color << 8) + color);
                        }
                    }
                }
                break;
            case PNGEncoder.COLOR_MODE:
                {
                    byte[] row = new byte[width * 3];
                    for (int y = 0; y < height; y++) {
                        inflater.inflate(new byte[1]);
                        inflater.inflate(row);
                        for (int x = 0; x < width; x++) {
                            result.setRGB(x, y, 
                                          ((row[x * 3 + 0]&0xff) << 16) +
                                          ((row[x * 3 + 1]&0xff) << 8) +
                                          ((row[x * 3 + 2]&0xff)));
                        }
                    }
                }
            }
        } catch(DataFormatException e) {
            throw(new RuntimeException("ZIP error"+e));
        }
        readInt();//!!crc
        readInt();//0
        byte[] iend = read(4);
        checkEquality(iend, "IEND".getBytes());
        readInt();//!!crc
        in.close();
        return(result);
    }
    /**
     * Decodes image from file.
     * @param fileName a file to read image from
     * @return a BufferedImage instance.
     */
    public static BufferedImage decode(String fileName) {
        try {
            return(new PNGDecoder(new FileInputStream(fileName)).decode());
        } catch(IOException e) {
            throw(new RuntimeException("IOException during image reading"+ e));
        }
    }
}
class PNGEncoder extends Object {
  /** black and white image mode. */    
  public static final byte BW_MODE = 0;
  /** grey scale image mode. */    
  public static final byte GREYSCALE_MODE = 1;
  /** full color image mode. */    
  public static final byte COLOR_MODE = 2;
  
  OutputStream out;
  CRC32 crc;
  byte mode;
  /** public constructor of PNGEncoder class with greyscale mode by default.
   * @param out output stream for PNG image format to write into
   */    
  public PNGEncoder(OutputStream out) {
      this(out, GREYSCALE_MODE);
  }
  /** public constructor of PNGEncoder class.
   * @param out output stream for PNG image format to write into
   * @param mode BW_MODE, GREYSCALE_MODE or COLOR_MODE
   */    
  public PNGEncoder(OutputStream out, byte mode) {
      crc=new CRC32();
      this.out = out;
      if (mode<0 || mode>2)
          throw new IllegalArgumentException("Unknown color mode");
      this.mode = mode;
  }
  void write(int i) throws IOException {
      byte b[]={(byte)((i>>24)&0xff),(byte)((i>>16)&0xff),(byte)((i>>8)&0xff),(byte)(i&0xff)};
      write(b);
  }
  void write(byte b[]) throws IOException {
      out.write(b);
      crc.update(b);
  }
  
  /** main encoding method (stays blocked till encoding is finished).
   * @param image BufferedImage to encode
   * @throws IOException IOException
   */    
  public void encode(BufferedImage image) throws IOException {
      int width = image.getWidth(null);
      int height = image.getHeight(null);
      final byte id[] = {-119, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13};
      write(id);
      crc.reset();
      write("IHDR".getBytes());
      write(width);
      write(height);
      byte head[]=null;
      switch (mode) {
          case BW_MODE: head=new byte[]{1, 0, 0, 0, 0}; break;
          case GREYSCALE_MODE: head=new byte[]{8, 0, 0, 0, 0}; break;
          case COLOR_MODE: head=new byte[]{8, 2, 0, 0, 0}; break;
      }                 
      write(head);
      write((int) crc.getValue());
      ByteArrayOutputStream compressed = new ByteArrayOutputStream(65536);
      BufferedOutputStream bos = new BufferedOutputStream( new DeflaterOutputStream(compressed, new Deflater(9)));
      int pixel;
      int color;
      int colorset;
      switch (mode) {
          case BW_MODE: 
              int rest=width%8;
              int bytes=width/8;
              for (int y=0;y<height;y++) {
                  bos.write(0);
                  for (int x=0;x<bytes;x++) {
                      colorset=0;
                      for (int sh=0; sh<8; sh++) {
                          pixel=image.getRGB(x*8+sh,y);
                          color=((pixel >> 16) & 0xff);
                          color+=((pixel >> 8) & 0xff);
                          color+=(pixel & 0xff);
                          colorset<<=1;
                          if (color>=3*128)
                              colorset|=1;
                      }
                      bos.write((byte)colorset);
                  }
                  if (rest>0) {
                      colorset=0;
                      for (int sh=0; sh<width%8; sh++) {
                          pixel=image.getRGB(bytes*8+sh,y);
                          color=((pixel >> 16) & 0xff);
                          color+=((pixel >> 8) & 0xff);
                          color+=(pixel & 0xff);
                          colorset<<=1;
                          if (color>=3*128)
                              colorset|=1;
                      }
                      colorset<<=8-rest;
                      bos.write((byte)colorset);
                  }
              }
              break;
          case GREYSCALE_MODE: 
              for (int y=0;y<height;y++) {
                  bos.write(0);
                  for (int x=0;x<width;x++) {
                      pixel=image.getRGB(x,y);
                      color=((pixel >> 16) & 0xff);
                      color+=((pixel >> 8) & 0xff);
                      color+=(pixel & 0xff);
                      bos.write((byte)(color/3));
                  }
              }
              break;
           case COLOR_MODE:
              for (int y=0;y<height;y++) {
                  bos.write(0);
                  for (int x=0;x<width;x++) {
                      pixel=image.getRGB(x,y);
                      bos.write((byte)((pixel >> 16) & 0xff));
                      bos.write((byte)((pixel >> 8) & 0xff));
                      bos.write((byte)(pixel & 0xff));
                  }
              }
              break;
      }
      bos.close();
      write(compressed.size());
      crc.reset();
      write("IDAT".getBytes());
      write(compressed.toByteArray());
      write((int) crc.getValue()); 
      write(0);
      crc.reset();
      write("IEND".getBytes());
      write((int) crc.getValue()); 
      out.close();
  }
  /** Static method performing screen capture into PNG image format file with given fileName.
   * @param rect Rectangle of screen to be captured
   * @param fileName file name for screen capture PNG image file */    
  public static void captureScreen(Rectangle rect, String fileName) {
      captureScreen(rect, fileName, GREYSCALE_MODE);
  }
  /** Static method performing screen capture into PNG image format file with given fileName.
   * @param rect Rectangle of screen to be captured
   * @param mode image color mode
   * @param fileName file name for screen capture PNG image file */    
  public static void captureScreen(Rectangle rect, String fileName, byte mode) {
      try {
          BufferedImage capture=new Robot().createScreenCapture(rect);
          BufferedOutputStream file=new BufferedOutputStream(new FileOutputStream(fileName));
          PNGEncoder encoder=new PNGEncoder(file, mode);
          encoder.encode(capture);
      } catch (AWTException awte) {
          awte.printStackTrace();
      } catch (IOException ioe) {
          ioe.printStackTrace();
      }
  }
   /** Static method performing one component screen capture into PNG image format file with given fileName.
    * @param comp Component to be captured
    * @param fileName String image target filename */    
  public static void captureScreen(Component comp, String fileName) {
      captureScreen(comp, fileName, GREYSCALE_MODE);
  }
  
  /** Static method performing one component screen capture into PNG image format file with given fileName.
   * @param comp Component to be captured
   * @param fileName String image target filename
   * @param mode image color mode */    
  public static void captureScreen(Component comp, String fileName, byte mode) {
captureScreen(new Rectangle(comp.getLocationOnScreen(),
          comp.getSize()),
        fileName, mode);
  }
  
  /** Static method performing whole screen capture into PNG image format file with given fileName.
   * @param fileName String image target filename */    
  public static void captureScreen(String fileName) {
      captureScreen(fileName, GREYSCALE_MODE);
  }
  
  /** Static method performing whole screen capture into PNG image format file with given fileName.
   * @param fileName String image target filename
   * @param mode image color mode */    
  public static void captureScreen(String fileName, byte mode) {
captureScreen(new Rectangle(Toolkit.getDefaultToolkit().getScreenSize()), fileName, mode);
  }
}



PNG Encoder

 
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common
 * Development and Distribution License("CDDL") (collectively, the
 * "License"). You may not use this file except in compliance with the
 * License. You can obtain a copy of the License at
 * http://www.netbeans.org/cddl-gplv2.html
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
 * specific language governing permissions and limitations under the
 * License.  When distributing the software, include this License Header
 * Notice in each file and include the License file at
 * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Sun in the GPL Version 2 section of the License file that
 * accompanied this code. If applicable, add the following below the
 * License Header, with the fields enclosed by brackets [] replaced by
 * your own identifying information:
 * "Portions Copyrighted [year] [name of copyright owner]"
 *
 * Contributor(s): Alexandre Iline.
 *
 * The Original Software is the Jemmy library.
 * The Initial Developer of the Original Software is Alexandre Iline.
 * All Rights Reserved.
 *
 * If you wish your version of this file to be governed by only the CDDL
 * or only the GPL Version 2, indicate your decision by adding
 * "[Contributor] elects to include this software in this distribution
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
 * single choice of license, a recipient has the option to distribute
 * your version of this file under either the CDDL, the GPL Version 2 or
 * to extend the choice of license to its licensees as provided above.
 * However, if you add GPL Version 2 code and therefore, elected the GPL
 * Version 2 license, then the option applies only if the new code is
 * made subject to such option by the copyright holder.
 *
 *
 *
 * $Id$ $Revision$ $Date$
 *
 */

import java.awt.AWTException;
import java.awt.ruponent;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.util.zip.CRC32;
import java.util.zip.Deflater;
import java.util.zip.DeflaterOutputStream;
import java.util.zip.Inflater;
/** This class allows to encode BufferedImage into B/W, greyscale or true color PNG
 * image format with maximum compression.<br>
 * It also provides complete functionality for capturing full screen, part of
 * screen or single component, encoding and saving captured image info PNG file.
 * @author Adam Sotona
 * @version 1.0 */
public class PNGEncoder extends Object {
    /** black and white image mode. */    
    public static final byte BW_MODE = 0;
    /** grey scale image mode. */    
    public static final byte GREYSCALE_MODE = 1;
    /** full color image mode. */    
    public static final byte COLOR_MODE = 2;
    
    OutputStream out;
    CRC32 crc;
    byte mode;
    /** public constructor of PNGEncoder class with greyscale mode by default.
     * @param out output stream for PNG image format to write into
     */    
    public PNGEncoder(OutputStream out) {
        this(out, GREYSCALE_MODE);
    }
    /** public constructor of PNGEncoder class.
     * @param out output stream for PNG image format to write into
     * @param mode BW_MODE, GREYSCALE_MODE or COLOR_MODE
     */    
    public PNGEncoder(OutputStream out, byte mode) {
        crc=new CRC32();
        this.out = out;
        if (mode<0 || mode>2)
            throw new IllegalArgumentException("Unknown color mode");
        this.mode = mode;
    }
    void write(int i) throws IOException {
        byte b[]={(byte)((i>>24)&0xff),(byte)((i>>16)&0xff),(byte)((i>>8)&0xff),(byte)(i&0xff)};
        write(b);
    }
    void write(byte b[]) throws IOException {
        out.write(b);
        crc.update(b);
    }
    
    /** main encoding method (stays blocked till encoding is finished).
     * @param image BufferedImage to encode
     * @throws IOException IOException
     */    
    public void encode(BufferedImage image) throws IOException {
        int width = image.getWidth(null);
        int height = image.getHeight(null);
        final byte id[] = {-119, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13};
        write(id);
        crc.reset();
        write("IHDR".getBytes());
        write(width);
        write(height);
        byte head[]=null;
        switch (mode) {
            case BW_MODE: head=new byte[]{1, 0, 0, 0, 0}; break;
            case GREYSCALE_MODE: head=new byte[]{8, 0, 0, 0, 0}; break;
            case COLOR_MODE: head=new byte[]{8, 2, 0, 0, 0}; break;
        }                 
        write(head);
        write((int) crc.getValue());
        ByteArrayOutputStream compressed = new ByteArrayOutputStream(65536);
        BufferedOutputStream bos = new BufferedOutputStream( new DeflaterOutputStream(compressed, new Deflater(9)));
        int pixel;
        int color;
        int colorset;
        switch (mode) {
            case BW_MODE: 
                int rest=width%8;
                int bytes=width/8;
                for (int y=0;y<height;y++) {
                    bos.write(0);
                    for (int x=0;x<bytes;x++) {
                        colorset=0;
                        for (int sh=0; sh<8; sh++) {
                            pixel=image.getRGB(x*8+sh,y);
                            color=((pixel >> 16) & 0xff);
                            color+=((pixel >> 8) & 0xff);
                            color+=(pixel & 0xff);
                            colorset<<=1;
                            if (color>=3*128)
                                colorset|=1;
                        }
                        bos.write((byte)colorset);
                    }
                    if (rest>0) {
                        colorset=0;
                        for (int sh=0; sh<width%8; sh++) {
                            pixel=image.getRGB(bytes*8+sh,y);
                            color=((pixel >> 16) & 0xff);
                            color+=((pixel >> 8) & 0xff);
                            color+=(pixel & 0xff);
                            colorset<<=1;
                            if (color>=3*128)
                                colorset|=1;
                        }
                        colorset<<=8-rest;
                        bos.write((byte)colorset);
                    }
                }
                break;
            case GREYSCALE_MODE: 
                for (int y=0;y<height;y++) {
                    bos.write(0);
                    for (int x=0;x<width;x++) {
                        pixel=image.getRGB(x,y);
                        color=((pixel >> 16) & 0xff);
                        color+=((pixel >> 8) & 0xff);
                        color+=(pixel & 0xff);
                        bos.write((byte)(color/3));
                    }
                }
                break;
             case COLOR_MODE:
                for (int y=0;y<height;y++) {
                    bos.write(0);
                    for (int x=0;x<width;x++) {
                        pixel=image.getRGB(x,y);
                        bos.write((byte)((pixel >> 16) & 0xff));
                        bos.write((byte)((pixel >> 8) & 0xff));
                        bos.write((byte)(pixel & 0xff));
                    }
                }
                break;
        }
        bos.close();
        write(compressed.size());
        crc.reset();
        write("IDAT".getBytes());
        write(compressed.toByteArray());
        write((int) crc.getValue()); 
        write(0);
        crc.reset();
        write("IEND".getBytes());
        write((int) crc.getValue()); 
        out.close();
    }
    /** Static method performing screen capture into PNG image format file with given fileName.
     * @param rect Rectangle of screen to be captured
     * @param fileName file name for screen capture PNG image file */    
    public static void captureScreen(Rectangle rect, String fileName) {
        captureScreen(rect, fileName, GREYSCALE_MODE);
    }
    /** Static method performing screen capture into PNG image format file with given fileName.
     * @param rect Rectangle of screen to be captured
     * @param mode image color mode
     * @param fileName file name for screen capture PNG image file */    
    public static void captureScreen(Rectangle rect, String fileName, byte mode) {
        try {
            BufferedImage capture=new Robot().createScreenCapture(rect);
            BufferedOutputStream file=new BufferedOutputStream(new FileOutputStream(fileName));
            PNGEncoder encoder=new PNGEncoder(file, mode);
            encoder.encode(capture);
        } catch (AWTException awte) {
            awte.printStackTrace();
        } catch (IOException ioe) {
            ioe.printStackTrace();
        }
    }
     /** Static method performing one component screen capture into PNG image format file with given fileName.
      * @param comp Component to be captured
      * @param fileName String image target filename */    
    public static void captureScreen(Component comp, String fileName) {
        captureScreen(comp, fileName, GREYSCALE_MODE);
    }
    
    /** Static method performing one component screen capture into PNG image format file with given fileName.
     * @param comp Component to be captured
     * @param fileName String image target filename
     * @param mode image color mode */    
    public static void captureScreen(Component comp, String fileName, byte mode) {
  captureScreen(new Rectangle(comp.getLocationOnScreen(),
            comp.getSize()),
          fileName, mode);
    }
    
    /** Static method performing whole screen capture into PNG image format file with given fileName.
     * @param fileName String image target filename */    
    public static void captureScreen(String fileName) {
        captureScreen(fileName, GREYSCALE_MODE);
    }
    
    /** Static method performing whole screen capture into PNG image format file with given fileName.
     * @param fileName String image target filename
     * @param mode image color mode */    
    public static void captureScreen(String fileName, byte mode) {
  captureScreen(new Rectangle(Toolkit.getDefaultToolkit().getScreenSize()), fileName, mode);
    }
}



PngEncoder takes a Java Image object and creates a byte string which can be saved as a PNG file

  

import java.awt.Image;
import java.awt.image.ImageObserver;
import java.awt.image.PixelGrabber;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.zip.CRC32;
import java.util.zip.Deflater;
import java.util.zip.DeflaterOutputStream;
/**
 * PngEncoder takes a Java Image object and creates a byte string which can be
 * saved as a PNG file.  The Image is presumed to use the DirectColorModel.
 *
 * <p>Thanks to Jay Denny at KeyPoint Software
 *    http://www.keypoint.ru/
 * who let me develop this code on company time.</p>
 *
 * <p>You may contact me with (probably very-much-needed) improvements,
 * comments, and bug fixes at:</p>
 *
 *   <p><code>david@catcode.ru</code></p>
 *
 * <p>This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.</p>
 *
 * <p>This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.</p>
 *
 * <p>You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
 * USA. A copy of the GNU LGPL may be found at
 * <code>http://www.gnu.org/copyleft/lesser.html</code></p>
 *
 * @author J. David Eisenberg
 * @version 1.5, 19 Oct 2003
 *
 * CHANGES:
 * --------
 * 19-Nov-2002 : CODING STYLE CHANGES ONLY (by David Gilbert for Object
 *               Refinery Limited);
 * 19-Sep-2003 : Fix for platforms using EBCDIC (contributed by Paulo Soares);
 * 19-Oct-2003 : Change private fields to protected fields so that
 *               PngEncoderB can inherit them (JDE)
 *               Fixed bug with calculation of nRows
 * 15-Aug-2008 : Added scrunch.end() in writeImageData() method - see
 *               JFreeChart bug report 2037930 (David Gilbert);
 */
public class PngEncoder {
    /** Constant specifying that alpha channel should be encoded. */
    public static final boolean ENCODE_ALPHA = true;
    /** Constant specifying that alpha channel should not be encoded. */
    public static final boolean NO_ALPHA = false;
    /** Constants for filter (NONE). */
    public static final int FILTER_NONE = 0;
    /** Constants for filter (SUB). */
    public static final int FILTER_SUB = 1;
    /** Constants for filter (UP). */
    public static final int FILTER_UP = 2;
    /** Constants for filter (LAST). */
    public static final int FILTER_LAST = 2;
    /** IHDR tag. */
    protected static final byte[] IHDR = {73, 72, 68, 82};
    /** IDAT tag. */
    protected static final byte[] IDAT = {73, 68, 65, 84};
    /** IEND tag. */
    protected static final byte[] IEND = {73, 69, 78, 68};
    /** PHYS tag. */
    protected static final byte[] PHYS = {(byte)"p", (byte)"H", (byte)"Y",
        (byte)"s"};
    /** The png bytes. */
    protected byte[] pngBytes;
    /** The prior row. */
    protected byte[] priorRow;
    /** The left bytes. */
    protected byte[] leftBytes;
    /** The image. */
    protected Image image;
    /** The width. */
    protected int width;
    /** The height. */
    protected int height;
    /** The byte position. */
    protected int bytePos;
    /** The maximum position. */
    protected int maxPos;
    /** CRC. */
    protected CRC32 crc = new CRC32();
    /** The CRC value. */
    protected long crcValue;
    /** Encode alpha? */
    protected boolean encodeAlpha;
    /** The filter type. */
    protected int filter;
    /** The bytes-per-pixel. */
    protected int bytesPerPixel;
    /** The physical pixel dimension : number of pixels per inch on the X axis. */
    private int xDpi = 0;
    /** The physical pixel dimension : number of pixels per inch on the Y axis. */
    private int yDpi = 0;
    /** Used for conversion of DPI to Pixels per Meter. */
    static private float INCH_IN_METER_UNIT = 0.0254f;
    /**
     * The compression level (1 = best speed, 9 = best compression,
     * 0 = no compression).
     */
    protected int compressionLevel;
    /**
     * Class constructor.
     */
    public PngEncoder() {
        this(null, false, FILTER_NONE, 0);
    }
    /**
     * Class constructor specifying Image to encode, with no alpha channel
     * encoding.
     *
     * @param image A Java Image object which uses the DirectColorModel
     * @see java.awt.Image
     */
    public PngEncoder(Image image) {
        this(image, false, FILTER_NONE, 0);
    }
    /**
     * Class constructor specifying Image to encode, and whether to encode
     * alpha.
     *
     * @param image A Java Image object which uses the DirectColorModel
     * @param encodeAlpha Encode the alpha channel? false=no; true=yes
     * @see java.awt.Image
     */
    public PngEncoder(Image image, boolean encodeAlpha) {
        this(image, encodeAlpha, FILTER_NONE, 0);
    }
    /**
     * Class constructor specifying Image to encode, whether to encode alpha,
     * and filter to use.
     *
     * @param image A Java Image object which uses the DirectColorModel
     * @param encodeAlpha Encode the alpha channel? false=no; true=yes
     * @param whichFilter 0=none, 1=sub, 2=up
     * @see java.awt.Image
     */
    public PngEncoder(Image image, boolean encodeAlpha, int whichFilter) {
        this(image, encodeAlpha, whichFilter, 0);
    }

    /**
     * Class constructor specifying Image source to encode, whether to encode
     * alpha, filter to use, and compression level.
     *
     * @param image A Java Image object
     * @param encodeAlpha Encode the alpha channel? false=no; true=yes
     * @param whichFilter 0=none, 1=sub, 2=up
     * @param compLevel 0..9 (1 = best speed, 9 = best compression, 0 = no
     *        compression)
     * @see java.awt.Image
     */
    public PngEncoder(Image image, boolean encodeAlpha, int whichFilter,
            int compLevel) {
        this.image = image;
        this.encodeAlpha = encodeAlpha;
        setFilter(whichFilter);
        if (compLevel >= 0 && compLevel <= 9) {
            this.rupressionLevel = compLevel;
        }
    }
    /**
     * Set the image to be encoded.
     *
     * @param image A Java Image object which uses the DirectColorModel
     * @see java.awt.Image
     * @see java.awt.image.DirectColorModel
     */
    public void setImage(Image image) {
        this.image = image;
        this.pngBytes = null;
    }
    /**
     * Returns the image to be encoded.
     *
     * @return The image.
     */
    public Image getImage() {
      return this.image;
    }
  /**
     * Creates an array of bytes that is the PNG equivalent of the current
     * image, specifying whether to encode alpha or not.
     *
     * @param encodeAlpha boolean false=no alpha, true=encode alpha
     * @return an array of bytes, or null if there was a problem
     */
    public byte[] pngEncode(boolean encodeAlpha) {
        byte[]  pngIdBytes = {-119, 80, 78, 71, 13, 10, 26, 10};
        if (this.image == null) {
            return null;
        }
        this.width = this.image.getWidth(null);
        this.height = this.image.getHeight(null);
        /*
         * start with an array that is big enough to hold all the pixels
         * (plus filter bytes), and an extra 200 bytes for header info
         */
        this.pngBytes = new byte[((this.width + 1) * this.height * 3) + 200];
        /*
         * keep track of largest byte written to the array
         */
        this.maxPos = 0;
        this.bytePos = writeBytes(pngIdBytes, 0);
        //hdrPos = bytePos;
        writeHeader();
        writeResolution();
        //dataPos = bytePos;
        if (writeImageData()) {
            writeEnd();
            this.pngBytes = resizeByteArray(this.pngBytes, this.maxPos);
        }
        else {
            this.pngBytes = null;
        }
        return this.pngBytes;
    }
    /**
     * Creates an array of bytes that is the PNG equivalent of the current
     * image.  Alpha encoding is determined by its setting in the constructor.
     *
     * @return an array of bytes, or null if there was a problem
     */
    public byte[] pngEncode() {
        return pngEncode(this.encodeAlpha);
    }
    /**
     * Set the alpha encoding on or off.
     *
     * @param encodeAlpha  false=no, true=yes
     */
    public void setEncodeAlpha(boolean encodeAlpha) {
        this.encodeAlpha = encodeAlpha;
    }
    /**
     * Retrieve alpha encoding status.
     *
     * @return boolean false=no, true=yes
     */
    public boolean getEncodeAlpha() {
        return this.encodeAlpha;
    }
    /**
     * Set the filter to use.
     *
     * @param whichFilter from constant list
     */
    public void setFilter(int whichFilter) {
        this.filter = FILTER_NONE;
        if (whichFilter <= FILTER_LAST) {
            this.filter = whichFilter;
        }
    }
    /**
     * Retrieve filtering scheme.
     *
     * @return int (see constant list)
     */
    public int getFilter() {
        return this.filter;
    }
    /**
     * Set the compression level to use.
     *
     * @param level the compression level (1 = best speed, 9 = best compression,
     *        0 = no compression)
     */
    public void setCompressionLevel(int level) {
        if (level >= 0 && level <= 9) {
            this.rupressionLevel = level;
        }
    }
    /**
     * Retrieve compression level.
     *
     * @return int (1 = best speed, 9 = best compression, 0 = no compression)
     */
    public int getCompressionLevel() {
        return this.rupressionLevel;
    }
    /**
     * Increase or decrease the length of a byte array.
     *
     * @param array The original array.
     * @param newLength The length you wish the new array to have.
     * @return Array of newly desired length. If shorter than the
     *         original, the trailing elements are truncated.
     */
    protected byte[] resizeByteArray(byte[] array, int newLength) {
        byte[]  newArray = new byte[newLength];
        int     oldLength = array.length;
        System.arraycopy(array, 0, newArray, 0, Math.min(oldLength, newLength));
        return newArray;
    }
    /**
     * Write an array of bytes into the pngBytes array.
     * Note: This routine has the side effect of updating
     * maxPos, the largest element written in the array.
     * The array is resized by 1000 bytes or the length
     * of the data to be written, whichever is larger.
     *
     * @param data The data to be written into pngBytes.
     * @param offset The starting point to write to.
     * @return The next place to be written to in the pngBytes array.
     */
    protected int writeBytes(byte[] data, int offset) {
        this.maxPos = Math.max(this.maxPos, offset + data.length);
        if (data.length + offset > this.pngBytes.length) {
            this.pngBytes = resizeByteArray(this.pngBytes, this.pngBytes.length
                    + Math.max(1000, data.length));
        }
        System.arraycopy(data, 0, this.pngBytes, offset, data.length);
        return offset + data.length;
    }
    /**
     * Write an array of bytes into the pngBytes array, specifying number of
     * bytes to write. Note: This routine has the side effect of updating
     * maxPos, the largest element written in the array.
     * The array is resized by 1000 bytes or the length
     * of the data to be written, whichever is larger.
     *
     * @param data The data to be written into pngBytes.
     * @param nBytes The number of bytes to be written.
     * @param offset The starting point to write to.
     * @return The next place to be written to in the pngBytes array.
     */
    protected int writeBytes(byte[] data, int nBytes, int offset) {
        this.maxPos = Math.max(this.maxPos, offset + nBytes);
        if (nBytes + offset > this.pngBytes.length) {
            this.pngBytes = resizeByteArray(this.pngBytes, this.pngBytes.length
                    + Math.max(1000, nBytes));
        }
        System.arraycopy(data, 0, this.pngBytes, offset, nBytes);
        return offset + nBytes;
    }
    /**
     * Write a two-byte integer into the pngBytes array at a given position.
     *
     * @param n The integer to be written into pngBytes.
     * @param offset The starting point to write to.
     * @return The next place to be written to in the pngBytes array.
     */
    protected int writeInt2(int n, int offset) {
        byte[] temp = {(byte) ((n >> 8) & 0xff), (byte) (n & 0xff)};
        return writeBytes(temp, offset);
    }
    /**
     * Write a four-byte integer into the pngBytes array at a given position.
     *
     * @param n The integer to be written into pngBytes.
     * @param offset The starting point to write to.
     * @return The next place to be written to in the pngBytes array.
     */
    protected int writeInt4(int n, int offset) {
        byte[] temp = {(byte) ((n >> 24) & 0xff),
                       (byte) ((n >> 16) & 0xff),
                       (byte) ((n >> 8) & 0xff),
                       (byte) (n & 0xff)};
        return writeBytes(temp, offset);
    }
    /**
     * Write a single byte into the pngBytes array at a given position.
     *
     * @param b The integer to be written into pngBytes.
     * @param offset The starting point to write to.
     * @return The next place to be written to in the pngBytes array.
     */
    protected int writeByte(int b, int offset) {
        byte[] temp = {(byte) b};
        return writeBytes(temp, offset);
    }
    /**
     * Write a PNG "IHDR" chunk into the pngBytes array.
     */
    protected void writeHeader() {
        int startPos = this.bytePos = writeInt4(13, this.bytePos);
        this.bytePos = writeBytes(IHDR, this.bytePos);
        this.width = this.image.getWidth(null);
        this.height = this.image.getHeight(null);
        this.bytePos = writeInt4(this.width, this.bytePos);
        this.bytePos = writeInt4(this.height, this.bytePos);
        this.bytePos = writeByte(8, this.bytePos); // bit depth
        this.bytePos = writeByte((this.encodeAlpha) ? 6 : 2, this.bytePos);
            // direct model
        this.bytePos = writeByte(0, this.bytePos); // compression method
        this.bytePos = writeByte(0, this.bytePos); // filter method
        this.bytePos = writeByte(0, this.bytePos); // no interlace
        this.crc.reset();
        this.crc.update(this.pngBytes, startPos, this.bytePos - startPos);
        this.crcValue = this.crc.getValue();
        this.bytePos = writeInt4((int) this.crcValue, this.bytePos);
    }
    /**
     * Perform "sub" filtering on the given row.
     * Uses temporary array leftBytes to store the original values
     * of the previous pixels.  The array is 16 bytes long, which
     * will easily hold two-byte samples plus two-byte alpha.
     *
     * @param pixels The array holding the scan lines being built
     * @param startPos Starting position within pixels of bytes to be filtered.
     * @param width Width of a scanline in pixels.
     */
    protected void filterSub(byte[] pixels, int startPos, int width) {
        int offset = this.bytesPerPixel;
        int actualStart = startPos + offset;
        int nBytes = width * this.bytesPerPixel;
        int leftInsert = offset;
        int leftExtract = 0;
        for (int i = actualStart; i < startPos + nBytes; i++) {
            this.leftBytes[leftInsert] =  pixels[i];
            pixels[i] = (byte) ((pixels[i] - this.leftBytes[leftExtract])
                     % 256);
            leftInsert = (leftInsert + 1) % 0x0f;
            leftExtract = (leftExtract + 1) % 0x0f;
        }
    }
    /**
     * Perform "up" filtering on the given row.
     * Side effect: refills the prior row with current row
     *
     * @param pixels The array holding the scan lines being built
     * @param startPos Starting position within pixels of bytes to be filtered.
     * @param width Width of a scanline in pixels.
     */
    protected void filterUp(byte[] pixels, int startPos, int width) {
        final int nBytes = width * this.bytesPerPixel;
        for (int i = 0; i < nBytes; i++) {
            final byte currentByte = pixels[startPos + i];
            pixels[startPos + i] = (byte) ((pixels[startPos  + i]
                    - this.priorRow[i]) % 256);
            this.priorRow[i] = currentByte;
        }
    }
    /**
     * Write the image data into the pngBytes array.
     * This will write one or more PNG "IDAT" chunks. In order
     * to conserve memory, this method grabs as many rows as will
     * fit into 32K bytes, or the whole image; whichever is less.
     *
     *
     * @return true if no errors; false if error grabbing pixels
     */
    protected boolean writeImageData() {
        int rowsLeft = this.height;  // number of rows remaining to write
        int startRow = 0;       // starting row to process this time through
        int nRows;              // how many rows to grab at a time
        byte[] scanLines;       // the scan lines to be compressed
        int scanPos;            // where we are in the scan lines
        int startPos;           // where this line"s actual pixels start (used
                                // for filtering)
        byte[] compressedLines; // the resultant compressed lines
        int nCompressed;        // how big is the compressed area?
        //int depth;              // color depth ( handle only 8 or 32 )
        PixelGrabber pg;
        this.bytesPerPixel = (this.encodeAlpha) ? 4 : 3;
        Deflater scrunch = new Deflater(this.rupressionLevel);
        ByteArrayOutputStream outBytes = new ByteArrayOutputStream(1024);
        DeflaterOutputStream compBytes = new DeflaterOutputStream(outBytes,
                scrunch);
        try {
            while (rowsLeft > 0) {
                nRows = Math.min(32767 / (this.width
                        * (this.bytesPerPixel + 1)), rowsLeft);
                nRows = Math.max(nRows, 1);
                int[] pixels = new int[this.width * nRows];
                pg = new PixelGrabber(this.image, 0, startRow,
                        this.width, nRows, pixels, 0, this.width);
                try {
                    pg.grabPixels();
                }
                catch (Exception e) {
                    System.err.println("interrupted waiting for pixels!");
                    return false;
                }
                if ((pg.getStatus() & ImageObserver.ABORT) != 0) {
                    System.err.println("image fetch aborted or errored");
                    return false;
                }
                /*
                 * Create a data chunk. scanLines adds "nRows" for
                 * the filter bytes.
                 */
                scanLines = new byte[this.width * nRows * this.bytesPerPixel
                                     + nRows];
                if (this.filter == FILTER_SUB) {
                    this.leftBytes = new byte[16];
                }
                if (this.filter == FILTER_UP) {
                    this.priorRow = new byte[this.width * this.bytesPerPixel];
                }
                scanPos = 0;
                startPos = 1;
                for (int i = 0; i < this.width * nRows; i++) {
                    if (i % this.width == 0) {
                        scanLines[scanPos++] = (byte) this.filter;
                        startPos = scanPos;
                    }
                    scanLines[scanPos++] = (byte) ((pixels[i] >> 16) & 0xff);
                    scanLines[scanPos++] = (byte) ((pixels[i] >>  8) & 0xff);
                    scanLines[scanPos++] = (byte) ((pixels[i]) & 0xff);
                    if (this.encodeAlpha) {
                        scanLines[scanPos++] = (byte) ((pixels[i] >> 24)
                                & 0xff);
                    }
                    if ((i % this.width == this.width - 1)
                            && (this.filter != FILTER_NONE)) {
                        if (this.filter == FILTER_SUB) {
                            filterSub(scanLines, startPos, this.width);
                        }
                        if (this.filter == FILTER_UP) {
                            filterUp(scanLines, startPos, this.width);
                        }
                    }
                }
                /*
                 * Write these lines to the output area
                 */
                compBytes.write(scanLines, 0, scanPos);
                startRow += nRows;
                rowsLeft -= nRows;
            }
            compBytes.close();
            /*
             * Write the compressed bytes
             */
            compressedLines = outBytes.toByteArray();
            nCompressed = compressedLines.length;
            this.crc.reset();
            this.bytePos = writeInt4(nCompressed, this.bytePos);
            this.bytePos = writeBytes(IDAT, this.bytePos);
            this.crc.update(IDAT);
            this.bytePos = writeBytes(compressedLines, nCompressed,
                    this.bytePos);
            this.crc.update(compressedLines, 0, nCompressed);
            this.crcValue = this.crc.getValue();
            this.bytePos = writeInt4((int) this.crcValue, this.bytePos);
            scrunch.finish();
            scrunch.end();
            return true;
        }
        catch (IOException e) {
            System.err.println(e.toString());
            return false;
        }
    }
    /**
     * Write a PNG "IEND" chunk into the pngBytes array.
     */
    protected void writeEnd() {
        this.bytePos = writeInt4(0, this.bytePos);
        this.bytePos = writeBytes(IEND, this.bytePos);
        this.crc.reset();
        this.crc.update(IEND);
        this.crcValue = this.crc.getValue();
        this.bytePos = writeInt4((int) this.crcValue, this.bytePos);
    }

    /**
     * Set the DPI for the X axis.
     *
     * @param xDpi  The number of dots per inch
     */
    public void setXDpi(int xDpi) {
        this.xDpi = Math.round(xDpi / INCH_IN_METER_UNIT);
    }
    /**
     * Get the DPI for the X axis.
     *
     * @return The number of dots per inch
     */
    public int getXDpi() {
        return Math.round(this.xDpi * INCH_IN_METER_UNIT);
    }
    /**
     * Set the DPI for the Y axis.
     *
     * @param yDpi  The number of dots per inch
     */
    public void setYDpi(int yDpi) {
        this.yDpi = Math.round(yDpi / INCH_IN_METER_UNIT);
    }
    /**
     * Get the DPI for the Y axis.
     *
     * @return The number of dots per inch
     */
    public int getYDpi() {
        return Math.round(this.yDpi * INCH_IN_METER_UNIT);
    }
    /**
     * Set the DPI resolution.
     *
     * @param xDpi  The number of dots per inch for the X axis.
     * @param yDpi  The number of dots per inch for the Y axis.
     */
    public void setDpi(int xDpi, int yDpi) {
        this.xDpi = Math.round(xDpi / INCH_IN_METER_UNIT);
        this.yDpi = Math.round(yDpi / INCH_IN_METER_UNIT);
    }
    /**
     * Write a PNG "pHYs" chunk into the pngBytes array.
     */
    protected void writeResolution() {
        if (this.xDpi > 0 && this.yDpi > 0) {
            final int startPos = this.bytePos = writeInt4(9, this.bytePos);
            this.bytePos = writeBytes(PHYS, this.bytePos);
            this.bytePos = writeInt4(this.xDpi, this.bytePos);
            this.bytePos = writeInt4(this.yDpi, this.bytePos);
            this.bytePos = writeByte(1, this.bytePos); // unit is the meter.
            this.crc.reset();
            this.crc.update(this.pngBytes, startPos, this.bytePos - startPos);
            this.crcValue = this.crc.getValue();
            this.bytePos = writeInt4((int) this.crcValue, this.bytePos);
        }
    }
}



PNG file format decoder

    
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.IndexColorModel;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.zip.CRC32;
import java.util.zip.InflaterInputStream;
import javax.swing.JFrame;
public class PNGDecoder {
  public static void main(String[] args) throws Exception {
    String name = "logo.png";
    if (args.length > 0)
      name = args[0];
    InputStream in = PNGDecoder.class.getResourceAsStream(name);
    final BufferedImage image = PNGDecoder.decode(in);
    in.close();
    JFrame f = new JFrame() {
      public void paint(Graphics g) {
        Insets insets = getInsets();
        g.drawImage(image, insets.left, insets.top, null);
      }
    };
    f.setVisible(true);
    Insets insets = f.getInsets();
    f.setSize(image.getWidth() + insets.left + insets.right, image
        .getHeight()
        + insets.top + insets.bottom);
  }
  public static BufferedImage decode(InputStream in) throws IOException {
    DataInputStream dataIn = new DataInputStream(in);
    readSignature(dataIn);
    PNGData chunks = readChunks(dataIn);
    long widthLong = chunks.getWidth();
    long heightLong = chunks.getHeight();
    if (widthLong > Integer.MAX_VALUE || heightLong > Integer.MAX_VALUE)
      throw new IOException("That image is too wide or tall.");
    int width = (int) widthLong;
    int height = (int) heightLong;
    ColorModel cm = chunks.getColorModel();
    WritableRaster raster = chunks.getRaster();
    BufferedImage image = new BufferedImage(cm, raster, false, null);
    return image;
  }
  protected static void readSignature(DataInputStream in) throws IOException {
    long signature = in.readLong();
    if (signature != 0x89504e470d0a1a0aL)
      throw new IOException("PNG signature not found!");
  }
  protected static PNGData readChunks(DataInputStream in) throws IOException {
    PNGData chunks = new PNGData();
    boolean trucking = true;
    while (trucking) {
      try {
        // Read the length.
        int length = in.readInt();
        if (length < 0)
          throw new IOException("Sorry, that file is too long.");
        // Read the type.
        byte[] typeBytes = new byte[4];
        in.readFully(typeBytes);
        // Read the data.
        byte[] data = new byte[length];
        in.readFully(data);
        // Read the CRC.
        long crc = in.readInt() & 0x00000000ffffffffL; // Make it
        // unsigned.
        if (verifyCRC(typeBytes, data, crc) == false)
          throw new IOException("That file appears to be corrupted.");
        PNGChunk chunk = new PNGChunk(typeBytes, data);
        chunks.add(chunk);
      } catch (EOFException eofe) {
        trucking = false;
      }
    }
    return chunks;
  }
  protected static boolean verifyCRC(byte[] typeBytes, byte[] data, long crc) {
    CRC32 crc32 = new CRC32();
    crc32.update(typeBytes);
    crc32.update(data);
    long calculated = crc32.getValue();
    return (calculated == crc);
  }
}
class PNGData {
  private int mNumberOfChunks;
  private PNGChunk[] mChunks;
  public PNGData() {
    mNumberOfChunks = 0;
    mChunks = new PNGChunk[10];
  }
  public void add(PNGChunk chunk) {
    mChunks[mNumberOfChunks++] = chunk;
    if (mNumberOfChunks >= mChunks.length) {
      PNGChunk[] largerArray = new PNGChunk[mChunks.length + 10];
      System.arraycopy(mChunks, 0, largerArray, 0, mChunks.length);
      mChunks = largerArray;
    }
  }
  public long getWidth() {
    return getChunk("IHDR").getUnsignedInt(0);
  }
  public long getHeight() {
    return getChunk("IHDR").getUnsignedInt(4);
  }
  public short getBitsPerPixel() {
    return getChunk("IHDR").getUnsignedByte(8);
  }
  public short getColorType() {
    return getChunk("IHDR").getUnsignedByte(9);
  }
  public short getCompression() {
    return getChunk("IHDR").getUnsignedByte(10);
  }
  public short getFilter() {
    return getChunk("IHDR").getUnsignedByte(11);
  }
  public short getInterlace() {
    return getChunk("IHDR").getUnsignedByte(12);
  }
  public ColorModel getColorModel() {
    short colorType = getColorType();
    int bitsPerPixel = getBitsPerPixel();
    if (colorType == 3) {
      byte[] paletteData = getChunk("PLTE").getData();
      int paletteLength = paletteData.length / 3;
      return new IndexColorModel(bitsPerPixel, paletteLength,
          paletteData, 0, false);
    }
    System.out.println("Unsupported color type: " + colorType);
    return null;
  }
  public WritableRaster getRaster() {
    int width = (int) getWidth();
    int height = (int) getHeight();
    int bitsPerPixel = getBitsPerPixel();
    short colorType = getColorType();
    if (colorType == 3) {
      byte[] imageData = getImageData();
      DataBuffer db = new DataBufferByte(imageData, imageData.length);
      WritableRaster raster = Raster.createPackedRaster(db, width,
          height, bitsPerPixel, null);
      return raster;
    } else
      System.out.println("Unsupported color type!");
    return null;
  }
  public byte[] getImageData() {
    try {
      ByteArrayOutputStream out = new ByteArrayOutputStream();
      // Write all the IDAT data into the array.
      for (int i = 0; i < mNumberOfChunks; i++) {
        PNGChunk chunk = mChunks[i];
        if (chunk.getTypeString().equals("IDAT")) {
          out.write(chunk.getData());
        }
      }
      out.flush();
      // Now deflate the data.
      InflaterInputStream in = new InflaterInputStream(
          new ByteArrayInputStream(out.toByteArray()));
      ByteArrayOutputStream inflatedOut = new ByteArrayOutputStream();
      int readLength;
      byte[] block = new byte[8192];
      while ((readLength = in.read(block)) != -1)
        inflatedOut.write(block, 0, readLength);
      inflatedOut.flush();
      byte[] imageData = inflatedOut.toByteArray();
      // Compute the real length.
      int width = (int) getWidth();
      int height = (int) getHeight();
      int bitsPerPixel = getBitsPerPixel();
      int length = width * height * bitsPerPixel / 8;
      byte[] prunedData = new byte[length];
      // We can only deal with non-interlaced images.
      if (getInterlace() == 0) {
        int index = 0;
        for (int i = 0; i < length; i++) {
          if ((i * 8 / bitsPerPixel) % width == 0) {
            index++; // Skip the filter byte.
          }
          prunedData[i] = imageData[index++];
        }
      } else
        System.out.println("Couldn"t undo interlacing.");
      return prunedData;
    } catch (IOException ioe) {
    }
    return null;
  }
  public PNGChunk getChunk(String type) {
    for (int i = 0; i < mNumberOfChunks; i++)
      if (mChunks[i].getTypeString().equals(type))
        return mChunks[i];
    return null;
  }
}
class PNGChunk {
  private byte[] mType;
  private byte[] mData;
  public PNGChunk(byte[] type, byte[] data) {
    mType = type;
    mData = data;
  }
  public String getTypeString() {
    try {
      return new String(mType, "UTF8");
    } catch (UnsupportedEncodingException uee) {
      return "";
    }
  }
  public byte[] getData() {
    return mData;
  }
  public long getUnsignedInt(int offset) {
    long value = 0;
    for (int i = 0; i < 4; i++)
      value += (mData[offset + i] & 0xff) << ((3 - i) * 8);
    return value;
  }
  public short getUnsignedByte(int offset) {
    return (short) (mData[offset] & 0x00ff);
  }
}



Saving a Generated Graphic to a PNG or JPEG File

    
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.io.File;
import javax.imageio.ImageIO;
public class Main {
  public static void main(String[] argv) throws Exception {
    int width = 100;
    int height = 100;
    BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
    Graphics2D g2d = bufferedImage.createGraphics();
    g2d.setColor(Color.white);
    g2d.fillRect(0, 0, width, height);
    g2d.setColor(Color.black);
    g2d.fillOval(0, 0, width, height);
    g2d.dispose();
    RenderedImage rendImage = bufferedImage;
    File file = new File("newimage.png");
    ImageIO.write(rendImage, "png", file);
    file = new File("newimage.jpg");
    ImageIO.write(rendImage, "jpg", file);
  }
}