gt:         if (this.writeImageData()) {
context: package rene.util;
  
  /**
   * 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.
   *
   * Thanks to Jay Denny at KeyPoint Software
   *    http://www.keypoint.com/
   * who let me develop this code on company time.
   *
   * You may contact me with (probably very-much-needed) improvements,
   * comments, and bug fixes at:
   *
   *   david@catcode.com
   *
   * 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.
   *
   * 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.
   *
   * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   * A copy of the GNU LGPL may be found at
   * http://www.gnu.org/copyleft/lesser.html,
   *
   * @author J. David Eisenberg
   * @version 1.4, 31 March 2000
   */
  
  import java.awt.*;
  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;
  
  public class PngEncoder extends Object {
      /**
       * 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 filters
       */
      public static final int FILTER_NONE = 0;
      public static final int FILTER_SUB = 1;
      public static final int FILTER_UP = 2;
      public static final int FILTER_LAST = 2;
  
      protected byte[] pngBytes;
      protected byte[] priorRow;
      protected byte[] leftBytes;
      protected Image image;
      protected int width, height;
      protected int bytePos, maxPos;
      protected int hdrPos, dataPos, endPos;
      protected CRC32 crc = new CRC32();
      protected long crcValue;
      protected boolean encodeAlpha;
      protected int filter;
      protected int bytesPerPixel;
      protected int compressionLevel;
      protected double DPI = 300;
  
      /**
       * Class constructor
       */
      public PngEncoder() {
          this(null, false, PngEncoder.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, PngEncoder.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, PngEncoder.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
       * @see java.awt.Image
       */
      public PngEncoder(Image image, boolean encodeAlpha, int whichFilter,
              int compLevel) {
          this.image = image;
          this.encodeAlpha = encodeAlpha;
          this.setFilter(whichFilter);
          if (compLevel >= 0 && compLevel <= 9) {
              this.compressionLevel = compLevel;
          }
      }
  
      /**
       * 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 i;
          final int offset = this.bytesPerPixel;
          final int actualStart = startPos + offset;
          final int nBytes = width * this.bytesPerPixel;
          int leftInsert = offset;
          int leftExtract = 0;
          for (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) {
          int i, nBytes;
          byte current_byte;
  
          nBytes = width * this.bytesPerPixel;
  
          for (i = 0; i < nBytes; i++) {
              current_byte = pixels[startPos + i];
              pixels[startPos + i] = (byte) ((pixels[startPos + i] - this.priorRow[i]) % 256);
              this.priorRow[i] = current_byte;
          }
      }
  
      /**
       * Retrieve compression level
       *
       * @return int in range 0-9
       */
      public int getCompressionLevel() {
          return this.compressionLevel;
      }
  
      /**
       * Retrieve alpha encoding status.
       *
       * @return boolean false=no, true=yes
       */
      public boolean getEncodeAlpha() {
          return this.encodeAlpha;
      }
  
      /**
       * Retrieve filtering scheme
       *
       * @return int (see constant list)
       */
      public int getFilter() {
          return this.filter;
      }
  
      /**
       * 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 this.pngEncode(this.encodeAlpha);
      }
  
      /**
       * 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) {
          final 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 = this.writeBytes(pngIdBytes, 0);
          this.hdrPos = this.bytePos;
          this.writeHeader();
          this.writePhys();
          this.dataPos = this.bytePos;
  