mev-4.0.01/source/org/tigr/util/PngEncoder.java

Code
Comments
Other
Rev Date Author Line
2 26 Feb 07 jari 1 /*
2 26 Feb 07 jari 2 Copyright @ 1999-2003, The Institute for Genomic Research (TIGR).
2 26 Feb 07 jari 3 All rights reserved.
2 26 Feb 07 jari 4 */
2 26 Feb 07 jari 5 /*
2 26 Feb 07 jari 6  * $RCSfile: PngEncoder.java,v $
2 26 Feb 07 jari 7  * $Revision: 1.2 $
2 26 Feb 07 jari 8  * $Date: 2005/03/10 15:27:01 $
2 26 Feb 07 jari 9  * $Author: braistedj $
2 26 Feb 07 jari 10  * $State: Exp $
2 26 Feb 07 jari 11  */
2 26 Feb 07 jari 12 package org.tigr.util;
2 26 Feb 07 jari 13
2 26 Feb 07 jari 14 /**
2 26 Feb 07 jari 15  * PngEncoder takes a Java Image object and creates a byte string which can be saved as a PNG file.
2 26 Feb 07 jari 16  * The Image is presumed to use the DirectColorModel.
2 26 Feb 07 jari 17  *
2 26 Feb 07 jari 18  * Thanks to Jay Denny at KeyPoint Software
2 26 Feb 07 jari 19  *    http://www.keypoint.com/
2 26 Feb 07 jari 20  * who let me develop this code on company time.
2 26 Feb 07 jari 21  *
2 26 Feb 07 jari 22  * You may contact me with (probably very-much-needed) improvements,
2 26 Feb 07 jari 23  * comments, and bug fixes at:
2 26 Feb 07 jari 24  *
2 26 Feb 07 jari 25  *   david@catcode.com
2 26 Feb 07 jari 26  *
2 26 Feb 07 jari 27  * This library is free software; you can redistribute it and/or
2 26 Feb 07 jari 28  * modify it under the terms of the GNU Lesser General Public
2 26 Feb 07 jari 29  * License as published by the Free Software Foundation; either
2 26 Feb 07 jari 30  * version 2.1 of the License, or (at your option) any later version.
2 26 Feb 07 jari 31  *
2 26 Feb 07 jari 32  * This library is distributed in the hope that it will be useful,
2 26 Feb 07 jari 33  * but WITHOUT ANY WARRANTY; without even the implied warranty of
2 26 Feb 07 jari 34  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
2 26 Feb 07 jari 35  * Lesser General Public License for more details.
2 26 Feb 07 jari 36  *
2 26 Feb 07 jari 37  * You should have received a copy of the GNU Lesser General Public
2 26 Feb 07 jari 38  * License along with this library; if not, write to the Free Software
2 26 Feb 07 jari 39  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
2 26 Feb 07 jari 40  * A copy of the GNU LGPL may be found at
2 26 Feb 07 jari 41  * http://www.gnu.org/copyleft/lesser.html,
2 26 Feb 07 jari 42  *
2 26 Feb 07 jari 43  * @author J. David Eisenberg
2 26 Feb 07 jari 44  * @version 1.4, 31 March 2000
2 26 Feb 07 jari 45  */
2 26 Feb 07 jari 46
2 26 Feb 07 jari 47 import java.awt.Color;
2 26 Feb 07 jari 48 import java.awt.Frame;
2 26 Feb 07 jari 49 import java.awt.Image;
2 26 Feb 07 jari 50 import java.awt.image.ImageObserver;
2 26 Feb 07 jari 51 import java.awt.image.PixelGrabber;
2 26 Feb 07 jari 52 import java.io.ByteArrayOutputStream;
2 26 Feb 07 jari 53 import java.io.IOException;
2 26 Feb 07 jari 54 import java.util.zip.CRC32;
2 26 Feb 07 jari 55 import java.util.zip.Deflater;
2 26 Feb 07 jari 56 import java.util.zip.DeflaterOutputStream;
2 26 Feb 07 jari 57
2 26 Feb 07 jari 58 import org.tigr.util.swing.ProgressBar;
2 26 Feb 07 jari 59
2 26 Feb 07 jari 60 public class PngEncoder {
2 26 Feb 07 jari 61     /** Constant specifying that alpha channel should be encoded. */
2 26 Feb 07 jari 62     public static final boolean ENCODE_ALPHA=true;
2 26 Feb 07 jari 63     /** Constant specifying that alpha channel should not be encoded. */
2 26 Feb 07 jari 64     public static final boolean NO_ALPHA=false;
2 26 Feb 07 jari 65     /** Constants for filters */
2 26 Feb 07 jari 66     public static final int FILTER_NONE = 0;
2 26 Feb 07 jari 67     public static final int FILTER_SUB = 1;
2 26 Feb 07 jari 68     public static final int FILTER_UP = 2;
2 26 Feb 07 jari 69     public static final int FILTER_LAST = 2;
2 26 Feb 07 jari 70     
2 26 Feb 07 jari 71     protected byte[] pngBytes;
2 26 Feb 07 jari 72     protected byte[] priorRow;
2 26 Feb 07 jari 73     protected byte[] leftBytes;
2 26 Feb 07 jari 74     protected Image image;
2 26 Feb 07 jari 75     protected int width, height;
2 26 Feb 07 jari 76     protected int bytePos, maxPos;
2 26 Feb 07 jari 77     protected int hdrPos, dataPos, endPos;
2 26 Feb 07 jari 78     protected CRC32 crc = new CRC32();
2 26 Feb 07 jari 79     protected long crcValue;
2 26 Feb 07 jari 80     protected boolean encodeAlpha;
2 26 Feb 07 jari 81     protected int filter;
2 26 Feb 07 jari 82     protected int bytesPerPixel;
2 26 Feb 07 jari 83     protected int compressionLevel;
2 26 Feb 07 jari 84     
2 26 Feb 07 jari 85     private Frame ParentFrame;
2 26 Feb 07 jari 86     
2 26 Feb 07 jari 87     /**
2 26 Feb 07 jari 88      * Class constructor
2 26 Feb 07 jari 89      *
2 26 Feb 07 jari 90      */
2 26 Feb 07 jari 91     public PngEncoder() {
2 26 Feb 07 jari 92   this(null, null, false, FILTER_NONE, 0 );
2 26 Feb 07 jari 93     }
2 26 Feb 07 jari 94     
2 26 Feb 07 jari 95     /**
2 26 Feb 07 jari 96      * Class constructor specifying Image to encode, with no alpha channel encoding.
2 26 Feb 07 jari 97      *
2 26 Feb 07 jari 98      * @param image A Java Image object which uses the DirectColorModel
2 26 Feb 07 jari 99      * @see java.awt.Image
2 26 Feb 07 jari 100      */
2 26 Feb 07 jari 101     public PngEncoder( Image image ) {
2 26 Feb 07 jari 102   this(null, image, false, FILTER_NONE, 0);
2 26 Feb 07 jari 103     }
2 26 Feb 07 jari 104     
2 26 Feb 07 jari 105     /**
2 26 Feb 07 jari 106      * Class constructor specifying Image to encode, and whether to encode alpha.
2 26 Feb 07 jari 107      *
2 26 Feb 07 jari 108      * @param image A Java Image object which uses the DirectColorModel
2 26 Feb 07 jari 109      * @param encodeAlpha Encode the alpha channel? false=no; true=yes
2 26 Feb 07 jari 110      * @see java.awt.Image
2 26 Feb 07 jari 111      */
2 26 Feb 07 jari 112     public PngEncoder( Image image, boolean encodeAlpha ) {
2 26 Feb 07 jari 113   this(null, image, encodeAlpha, FILTER_NONE, 0);
2 26 Feb 07 jari 114     }
2 26 Feb 07 jari 115     
2 26 Feb 07 jari 116     /**
2 26 Feb 07 jari 117      * Class constructor specifying Image to encode, whether to encode alpha, and filter to use.
2 26 Feb 07 jari 118      *
2 26 Feb 07 jari 119      * @param image A Java Image object which uses the DirectColorModel
2 26 Feb 07 jari 120      * @param encodeAlpha Encode the alpha channel? false=no; true=yes
2 26 Feb 07 jari 121      * @param whichFilter 0=none, 1=sub, 2=up
2 26 Feb 07 jari 122      * @see java.awt.Image
2 26 Feb 07 jari 123      */
2 26 Feb 07 jari 124     public PngEncoder( Image image, boolean encodeAlpha, int whichFilter ) {
2 26 Feb 07 jari 125   this(null, image, encodeAlpha, whichFilter, 0 );
2 26 Feb 07 jari 126     }
2 26 Feb 07 jari 127     
2 26 Feb 07 jari 128     
2 26 Feb 07 jari 129     /**
2 26 Feb 07 jari 130      * Class constructor specifying Image source to encode, whether to encode alpha, filter to use, and compression level.
2 26 Feb 07 jari 131      *
2 26 Feb 07 jari 132      * @param image A Java Image object
2 26 Feb 07 jari 133      * @param encodeAlpha Encode the alpha channel? false=no; true=yes
2 26 Feb 07 jari 134      * @param whichFilter 0=none, 1=sub, 2=up
2 26 Feb 07 jari 135      * @param compLevel 0..9
2 26 Feb 07 jari 136      * @see java.awt.Image
2 26 Feb 07 jari 137      */
2 26 Feb 07 jari 138     public PngEncoder(Frame parentFrame, Image image, boolean encodeAlpha, int whichFilter,
2 26 Feb 07 jari 139     int compLevel) {
2 26 Feb 07 jari 140   this.image = image;
2 26 Feb 07 jari 141   this.encodeAlpha = encodeAlpha;
2 26 Feb 07 jari 142   setFilter( whichFilter );
2 26 Feb 07 jari 143   if (compLevel >=0 && compLevel <=9) {
2 26 Feb 07 jari 144       this.compressionLevel = compLevel;
2 26 Feb 07 jari 145   }
2 26 Feb 07 jari 146   ParentFrame=parentFrame;
2 26 Feb 07 jari 147     }
2 26 Feb 07 jari 148     
2 26 Feb 07 jari 149     /**
2 26 Feb 07 jari 150      * Set the image to be encoded
2 26 Feb 07 jari 151      *
2 26 Feb 07 jari 152      * @param image A Java Image object which uses the DirectColorModel
2 26 Feb 07 jari 153      * @see java.awt.Image
2 26 Feb 07 jari 154      * @see java.awt.image.DirectColorModel
2 26 Feb 07 jari 155      */
2 26 Feb 07 jari 156     public void setImage( Image image ) {
2 26 Feb 07 jari 157   this.image = image;
2 26 Feb 07 jari 158   pngBytes = null;
2 26 Feb 07 jari 159     }
2 26 Feb 07 jari 160     
2 26 Feb 07 jari 161     /**
2 26 Feb 07 jari 162      * Creates an array of bytes that is the PNG equivalent of the current image, specifying whether to encode alpha or not.
2 26 Feb 07 jari 163      *
2 26 Feb 07 jari 164      * @param encodeAlpha boolean false=no alpha, true=encode alpha
2 26 Feb 07 jari 165      * @return an array of bytes, or null if there was a problem
2 26 Feb 07 jari 166      */
2 26 Feb 07 jari 167     public byte[] pngEncode( boolean encodeAlpha ) {
2 26 Feb 07 jari 168   byte[]  pngIdBytes = { -119, 80, 78, 71, 13, 10, 26, 10};
2 26 Feb 07 jari 169   int     i;
2 26 Feb 07 jari 170   
2 26 Feb 07 jari 171   if (image == null) {
2 26 Feb 07 jari 172       return null;
2 26 Feb 07 jari 173   }
2 26 Feb 07 jari 174   width = image.getWidth( null );
2 26 Feb 07 jari 175   height = image.getHeight( null );
2 26 Feb 07 jari 176   //no effect ?? this.image = image;
2 26 Feb 07 jari 177   
2 26 Feb 07 jari 178   /*
2 26 Feb 07 jari 179    * start with an array that is big enough to hold all the pixels
2 26 Feb 07 jari 180    * (plus filter bytes), and an extra 200 bytes for header info
2 26 Feb 07 jari 181    */
2 26 Feb 07 jari 182   pngBytes = new byte[((width+1) * height * 3) + 200];
2 26 Feb 07 jari 183   
2 26 Feb 07 jari 184   /*
2 26 Feb 07 jari 185    * keep track of largest byte written to the array
2 26 Feb 07 jari 186    */
2 26 Feb 07 jari 187   maxPos = 0;
2 26 Feb 07 jari 188   
2 26 Feb 07 jari 189   bytePos = writeBytes( pngIdBytes, 0 );
2 26 Feb 07 jari 190   hdrPos = bytePos;
2 26 Feb 07 jari 191   writeHeader();
2 26 Feb 07 jari 192   dataPos = bytePos;
2 26 Feb 07 jari 193   if (writeImageData()) {
2 26 Feb 07 jari 194       writeEnd();
2 26 Feb 07 jari 195       pngBytes = resizeByteArray( pngBytes, maxPos );
2 26 Feb 07 jari 196   } else {
2 26 Feb 07 jari 197       pngBytes = null;
2 26 Feb 07 jari 198   }
2 26 Feb 07 jari 199   return pngBytes;
2 26 Feb 07 jari 200     }
2 26 Feb 07 jari 201     
2 26 Feb 07 jari 202     /**
2 26 Feb 07 jari 203      * Creates an array of bytes that is the PNG equivalent of the current image.
2 26 Feb 07 jari 204      * Alpha encoding is determined by its setting in the constructor.
2 26 Feb 07 jari 205      *
2 26 Feb 07 jari 206      * @return an array of bytes, or null if there was a problem
2 26 Feb 07 jari 207      */
2 26 Feb 07 jari 208     public byte[] pngEncode() {
2 26 Feb 07 jari 209   return pngEncode( encodeAlpha );
2 26 Feb 07 jari 210     }
2 26 Feb 07 jari 211     
2 26 Feb 07 jari 212     /**
2 26 Feb 07 jari 213      * Set the alpha encoding on or off.
2 26 Feb 07 jari 214      *
2 26 Feb 07 jari 215      * @param encodeAlpha  false=no, true=yes
2 26 Feb 07 jari 216      */
2 26 Feb 07 jari 217     public void setEncodeAlpha( boolean encodeAlpha ) {
2 26 Feb 07 jari 218   this.encodeAlpha = encodeAlpha;
2 26 Feb 07 jari 219     }
2 26 Feb 07 jari 220     
2 26 Feb 07 jari 221     /**
2 26 Feb 07 jari 222      * Retrieve alpha encoding status.
2 26 Feb 07 jari 223      *
2 26 Feb 07 jari 224      * @return boolean false=no, true=yes
2 26 Feb 07 jari 225      */
2 26 Feb 07 jari 226     public boolean getEncodeAlpha() {
2 26 Feb 07 jari 227   return encodeAlpha;
2 26 Feb 07 jari 228     }
2 26 Feb 07 jari 229     
2 26 Feb 07 jari 230     /**
2 26 Feb 07 jari 231      * Set the filter to use
2 26 Feb 07 jari 232      *
2 26 Feb 07 jari 233      * @param whichFilter from constant list
2 26 Feb 07 jari 234      */
2 26 Feb 07 jari 235     public void setFilter( int whichFilter ) {
2 26 Feb 07 jari 236   this.filter = FILTER_NONE;
2 26 Feb 07 jari 237   if (whichFilter <= FILTER_LAST) {
2 26 Feb 07 jari 238       this.filter = whichFilter;
2 26 Feb 07 jari 239   }
2 26 Feb 07 jari 240     }
2 26 Feb 07 jari 241     
2 26 Feb 07 jari 242     /**
2 26 Feb 07 jari 243      * Retrieve filtering scheme
2 26 Feb 07 jari 244      *
2 26 Feb 07 jari 245      * @return int (see constant list)
2 26 Feb 07 jari 246      */
2 26 Feb 07 jari 247     public int getFilter() {
2 26 Feb 07 jari 248   return filter;
2 26 Feb 07 jari 249     }
2 26 Feb 07 jari 250     
2 26 Feb 07 jari 251     /**
2 26 Feb 07 jari 252      * Set the compression level to use
2 26 Feb 07 jari 253      *
2 26 Feb 07 jari 254      * @param level 0 through 9
2 26 Feb 07 jari 255      */
2 26 Feb 07 jari 256     public void setCompressionLevel( int level ) {
2 26 Feb 07 jari 257   if (level >= 0 && level <= 9) {
2 26 Feb 07 jari 258       this.compressionLevel = level;
2 26 Feb 07 jari 259   }
2 26 Feb 07 jari 260     }
2 26 Feb 07 jari 261     
2 26 Feb 07 jari 262     /**
2 26 Feb 07 jari 263      * Retrieve compression level
2 26 Feb 07 jari 264      *
2 26 Feb 07 jari 265      * @return int in range 0-9
2 26 Feb 07 jari 266      */
2 26 Feb 07 jari 267     public int getCompressionLevel() {
2 26 Feb 07 jari 268   return compressionLevel;
2 26 Feb 07 jari 269     }
2 26 Feb 07 jari 270     
2 26 Feb 07 jari 271     /**
2 26 Feb 07 jari 272      * Increase or decrease the length of a byte array.
2 26 Feb 07 jari 273      *
2 26 Feb 07 jari 274      * @param array The original array.
2 26 Feb 07 jari 275      * @param newLength The length you wish the new array to have.
2 26 Feb 07 jari 276      * @return Array of newly desired length. If shorter than the
2 26 Feb 07 jari 277      *         original, the trailing elements are truncated.
2 26 Feb 07 jari 278      */
2 26 Feb 07 jari 279     protected byte[] resizeByteArray( byte[] array, int newLength ) {
2 26 Feb 07 jari 280   byte[]  newArray = new byte[newLength];
2 26 Feb 07 jari 281   int     oldLength = array.length;
2 26 Feb 07 jari 282   
2 26 Feb 07 jari 283   System.arraycopy( array, 0, newArray, 0,
2 26 Feb 07 jari 284   Math.min( oldLength, newLength ) );
2 26 Feb 07 jari 285   return newArray;
2 26 Feb 07 jari 286     }
2 26 Feb 07 jari 287     
2 26 Feb 07 jari 288     /**
2 26 Feb 07 jari 289      * Write an array of bytes into the pngBytes array.
2 26 Feb 07 jari 290      * Note: This routine has the side effect of updating
2 26 Feb 07 jari 291      * maxPos, the largest element written in the array.
2 26 Feb 07 jari 292      * The array is resized by 1000 bytes or the length
2 26 Feb 07 jari 293      * of the data to be written, whichever is larger.
2 26 Feb 07 jari 294      *
2 26 Feb 07 jari 295      * @param data The data to be written into pngBytes.
2 26 Feb 07 jari 296      * @param offset The starting point to write to.
2 26 Feb 07 jari 297      * @return The next place to be written to in the pngBytes array.
2 26 Feb 07 jari 298      */
2 26 Feb 07 jari 299     protected int writeBytes( byte[] data, int offset ) {
2 26 Feb 07 jari 300   maxPos = Math.max( maxPos, offset + data.length );
2 26 Feb 07 jari 301   if (data.length + offset > pngBytes.length) {
2 26 Feb 07 jari 302       pngBytes = resizeByteArray( pngBytes, pngBytes.length +
2 26 Feb 07 jari 303       Math.max( 1000, data.length ) );
2 26 Feb 07 jari 304   }
2 26 Feb 07 jari 305   System.arraycopy( data, 0, pngBytes, offset, data.length );
2 26 Feb 07 jari 306   return offset + data.length;
2 26 Feb 07 jari 307     }
2 26 Feb 07 jari 308     
2 26 Feb 07 jari 309     /**
2 26 Feb 07 jari 310      * Write an array of bytes into the pngBytes array, specifying number of bytes to write.
2 26 Feb 07 jari 311      * Note: This routine has the side effect of updating
2 26 Feb 07 jari 312      * maxPos, the largest element written in the array.
2 26 Feb 07 jari 313      * The array is resized by 1000 bytes or the length
2 26 Feb 07 jari 314      * of the data to be written, whichever is larger.
2 26 Feb 07 jari 315      *
2 26 Feb 07 jari 316      * @param data The data to be written into pngBytes.
2 26 Feb 07 jari 317      * @param nBytes The number of bytes to be written.
2 26 Feb 07 jari 318      * @param offset The starting point to write to.
2 26 Feb 07 jari 319      * @return The next place to be written to in the pngBytes array.
2 26 Feb 07 jari 320      */
2 26 Feb 07 jari 321     protected int writeBytes( byte[] data, int nBytes, int offset ) {
2 26 Feb 07 jari 322   maxPos = Math.max( maxPos, offset + nBytes );
2 26 Feb 07 jari 323   if (nBytes + offset > pngBytes.length) {
2 26 Feb 07 jari 324       pngBytes = resizeByteArray( pngBytes, pngBytes.length +
2 26 Feb 07 jari 325       Math.max( 1000, nBytes ) );
2 26 Feb 07 jari 326   }
2 26 Feb 07 jari 327   System.arraycopy( data, 0, pngBytes, offset, nBytes );
2 26 Feb 07 jari 328   return offset + nBytes;
2 26 Feb 07 jari 329     }
2 26 Feb 07 jari 330     
2 26 Feb 07 jari 331     /**
2 26 Feb 07 jari 332      * Write a two-byte integer into the pngBytes array at a given position.
2 26 Feb 07 jari 333      *
2 26 Feb 07 jari 334      * @param n The integer to be written into pngBytes.
2 26 Feb 07 jari 335      * @param offset The starting point to write to.
2 26 Feb 07 jari 336      * @return The next place to be written to in the pngBytes array.
2 26 Feb 07 jari 337      */
2 26 Feb 07 jari 338     protected int writeInt2( int n, int offset ) {
2 26 Feb 07 jari 339   byte[] temp = { (byte)((n >> 8) & 0xff),
2 26 Feb 07 jari 340   (byte) (n & 0xff)};
2 26 Feb 07 jari 341   return writeBytes( temp, offset );
2 26 Feb 07 jari 342     }
2 26 Feb 07 jari 343     
2 26 Feb 07 jari 344     /**
2 26 Feb 07 jari 345      * Write a four-byte integer into the pngBytes array at a given position.
2 26 Feb 07 jari 346      *
2 26 Feb 07 jari 347      * @param n The integer to be written into pngBytes.
2 26 Feb 07 jari 348      * @param offset The starting point to write to.
2 26 Feb 07 jari 349      * @return The next place to be written to in the pngBytes array.
2 26 Feb 07 jari 350      */
2 26 Feb 07 jari 351     protected int writeInt4( int n, int offset ) {
2 26 Feb 07 jari 352   byte[] temp = { (byte)((n >> 24) & 0xff),
2 26 Feb 07 jari 353   (byte) ((n >> 16) & 0xff ),
2 26 Feb 07 jari 354   (byte) ((n >> 8) & 0xff ),
2 26 Feb 07 jari 355   (byte) ( n & 0xff )};
2 26 Feb 07 jari 356   return writeBytes( temp, offset );
2 26 Feb 07 jari 357     }
2 26 Feb 07 jari 358     
2 26 Feb 07 jari 359     /**
2 26 Feb 07 jari 360      * Write a single byte into the pngBytes array at a given position.
2 26 Feb 07 jari 361      *
2 26 Feb 07 jari 362      * @param n The integer to be written into pngBytes.
2 26 Feb 07 jari 363      * @param offset The starting point to write to.
2 26 Feb 07 jari 364      * @return The next place to be written to in the pngBytes array.
2 26 Feb 07 jari 365      */
2 26 Feb 07 jari 366     protected int writeByte( int b, int offset ) {
2 26 Feb 07 jari 367   byte[] temp = { (byte) b};
2 26 Feb 07 jari 368   return writeBytes( temp, offset );
2 26 Feb 07 jari 369     }
2 26 Feb 07 jari 370     
2 26 Feb 07 jari 371     /**
2 26 Feb 07 jari 372      * Write a string into the pngBytes array at a given position.
2 26 Feb 07 jari 373      * This uses the getBytes method, so the encoding used will
2 26 Feb 07 jari 374      * be its default.
2 26 Feb 07 jari 375      *
2 26 Feb 07 jari 376      * @param n The integer to be written into pngBytes.
2 26 Feb 07 jari 377      * @param offset The starting point to write to.
2 26 Feb 07 jari 378      * @return The next place to be written to in the pngBytes array.
2 26 Feb 07 jari 379      * @see java.lang.String#getBytes()
2 26 Feb 07 jari 380      */
2 26 Feb 07 jari 381     protected int writeString( String s, int offset ) {
2 26 Feb 07 jari 382   return writeBytes( s.getBytes(), offset );
2 26 Feb 07 jari 383     }
2 26 Feb 07 jari 384     
2 26 Feb 07 jari 385     /**
2 26 Feb 07 jari 386      * Write a PNG "IHDR" chunk into the pngBytes array.
2 26 Feb 07 jari 387      */
2 26 Feb 07 jari 388     protected void writeHeader() {
2 26 Feb 07 jari 389   int startPos;
2 26 Feb 07 jari 390   
2 26 Feb 07 jari 391   startPos = bytePos = writeInt4( 13, bytePos );
2 26 Feb 07 jari 392   bytePos = writeString( "IHDR", bytePos );
2 26 Feb 07 jari 393   width = image.getWidth( null );
2 26 Feb 07 jari 394   height = image.getHeight( null );
2 26 Feb 07 jari 395   bytePos = writeInt4( width, bytePos );
2 26 Feb 07 jari 396   bytePos = writeInt4( height, bytePos );
2 26 Feb 07 jari 397   bytePos = writeByte( 8, bytePos ); // bit depth
2 26 Feb 07 jari 398   bytePos = writeByte( (encodeAlpha) ? 6 : 2, bytePos ); // direct model
2 26 Feb 07 jari 399   bytePos = writeByte( 0, bytePos ); // compression method
2 26 Feb 07 jari 400   bytePos = writeByte( 0, bytePos ); // filter method
2 26 Feb 07 jari 401   bytePos = writeByte( 0, bytePos ); // no interlace
2 26 Feb 07 jari 402   crc.reset();
2 26 Feb 07 jari 403   crc.update( pngBytes, startPos, bytePos-startPos );
2 26 Feb 07 jari 404   crcValue = crc.getValue();
2 26 Feb 07 jari 405   bytePos = writeInt4( (int) crcValue, bytePos );
2 26 Feb 07 jari 406     }
2 26 Feb 07 jari 407     
2 26 Feb 07 jari 408     /**
2 26 Feb 07 jari 409      * Perform "sub" filtering on the given row.
2 26 Feb 07 jari 410      * Uses temporary array leftBytes to store the original values
2 26 Feb 07 jari 411      * of the previous pixels.  The array is 16 bytes long, which
2 26 Feb 07 jari 412      * will easily hold two-byte samples plus two-byte alpha.
2 26 Feb 07 jari 413      *
2 26 Feb 07 jari 414      * @param pixels The array holding the scan lines being built
2 26 Feb 07 jari 415      * @param startPos Starting position within pixels of bytes to be filtered.
2 26 Feb 07 jari 416      * @param width Width of a scanline in pixels.
2 26 Feb 07 jari 417      */
2 26 Feb 07 jari 418     protected void filterSub( byte[] pixels, int startPos, int width ) {
2 26 Feb 07 jari 419   int i;
2 26 Feb 07 jari 420   int offset = bytesPerPixel;
2 26 Feb 07 jari 421   int actualStart = startPos + offset;
2 26 Feb 07 jari 422   int nBytes = width * bytesPerPixel;
2 26 Feb 07 jari 423   int leftInsert = offset;
2 26 Feb 07 jari 424   int leftExtract = 0;
2 26 Feb 07 jari 425   byte current_byte;
2 26 Feb 07 jari 426   
2 26 Feb 07 jari 427   for (i=actualStart; i < startPos + nBytes; i++) {
2 26 Feb 07 jari 428       leftBytes[leftInsert] =  pixels[i];
2 26 Feb 07 jari 429       pixels[i] = (byte) ((pixels[i] - leftBytes[leftExtract]) % 256);
2 26 Feb 07 jari 430       leftInsert = (leftInsert+1) % 0x0f;
2 26 Feb 07 jari 431       leftExtract = (leftExtract + 1) % 0x0f;
2 26 Feb 07 jari 432   }
2 26 Feb 07 jari 433     }
2 26 Feb 07 jari 434     
2 26 Feb 07 jari 435     /**
2 26 Feb 07 jari 436      * Perform "up" filtering on the given row.
2 26 Feb 07 jari 437      * Side effect: refills the prior row with current row
2 26 Feb 07 jari 438      *
2 26 Feb 07 jari 439      * @param pixels The array holding the scan lines being built
2 26 Feb 07 jari 440      * @param startPos Starting position within pixels of bytes to be filtered.
2 26 Feb 07 jari 441      * @param width Width of a scanline in pixels.
2 26 Feb 07 jari 442      */
2 26 Feb 07 jari 443     protected void filterUp( byte[] pixels, int startPos, int width ) {
2 26 Feb 07 jari 444   int     i, nBytes;
2 26 Feb 07 jari 445   byte    current_byte;
2 26 Feb 07 jari 446   
2 26 Feb 07 jari 447   nBytes = width * bytesPerPixel;
2 26 Feb 07 jari 448   
2 26 Feb 07 jari 449   for (i=0; i < nBytes; i++) {
2 26 Feb 07 jari 450       current_byte = pixels[startPos + i];
2 26 Feb 07 jari 451       pixels[startPos + i] = (byte) ((pixels[startPos  + i] - priorRow[i]) % 256);
2 26 Feb 07 jari 452       priorRow[i] = current_byte;
2 26 Feb 07 jari 453   }
2 26 Feb 07 jari 454     }
2 26 Feb 07 jari 455     
2 26 Feb 07 jari 456     /**
2 26 Feb 07 jari 457      * Write the image data into the pngBytes array.
2 26 Feb 07 jari 458      * This will write one or more PNG "IDAT" chunks. In order
2 26 Feb 07 jari 459      * to conserve memory, this method grabs as many rows as will
2 26 Feb 07 jari 460      * fit into 32K bytes, or the whole image; whichever is less.
2 26 Feb 07 jari 461      *
2 26 Feb 07 jari 462      *
2 26 Feb 07 jari 463      * @return true if no errors; false if error grabbing pixels
2 26 Feb 07 jari 464      */
2 26 Feb 07 jari 465     protected boolean writeImageData() {
2 26 Feb 07 jari 466   int rowsLeft = height;  // number of rows remaining to write
2 26 Feb 07 jari 467   int startRow = 0;       // starting row to process this time through
2 26 Feb 07 jari 468   int nRows;              // how many rows to grab at a time
2 26 Feb 07 jari 469   
2 26 Feb 07 jari 470   byte[] scanLines;       // the scan lines to be compressed
2 26 Feb 07 jari 471   int scanPos;            // where we are in the scan lines
2 26 Feb 07 jari 472   int startPos;           // where this line's actual pixels start (used for filtering)
2 26 Feb 07 jari 473   
2 26 Feb 07 jari 474   byte[] compressedLines; // the resultant compressed lines
2 26 Feb 07 jari 475   int nCompressed;        // how big is the compressed area?
2 26 Feb 07 jari 476   
2 26 Feb 07 jari 477   int depth;              // color depth ( handle only 8 or 32 )
2 26 Feb 07 jari 478   
2 26 Feb 07 jari 479   PixelGrabber pg;
2 26 Feb 07 jari 480   
2 26 Feb 07 jari 481   bytesPerPixel = (encodeAlpha) ? 4 : 3;
2 26 Feb 07 jari 482   
2 26 Feb 07 jari 483   Deflater scrunch = new Deflater( compressionLevel );
2 26 Feb 07 jari 484   ByteArrayOutputStream outBytes =
2 26 Feb 07 jari 485   new ByteArrayOutputStream(1024);
2 26 Feb 07 jari 486   
2 26 Feb 07 jari 487   DeflaterOutputStream compBytes =new DeflaterOutputStream( outBytes, scrunch );
2 26 Feb 07 jari 488   double Factor=200/(double)height;
2 26 Feb 07 jari 489   long CurrentProgress=0;
2 26 Feb 07 jari 490   long OldCurrentProgress=0;
2 26 Feb 07 jari 491   
2 26 Feb 07 jari 492   try {
2 26 Feb 07 jari 493       ProgressBar MyProgressBar=new ProgressBar(ParentFrame,"Image processing",new Color(0,0,128),new Color(0,128,225),Color.black,200);
2 26 Feb 07 jari 494       while (rowsLeft > 0) {
2 26 Feb 07 jari 495     CurrentProgress=(int)((startRow)*Factor);
2 26 Feb 07 jari 496     if (CurrentProgress>OldCurrentProgress) {
2 26 Feb 07 jari 497         MyProgressBar.set((int)CurrentProgress);
2 26 Feb 07 jari 498         OldCurrentProgress=CurrentProgress;
2 26 Feb 07 jari 499     }
2 26 Feb 07 jari 500     nRows = Math.min( 32767 / (width*(bytesPerPixel+1)), rowsLeft );
2 26 Feb 07 jari 501     // nRows = rowsLeft;
2 26 Feb 07 jari 502     
2 26 Feb 07 jari 503     int[] pixels = new int[width * nRows];
2 26 Feb 07 jari 504     
2 26 Feb 07 jari 505     pg = new PixelGrabber(image, 0, startRow,
2 26 Feb 07 jari 506     width, nRows, pixels, 0, width);
2 26 Feb 07 jari 507     try {
2 26 Feb 07 jari 508         pg.grabPixels();
2 26 Feb 07 jari 509     } catch (Exception e) {
2 26 Feb 07 jari 510         System.err.println("interrupted waiting for pixels!");
2 26 Feb 07 jari 511         return false;
2 26 Feb 07 jari 512     }
2 26 Feb 07 jari 513     if ((pg.getStatus() & ImageObserver.ABORT) != 0) {
2 26 Feb 07 jari 514         System.err.println("image fetch aborted or errored");
2 26 Feb 07 jari 515         return false;
2 26 Feb 07 jari 516     }
2 26 Feb 07 jari 517     
2 26 Feb 07 jari 518     /*
2 26 Feb 07 jari 519      * Create a data chunk. scanLines adds "nRows" for
2 26 Feb 07 jari 520      * the filter bytes.
2 26 Feb 07 jari 521      */
2 26 Feb 07 jari 522     scanLines = new byte[width * nRows * bytesPerPixel +  nRows];
2 26 Feb 07 jari 523     
2 26 Feb 07 jari 524     if (filter == FILTER_SUB) {
2 26 Feb 07 jari 525         leftBytes = new byte[16];
2 26 Feb 07 jari 526     }
2 26 Feb 07 jari 527     if (filter == FILTER_UP) {
2 26 Feb 07 jari 528         priorRow = new byte[width*bytesPerPixel];
2 26 Feb 07 jari 529     }
2 26 Feb 07 jari 530     
2 26 Feb 07 jari 531     scanPos = 0;
2 26 Feb 07 jari 532     startPos = 1;
2 26 Feb 07 jari 533     for (int i=0; i<width*nRows; i++) {
2 26 Feb 07 jari 534         if (i % width == 0) {
2 26 Feb 07 jari 535       scanLines[scanPos++] = (byte) filter;
2 26 Feb 07 jari 536       startPos = scanPos;
2 26 Feb 07 jari 537         }
2 26 Feb 07 jari 538         scanLines[scanPos++] = (byte) ((pixels[i] >> 16) & 0xff);
2 26 Feb 07 jari 539         scanLines[scanPos++] = (byte) ((pixels[i] >>  8) & 0xff);
2 26 Feb 07 jari 540         scanLines[scanPos++] = (byte) ((pixels[i]      ) & 0xff);
2 26 Feb 07 jari 541         if (encodeAlpha) {
2 26 Feb 07 jari 542       scanLines[scanPos++] = (byte) ((pixels[i] >> 24) & 0xff );
2 26 Feb 07 jari 543         }
2 26 Feb 07 jari 544         if ((i % width == width-1) && (filter != FILTER_NONE)) {
2 26 Feb 07 jari 545       if (filter == FILTER_SUB) {
2 26 Feb 07 jari 546           filterSub( scanLines, startPos, width );
2 26 Feb 07 jari 547       }
2 26 Feb 07 jari 548       if (filter == FILTER_UP) {
2 26 Feb 07 jari 549           filterUp( scanLines, startPos, width );
2 26 Feb 07 jari 550       }
2 26 Feb 07 jari 551         }
2 26 Feb 07 jari 552     }
2 26 Feb 07 jari 553     
2 26 Feb 07 jari 554     /*
2 26 Feb 07 jari 555      * Write these lines to the output area
2 26 Feb 07 jari 556      */
2 26 Feb 07 jari 557     compBytes.write( scanLines, 0, scanPos );
2 26 Feb 07 jari 558     
2 26 Feb 07 jari 559     
2 26 Feb 07 jari 560     startRow += nRows;
2 26 Feb 07 jari 561     rowsLeft -= nRows;
2 26 Feb 07 jari 562       }
2 26 Feb 07 jari 563       compBytes.close();
2 26 Feb 07 jari 564       
2 26 Feb 07 jari 565       /*
2 26 Feb 07 jari 566        * Write the compressed bytes
2 26 Feb 07 jari 567        */
2 26 Feb 07 jari 568       compressedLines = outBytes.toByteArray();
2 26 Feb 07 jari 569       nCompressed = compressedLines.length;
2 26 Feb 07 jari 570       
2 26 Feb 07 jari 571       crc.reset();
2 26 Feb 07 jari 572       bytePos = writeInt4( nCompressed, bytePos );
2 26 Feb 07 jari 573       bytePos = writeString("IDAT", bytePos );
2 26 Feb 07 jari 574       crc.update("IDAT".getBytes());
2 26 Feb 07 jari 575       bytePos = writeBytes( compressedLines, nCompressed, bytePos );
2 26 Feb 07 jari 576       crc.update( compressedLines, 0, nCompressed );
2 26 Feb 07 jari 577       
2 26 Feb 07 jari 578       crcValue = crc.getValue();
2 26 Feb 07 jari 579       bytePos = writeInt4( (int) crcValue, bytePos );
2 26 Feb 07 jari 580       scrunch.finish();
2 26 Feb 07 jari 581       MyProgressBar.dispose();
2 26 Feb 07 jari 582       return true;
2 26 Feb 07 jari 583   } catch (IOException e) {
2 26 Feb 07 jari 584       System.err.println( e.toString());
2 26 Feb 07 jari 585       return false;
2 26 Feb 07 jari 586   }
2 26 Feb 07 jari 587     }
2 26 Feb 07 jari 588     
2 26 Feb 07 jari 589     /**
2 26 Feb 07 jari 590      * Write a PNG "IEND" chunk into the pngBytes array.
2 26 Feb 07 jari 591      */
2 26 Feb 07 jari 592     protected void writeEnd() {
2 26 Feb 07 jari 593   bytePos = writeInt4( 0, bytePos );
2 26 Feb 07 jari 594   bytePos = writeString( "IEND", bytePos );
2 26 Feb 07 jari 595   crc.reset();
2 26 Feb 07 jari 596   crc.update("IEND".getBytes());
2 26 Feb 07 jari 597   crcValue = crc.getValue();
2 26 Feb 07 jari 598   bytePos = writeInt4( (int) crcValue, bytePos );
2 26 Feb 07 jari 599     }
2 26 Feb 07 jari 600 }
2 26 Feb 07 jari 601