Back to index

moin  1.9.0~rc2
Public Member Functions | Protected Member Functions | Protected Attributes | Static Protected Attributes | Package Attributes
com.keypoint.PngEncoderIndexed Class Reference

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

Collaboration diagram for com.keypoint.PngEncoderIndexed:
Collaboration graph
[legend]

List of all members.

Public Member Functions

 PngEncoderIndexed ()
 Class constructor.
 PngEncoderIndexed (Image image)
 Class constructor specifying Image to encode, with no alpha channel encoding.
 PngEncoderIndexed (Image image, int compLevel)
 Class constructor specifying Image source to encode, and compression level.
void setImage (Image image)
 Set the image to be encoded.
byte[] pngEncode ()
 Creates an array of bytes that is the PNG equivalent of the current image.
void setCompressionLevel (int level)
 Set the compression level to use.
int getCompressionLevel ()
 Retrieve compression level.

Protected Member Functions

byte[] resizeByteArray (byte[] array, int newLength)
 Increase or decrease the length of a byte array.
int writeBytes (byte[] data, int offset)
 Write an array of bytes into the pngBytes array.
int writeBytes (byte[] data, int nBytes, int offset)
 Write an array of bytes into the pngBytes array, specifying number of bytes to write.
int writeInt2 (int n, int offset)
 Write a two-byte integer into the pngBytes array at a given position.
int writeInt4 (int n, int offset)
 Write a four-byte integer into the pngBytes array at a given position.
int writeByte (int b, int offset)
 Write a single byte into the pngBytes array at a given position.
void writeHeader ()
 Write a PNG "IHDR" chunk into the pngBytes array.
boolean writeImageData ()
 Write the image data into the pngBytes array.
void writeEnd ()
 Write a PNG "IEND" chunk into the pngBytes array.

Protected Attributes

byte[] pngBytes
 The png bytes.
Image image
 The image.
int width
 The width.
int bytePos
 The byte position.
CRC32 crc = new CRC32()
 CRC.
long crcValue
 The CRC value.
int compressionLevel
 The compression level.

Static Protected Attributes

static final byte IHDR [] = {73, 72, 68, 82}
 IHDR tag.
static final byte PLTE [] = {80, 76, 84, 69}
 PLTE tag.
static final byte tRNS [] = {116, 82, 78, 83}
 tRNS tag.
static final byte IDAT [] = {73, 68, 65, 84}
 IDAT tag.
static final byte IEND [] = {73, 69, 78, 68}
 IEND tag.
static final int DEFAULT_COMPRESSION = 5

Package Attributes

int height
int maxPos

Detailed Description

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.nosp@m.@cat.nosp@m.code..nosp@m.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.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

Definition at line 55 of file PngEncoderIndexed.java.


Constructor & Destructor Documentation

Class constructor.

Definition at line 98 of file PngEncoderIndexed.java.

                               {
        this(null, DEFAULT_COMPRESSION);
    }

Class constructor specifying Image to encode, with no alpha channel encoding.

Parameters:
imageA Java Image object which uses the DirectColorModel
See also:
java.awt.Image

Definition at line 108 of file PngEncoderIndexed.java.

com.keypoint.PngEncoderIndexed.PngEncoderIndexed ( Image  image,
int  compLevel 
) [inline]

Class constructor specifying Image source to encode, and compression level.

Parameters:
imageA Java Image object
compLevel0..9
See also:
java.awt.Image

Definition at line 120 of file PngEncoderIndexed.java.

                                                         {
        this.image = image;
        if (compLevel >= 0 && compLevel <= 9) {
            this.compressionLevel = compLevel;
        }
    }

Member Function Documentation

Retrieve compression level.

Returns:
int in range 0-9

Definition at line 194 of file PngEncoderIndexed.java.

                                     {
        return compressionLevel;
    }

Creates an array of bytes that is the PNG equivalent of the current image.

Returns:
an array of bytes, or null if there was a problem

Definition at line 144 of file PngEncoderIndexed.java.

                              {
        byte[]  pngIdBytes = {-119, 80, 78, 71, 13, 10, 26, 10};

        if (image == null) {
            return null;
        }
        width = image.getWidth(null);
        height = 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
         */
        pngBytes = new byte[((width + 1) * height * 3) + 200];

        /*
         * keep track of largest byte written to the array
         */
        maxPos = 0;

        bytePos = writeBytes(pngIdBytes, 0);
        //hdrPos = bytePos;
        writeHeader();
        //dataPos = bytePos;
        if (writeImageData()) {
            writeEnd();
            pngBytes = resizeByteArray(pngBytes, maxPos);
        }
        else {
            pngBytes = null;
        }
        return pngBytes;
    }

