@ -45,6 +45,10 @@
* - inlined all methods that had been called only once
* - moved class objects which appear after all refactoring only within a single method into this method
* - removed a giant number of useless ( obvious things ) comments and empty lines to increase readability ( ! )
* - new order of data computation : first compute the size of compressed deflater output ,
* then assign an exact - sized byte [ ] which makes resizing afterwards superfluous
* - after all enhancements all class objects were removed ; result is just one short static method
* - made objects final where possible
* /
package net.yacy.visualization ;
@ -63,107 +67,76 @@ public class PngEncoder extends Object {
private static final byte IHDR [ ] = { 73 , 72 , 68 , 82 } ;
private static final byte IDAT [ ] = { 73 , 68 , 65 , 84 } ;
private static final byte IEND [ ] = { 73 , 69 , 78 , 68 } ;
private byte [ ] pngBytes ;
private Image image ;
private int maxPos ;
public PngEncoder ( Image image ) {
this . image = image ;
}
public byte [ ] pngEncode ( int compressionLevel ) {
if ( image = = null ) return null ;
int width = image . getWidth ( null ) ;
int height = image . getHeight ( null ) ;
pngBytes = new byte [ ( ( width + 1 ) * height * 3 ) + 200 ] ;
maxPos = 0 ;
int bytePos = writeBytes ( new byte [ ] { - 119 , 80 , 78 , 71 , 13 , 10 , 26 , 10 } , 0 ) ;
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 = writeBytes ( new byte [ ] { 8 , 2 , 0 , 0 , 0 } , bytePos ) ;
CRC32 crc = new CRC32 ( ) ;
crc . reset ( ) ;
crc . update ( pngBytes , startPos , bytePos - startPos ) ;
bytePos = writeInt4 ( ( int ) crc . getValue ( ) , bytePos ) ;
try {
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
byte [ ] compressedLines ; // the resultant compressed lines
int nCompressed ; // how big is the compressed area?
PixelGrabber pg ;
Deflater scrunch = new Deflater ( compressionLevel ) ;
ByteArrayOutputStream outBytes = new ByteArrayOutputStream ( 1024 ) ;
DeflaterOutputStream compBytes = new DeflaterOutputStream ( outBytes , scrunch ) ;
while ( rowsLeft > 0 ) {
nRows = Math . min ( 32767 / ( 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 ( InterruptedException e ) { throw new IOException ( "interrupted waiting for pixels!" ) ; }
if ( ( pg . getStatus ( ) & ImageObserver . ABORT ) ! = 0 ) throw new IOException ( "image fetch aborted or errored" ) ;
scanLines = new byte [ width * nRows * 3 + nRows ] ;
scanPos = 0 ;
for ( int i = 0 ; i < width * nRows ; i + + ) {
if ( i % width = = 0 ) scanLines [ scanPos + + ] = ( byte ) 0 ;
scanLines [ scanPos + + ] = ( byte ) ( ( pixels [ i ] > > 16 ) & 0xff ) ;
scanLines [ scanPos + + ] = ( byte ) ( ( pixels [ i ] > > 8 ) & 0xff ) ;
scanLines [ scanPos + + ] = ( byte ) ( ( pixels [ i ] ) & 0xff ) ;
}
compBytes . write ( scanLines , 0 , scanPos ) ;
startRow + = nRows ;
rowsLeft - = nRows ;
public final static byte [ ] pngEncode ( final Image image , final int compressionLevel ) throws IOException {
if ( image = = null ) throw new IOException ( "image == null" ) ;
final int width = image . getWidth ( null ) ;
final int height = image . getHeight ( null ) ;
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
final Deflater scrunch = new Deflater ( compressionLevel ) ;
final ByteArrayOutputStream outBytes = new ByteArrayOutputStream ( 1024 ) ;
final DeflaterOutputStream compBytes = new DeflaterOutputStream ( outBytes , scrunch ) ;
while ( rowsLeft > 0 ) {
nRows = Math . min ( 32767 / ( width * 4 ) , rowsLeft ) ;
nRows = Math . max ( nRows , 1 ) ;
int [ ] pixels = new int [ width * nRows ] ;
PixelGrabber pg = new PixelGrabber ( image , 0 , startRow , width , nRows , pixels , 0 , width ) ;
try { pg . grabPixels ( ) ; } catch ( InterruptedException e ) { throw new IOException ( "interrupted waiting for pixels!" ) ; }
if ( ( pg . getStatus ( ) & ImageObserver . ABORT ) ! = 0 ) throw new IOException ( "image fetch aborted or errored" ) ;
scanLines = new byte [ width * nRows * 3 + nRows ] ;
scanPos = 0 ;
for ( int i = 0 ; i < width * nRows ; i + + ) {
if ( i % width = = 0 ) scanLines [ scanPos + + ] = ( byte ) 0 ;
scanLines [ scanPos + + ] = ( byte ) ( ( pixels [ i ] > > 16 ) & 0xff ) ;
scanLines [ scanPos + + ] = ( byte ) ( ( pixels [ i ] > > 8 ) & 0xff ) ;
scanLines [ scanPos + + ] = ( byte ) ( ( pixels [ i ] ) & 0xff ) ;
}
compBytes . close ( ) ;
compressedLines = outBytes . toByteArray ( ) ;
nCompressed = compressedLines . length ;
crc . reset ( ) ;
bytePos = writeInt4 ( nCompressed , bytePos ) ;
bytePos = writeBytes ( IDAT , bytePos ) ;
crc . update ( IDAT ) ;
maxPos = Math . max ( maxPos , bytePos + nCompressed ) ;
if ( nCompressed + bytePos > pngBytes . length ) {
pngBytes = resizeByteArray ( pngBytes , pngBytes . length + Math . max ( 1000 , nCompressed ) ) ;
}
System . arraycopy ( compressedLines , 0 , pngBytes , bytePos , nCompressed ) ;
bytePos + = nCompressed ;
crc . update ( compressedLines , 0 , nCompressed ) ;
bytePos = writeInt4 ( ( int ) crc . getValue ( ) , bytePos ) ;
scrunch . finish ( ) ;
bytePos = writeInt4 ( 0 , bytePos ) ;
bytePos = writeBytes ( IEND , bytePos ) ;
crc . reset ( ) ;
crc . update ( IEND ) ;
bytePos = writeInt4 ( ( int ) crc . getValue ( ) , bytePos ) ;
pngBytes = resizeByteArray ( pngBytes , maxPos ) ;
return pngBytes ;
} catch ( IOException e ) {
return null ;
compBytes . write ( scanLines , 0 , scanPos ) ;
startRow + = nRows ;
rowsLeft - = nRows ;
}
compBytes . close ( ) ;
final byte [ ] compressedLines = outBytes . toByteArray ( ) ;
final int nCompressed = compressedLines . length ;
final byte [ ] pngBytes = new byte [ nCompressed + 57 ] ; // yes thats the exact size, not too less, not too much. No resizing needed.
int bytePos = writeBytes ( pngBytes , new byte [ ] { - 119 , 80 , 78 , 71 , 13 , 10 , 26 , 10 } , 0 ) ;
final int startPos = bytePos = writeInt4 ( pngBytes , 13 , bytePos ) ;
bytePos = writeBytes ( pngBytes , IHDR , bytePos ) ;
bytePos = writeInt4 ( pngBytes , width , bytePos ) ;
bytePos = writeInt4 ( pngBytes , height , bytePos ) ;
bytePos = writeBytes ( pngBytes , new byte [ ] { 8 , 2 , 0 , 0 , 0 } , bytePos ) ;
final CRC32 crc = new CRC32 ( ) ;
crc . reset ( ) ;
crc . update ( pngBytes , startPos , bytePos - startPos ) ;
bytePos = writeInt4 ( pngBytes , ( int ) crc . getValue ( ) , bytePos ) ;
crc . reset ( ) ;
bytePos = writeInt4 ( pngBytes , nCompressed , bytePos ) ;
bytePos = writeBytes ( pngBytes , IDAT , bytePos ) ;
crc . update ( IDAT ) ;
System . arraycopy ( compressedLines , 0 , pngBytes , bytePos , nCompressed ) ;
bytePos + = nCompressed ;
crc . update ( compressedLines , 0 , nCompressed ) ;
bytePos = writeInt4 ( pngBytes , ( int ) crc . getValue ( ) , bytePos ) ;
scrunch . finish ( ) ;
bytePos = writeInt4 ( pngBytes , 0 , bytePos ) ;
bytePos = writeBytes ( pngBytes , IEND , bytePos ) ;
crc . reset ( ) ;
crc . update ( IEND ) ;
bytePos = writeInt4 ( pngBytes , ( int ) crc . getValue ( ) , bytePos ) ;
return pngBytes ;
}
private byte [ ] resizeByteArray ( byte [ ] array , int newLength ) {
byte [ ] newArray = new byte [ newLength ] ;
System . arraycopy ( array , 0 , newArray , 0 , Math . min ( array . length , newLength ) ) ;
return newArray ;
private final static int writeInt4 ( final byte [ ] target , final int n , final int offset ) {
return writeBytes ( target , new byte [ ] { ( byte ) ( ( n > > 24 ) & 0xff ) , ( byte ) ( ( n > > 16 ) & 0xff ) , ( byte ) ( ( n > > 8 ) & 0xff ) , ( byte ) ( n & 0xff ) } , offset ) ;
}
private 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 ) ;
private final static int writeBytes ( final byte [ ] target , final byte [ ] data , final int offset ) {
System . arraycopy ( data , 0 , target , offset , data . length ) ;
return offset + data . length ;
}
private int writeInt4 ( int n , int offset ) {
return writeBytes ( new byte [ ] { ( byte ) ( ( n > > 24 ) & 0xff ) , ( byte ) ( ( n > > 16 ) & 0xff ) , ( byte ) ( ( n > > 8 ) & 0xff ) , ( byte ) ( n & 0xff ) } , offset ) ;
}
}