首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 计算机考试 > 认证考试 > JAVA认证 >

将Javaimage对象转换成PNG格式字节数组

2008-10-05 
** * pngencoder takes a Java image object and creates a byte string which can be saved as a ...
**

* 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.*;

import java.util.*;

import java.util.zip.*;

import java.io.*;



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;





/**

* 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

* @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.compressionlevel = 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;

pngbytes = null;

}





/**

* 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 };

int i;



if (image == null)

{

return null;

}

width = image.getwidth( null );

height = image.getheight( null );

this.image = image;



/*

* 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;

}



/**

* 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( 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 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 filter;

}



/**

* set the compression level to use

*

* @param level 0 through 9

*/

public void setcompressionlevel( int level )

{

if ( level >= 0 && level <= 9)

{

this.compressionlevel = level;

}

}



/**

* retrieve compression level

*

* @return int in range 0-9

*/

public int getcompressionlevel()

{

return compressionlevel;

}



/**

* 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 )

{

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;

}





/**

* 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 )

{

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;

}



/**

* 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 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 writebyte( int b, int offset )

{

byte[] temp = { (byte) b };

return writebytes( temp, offset );

}



/**

* write a string into the pngbytes array at a given position.

* this uses the getbytes method, so the encoding used will

* be its default.

*

* @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.

* @see java.lang.string#getbytes()

*/

protected int writestring( string s, int offset )

{

return writebytes( s.getbytes(), offset );

}



/**

* write a png "ihdr" chunk into the pngbytes array.

*/

protected void writeheader()

{

int startpos;



startpos = bytepos = writeint4( 13, bytepos );

bytepos = writestring( "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( (encodealpha) ? 6 : 2, bytepos ); // direct model

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 );

}



/**

* 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;

int offset = bytesperpixel;

int actualstart = startpos offset;

int nbytes = width * bytesperpixel;

int leftinsert = offset;

int leftextract = 0;

byte current_byte;



for (i=actualstart; i < startpos nbytes; i )

{

leftbytes[leftinsert] = pixels[i];

pixels[i] = (byte) ((pixels[i] - 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 * bytesperpixel;



for (i=0; i < nbytes; i )

{

current_byte = pixels[startpos i];

pixels[startpos i] = (byte) ((pixels[startpos i] - priorrow[i]) % 256);

priorrow[i] = current_byte;

}

}



/**

* 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 = 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;



bytesperpixel = (encodealpha) ? 4 : 3;



deflater scrunch = new deflater( compressionlevel );

bytearrayoutputstream outbytes =

new bytearrayoutputstream(1024);



deflateroutputstream compbytes =

new deflateroutputstream( outbytes, scrunch );

try

{

while (rowsleft > 0)

{

nrows = math.min( 32767 / (width*(bytesperpixel 1)), rowsleft );

// nrows = rowsleft;



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 * bytesperpixel nrows];



if (filter == filter_sub)

{

leftbytes = new byte[16];

}

if (filter == filter_up)

{

priorrow = new byte[width*bytesperpixel];

}



scanpos = 0;

startpos = 1;

for (int i=0; i
{

if (i % width == 0)

{

scanlines[scanpos ] = (byte) filter;

startpos = scanpos;

}

scanlines[scanpos ] = (byte) ((pixels[i] >> 16) & 0xff);

scanlines[scanpos ] = (byte) ((pixels[i] >> 8) & 0xff);

scanlines[scanpos ] = (byte) ((pixels[i] ) & 0xff);

if (encodealpha)

{

scanlines[scanpos ] = (byte) ((pixels[i] >> 24) & 0xff );

}

if ((i % width == width-1) && (filter != filter_none))

{

if (filter == filter_sub)

{

filtersub( scanlines, startpos, width );

}

if (filter == filter_up)

{

filterup( scanlines, startpos, 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;



crc.reset();

bytepos = writeint4( ncompressed, bytepos );

bytepos = writestring("idat", bytepos );

crc.update("idat".getbytes());

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;

}

}



/**

* write a png "iend" chunk into the pngbytes array.

*/

protected void writeend()

{

bytepos = writeint4( 0, bytepos );

bytepos = writestring( "iend", bytepos );

crc.reset();

crc.update("iend".getbytes());

crcvalue = crc.getvalue();

bytepos = writeint4( (int) crcvalue, bytepos );

}

}