Here is the call graph for this function:

Here is the caller graph for this function:

byte [] com.keypoint.PngEncoderIndexed.resizeByteArray ( byte[]  array,
int  newLength 
) [inline, protected]

Increase or decrease the length of a byte array.

Parameters:
arrayThe original array.
newLengthThe length you wish the new array to have.
Returns:
Array of newly desired length. If shorter than the original, the trailing elements are truncated.

Definition at line 206 of file PngEncoderIndexed.java.

                                                                  {
        byte[]  newArray = new byte[newLength];
        int     oldLength = array.length;

        System.arraycopy(array, 0, newArray, 0, Math.min(oldLength, newLength));
        return newArray;
    }

Here is the caller graph for this function:

Set the compression level to use.

Parameters:
level0 through 9

Definition at line 183 of file PngEncoderIndexed.java.

                                               {
        if (level >= 0 && level <= 9) {
            this.compressionLevel = level;
        }
    }
void com.keypoint.PngEncoderIndexed.setImage ( Image  image) [inline]

Set the image to be encoded.

Parameters:
imageA Java Image object which uses the DirectColorModel
See also:
java.awt.Image
java.awt.image.DirectColorModel

Definition at line 134 of file PngEncoderIndexed.java.

                                      {
        this.image = image;
        pngBytes = null;
    }
int com.keypoint.PngEncoderIndexed.writeByte ( int  b,
int  offset 
) [inline, protected]

Write a single byte into the pngBytes array at a given position.

Parameters:
bThe integer to be written into pngBytes.
offsetThe starting point to write to.
Returns:
The next place to be written to in the pngBytes array.

Definition at line 289 of file PngEncoderIndexed.java.

                                               {
        byte[] temp = {(byte) b};
        return writeBytes(temp, offset);
    }

Here is the call graph for this function:

Here is the caller graph for this function:

int com.keypoint.PngEncoderIndexed.writeBytes ( byte[]  data,
int  offset 
) [inline, protected]

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.

Parameters:
dataThe data to be written into pngBytes.
offsetThe starting point to write to.
Returns:
The next place to be written to in the pngBytes array.

Definition at line 225 of file PngEncoderIndexed.java.

                                                      {
        maxPos = Math.max(maxPos, offset + data.length);
        if (data.length + offset > pngBytes.length) {
            pngBytes = resizeByteArray(pngBytes, pngBytes.length + Math.max(1000, data.length));
        }
        System.arraycopy(data, 0, pngBytes, offset, data.length);
        return offset + data.length;
    }

Here is the call graph for this function:

Here is the caller graph for this function:

int com.keypoint.PngEncoderIndexed.writeBytes ( byte[]  data,
int  nBytes,
int  offset 
) [inline, protected]

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.

Parameters:
dataThe data to be written into pngBytes.
nBytesThe number of bytes to be written.
offsetThe starting point to write to.
Returns:
The next place to be written to in the pngBytes array.

Definition at line 246 of file PngEncoderIndexed.java.

                                                                  {
        maxPos = Math.max(maxPos, offset + nBytes);
        if (nBytes + offset > pngBytes.length) {
            pngBytes = resizeByteArray(pngBytes, pngBytes.length + Math.max(1000, nBytes));
        }
        System.arraycopy(data, 0, pngBytes, offset, nBytes);
        return offset + nBytes;
    }

Here is the call graph for this function:

void com.keypoint.PngEncoderIndexed.writeEnd ( ) [inline, protected]

Write a PNG "IEND" chunk into the pngBytes array.

Definition at line 498 of file PngEncoderIndexed.java.

                              {
        bytePos = writeInt4(0, bytePos);
        bytePos = writeBytes(IEND, bytePos);
        crc.reset();
        crc.update(IEND);
        crcValue = crc.getValue();
        bytePos = writeInt4((int) crcValue, bytePos);
    }

Here is the call graph for this function:

Here is the caller graph for this function:

void com.keypoint.PngEncoderIndexed.writeHeader ( ) [inline, protected]

Write a PNG "IHDR" chunk into the pngBytes array.

Definition at line 297 of file PngEncoderIndexed.java.

                                 {
        int startPos;

        startPos = bytePos = writeInt4(13, bytePos);
        bytePos = writeBytes(IHDR, bytePos);
        width = image.getWidth(null);
        height = image.getHeight(null);
        bytePos = writeInt4(width, bytePos);
        bytePos = writeInt4(height, bytePos);
        bytePos = writeByte(8, bytePos); // bit depth
        bytePos = writeByte(3, bytePos); // palette
        bytePos = writeByte(0, bytePos); // compression method
        bytePos = writeByte(0, bytePos); // filter method
        bytePos = writeByte(0, bytePos); // no interlace
        crc.reset();
        crc.update(pngBytes, startPos, bytePos - startPos);
        crcValue = crc.getValue();
        bytePos = writeInt4((int) crcValue, bytePos);
    }

Here is the call graph for this function:

Here is the caller graph for this function:

boolean com.keypoint.PngEncoderIndexed.writeImageData ( ) [inline, protected]

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.

Returns:
true if no errors; false if error grabbing pixels

Definition at line 326 of file PngEncoderIndexed.java.

                                       {
        int rowsLeft;  // number of rows remaining to write
        int startRow;       // 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 )

              // palette without transparency (TWikiDrawPlugin does not need it)
              Acme.IntHashtable palette = new Acme.IntHashtable();
              PngEncoderHashitem item;
              int paletteIndex = 0, transIndex = -1, transRGBA = 0;

        PixelGrabber pg;

        Deflater scrunch = new Deflater(compressionLevel);
        ByteArrayOutputStream outBytes = new ByteArrayOutputStream(1024);

        DeflaterOutputStream compBytes = new DeflaterOutputStream(outBytes, scrunch);
        try {
                     /*
                      * create palette for image
                      */
                     rowsLeft = height;
                     startRow = 0;
            while (rowsLeft > 0) {
                nRows = Math.min(64000 / (width * 4), rowsLeft);
                nRows = Math.max(nRows, 1);

                int[] pixels = new int[width * nRows];

                pg = new PixelGrabber(image, 0, startRow, width, nRows,
                                                                 pixels, 0, 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;
                }

                scanPos = 0;
                startPos = 1;
                for (int i = 0; i < width * nRows; i++) {
                                   int rgba = pixels[i];
                                   item = (PngEncoderHashitem) palette.get( rgba );
                                   if (item == null) {
                                          if (paletteIndex >= 256)
                                                 throw new IOException("too many colors for a PNG");
                                          item = new PngEncoderHashitem(rgba, 1, paletteIndex, false);
                                          palette.put( rgba, item );
                                          ++paletteIndex;
                                   } else
                                          ++item.count;
                }

                startRow += nRows;
                rowsLeft -= nRows;
            }

                     /*
                      * write PLTE chunk
                      */
            crc.reset();
            bytePos = writeInt4(paletteIndex * 3, bytePos); // length
            bytePos = writeBytes(PLTE, bytePos);            // magic
            crc.update(PLTE);
                     // write palette data
                     byte[] pltedata = new byte[paletteIndex * 3];
                     for (Enumeration e = palette.elements(); e.hasMoreElements();) {
                            item = (PngEncoderHashitem) e.nextElement();
                            pltedata[item.index*3]=(byte) ((item.rgba>>16)&0xff);
                            pltedata[item.index*3+1]=(byte) ((item.rgba>> 8)&0xff);
                            pltedata[item.index*3+2]=(byte) ( item.rgba     &0xff);
                     }
                     bytePos = writeBytes(pltedata, bytePos);
                     crc.update(pltedata);
                     // add crc
            crcValue = crc.getValue();
            bytePos = writeInt4((int) crcValue, bytePos);
                     
                     /*
                      * create scanline with palette indices
                      */
                     rowsLeft = height;
                     startRow = 0;
            while (rowsLeft > 0) {
                nRows = Math.min(64000 / (width * 4), rowsLeft);
                nRows = Math.max(nRows, 1);

                int[] pixels = new int[width * nRows];

                pg = new PixelGrabber(image, 0, startRow,
                    width, nRows, pixels, 0, 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[width * nRows + nRows];
                            
                scanPos = 0;
                startPos = 1;
                for (int i = 0; i < width * nRows; i++) {
                    if (i % width == 0) {
                        scanLines[scanPos++] = (byte) 0; /* no filter */
                        startPos = scanPos;
                    }
                                   item = (PngEncoderHashitem) palette.get( pixels[i] );
                                   if (item == null)
                                          throw new IOException( "color not found" );
                                   scanLines[scanPos++] = (byte) item.index;
                }
                            
                /*
                 * 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;

            crc.reset();
            bytePos = writeInt4(nCompressed, bytePos);
            bytePos = writeBytes(IDAT, bytePos);
            crc.update(IDAT);
            bytePos = writeBytes(compressedLines, nCompressed, bytePos);
            crc.update(compressedLines, 0, nCompressed);

            crcValue = crc.getValue();
            bytePos = writeInt4((int) crcValue, bytePos);
            scrunch.finish();
            return true;
        }
        catch (IOException e) {
            System.err.println(e.toString());
            return false;
        }
    }

Here is the call graph for this function:

Here is the caller graph for this function:

int com.keypoint.PngEncoderIndexed.writeInt2 ( int  n,
int  offset 
) [inline, protected]

Write a two-byte integer into the pngBytes array at a given position.

Parameters:
nThe integer to be written into pngBytes.
offsetThe starting point to write to.
Returns:
The next place to be written to in the pngBytes array.

Definition at line 262 of file PngEncoderIndexed.java.

                                               {
        byte[] temp = {(byte) ((n >> 8) & 0xff), (byte) (n & 0xff)};
        return writeBytes(temp, offset);
    }

Here is the call graph for this function:

int com.keypoint.PngEncoderIndexed.writeInt4 ( int  n,
int  offset 
) [inline, protected]

Write a four-byte integer into the pngBytes array at a given position.

Parameters:
nThe integer to be written into pngBytes.
offsetThe starting point to write to.
Returns:
The next place to be written to in the pngBytes array.

Definition at line 274 of file PngEncoderIndexed.java.

                                               {
        byte[] temp = {(byte) ((n >> 24) & 0xff),
                       (byte) ((n >> 16) & 0xff),
                       (byte) ((n >> 8) & 0xff),
                       (byte) (n & 0xff)};
        return writeBytes(temp, offset);
    }

Here is the call graph for this function:

Here is the caller graph for this function:


Member Data Documentation

The byte position.

Definition at line 84 of file PngEncoderIndexed.java.

The compression level.

Definition at line 93 of file PngEncoderIndexed.java.

CRC32 com.keypoint.PngEncoderIndexed.crc = new CRC32() [protected]

CRC.

Definition at line 87 of file PngEncoderIndexed.java.

The CRC value.

Definition at line 90 of file PngEncoderIndexed.java.

final int com.keypoint.PngEncoderIndexed.DEFAULT_COMPRESSION = 5 [static, protected]

Definition at line 72 of file PngEncoderIndexed.java.

Definition at line 81 of file PngEncoderIndexed.java.

final byte com.keypoint.PngEncoderIndexed.IDAT[] = {73, 68, 65, 84} [static, protected]

IDAT tag.

Definition at line 67 of file PngEncoderIndexed.java.

final byte com.keypoint.PngEncoderIndexed.IEND[] = {73, 69, 78, 68} [static, protected]

IEND tag.

Definition at line 70 of file PngEncoderIndexed.java.

final byte com.keypoint.PngEncoderIndexed.IHDR[] = {73, 72, 68, 82} [static, protected]

IHDR tag.

Definition at line 58 of file PngEncoderIndexed.java.

The image.

Definition at line 78 of file PngEncoderIndexed.java.

Definition at line 84 of file PngEncoderIndexed.java.

final byte com.keypoint.PngEncoderIndexed.PLTE[] = {80, 76, 84, 69} [static, protected]

PLTE tag.

Definition at line 61 of file PngEncoderIndexed.java.

The png bytes.

Definition at line 75 of file PngEncoderIndexed.java.

final byte com.keypoint.PngEncoderIndexed.tRNS[] = {116, 82, 78, 83} [static, protected]

tRNS tag.

Definition at line 64 of file PngEncoderIndexed.java.

The width.

Definition at line 81 of file PngEncoderIndexed.java.


The documentation for this class was generated from the following file: