- 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
pull/1/head
Michael Peter Christen 13 years ago
parent c6a1b21399
commit e9c6f4ce2e

@ -45,6 +45,10 @@
* - inlined all methods that had been called only once * - 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 * - 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 (!) * - 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; 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 IHDR[] = {73, 72, 68, 82};
private static final byte IDAT[] = {73, 68, 65, 84}; private static final byte IDAT[] = {73, 68, 65, 84};
private static final byte IEND[] = {73, 69, 78, 68}; private static final byte IEND[] = {73, 69, 78, 68};
private byte[] pngBytes;
private Image image;
private int maxPos;
public PngEncoder(Image image) { public final static byte[] pngEncode(final Image image, final int compressionLevel) throws IOException {
this.image = image; if (image == null) throw new IOException("image == null");
} final int width = image.getWidth(null);
final int height = image.getHeight(null);
public byte[] pngEncode(int compressionLevel) { int rowsLeft = height; // number of rows remaining to write
if (image == null) return null; int startRow = 0; // starting row to process this time through
int width = image.getWidth(null); int nRows; // how many rows to grab at a time
int height = image.getHeight(null); byte[] scanLines; // the scan lines to be compressed
pngBytes = new byte[((width + 1) * height * 3) + 200]; int scanPos; // where we are in the scan lines
maxPos = 0; final Deflater scrunch = new Deflater(compressionLevel);
int bytePos = writeBytes(new byte[]{-119, 80, 78, 71, 13, 10, 26, 10}, 0); final ByteArrayOutputStream outBytes = new ByteArrayOutputStream(1024);
int startPos; final DeflaterOutputStream compBytes = new DeflaterOutputStream(outBytes, scrunch);
startPos = bytePos = writeInt4(13, bytePos); while (rowsLeft > 0) {
bytePos = writeBytes(IHDR, bytePos); nRows = Math.min(32767 / (width * 4), rowsLeft);
width = image.getWidth(null); nRows = Math.max(nRows, 1);
height = image.getHeight(null); int[] pixels = new int[width * nRows];
bytePos = writeInt4(width, bytePos); PixelGrabber pg = new PixelGrabber(image, 0, startRow, width, nRows, pixels, 0, width);
bytePos = writeInt4(height, bytePos); try {pg.grabPixels();} catch (InterruptedException e) {throw new IOException("interrupted waiting for pixels!");}
bytePos = writeBytes(new byte[]{8, 2, 0, 0, 0}, bytePos); if ((pg.getStatus() & ImageObserver.ABORT) != 0) throw new IOException("image fetch aborted or errored");
CRC32 crc = new CRC32(); scanLines = new byte[width * nRows * 3 + nRows];
crc.reset(); scanPos = 0;
crc.update(pngBytes, startPos, bytePos - startPos); for (int i = 0; i < width * nRows; i++) {
bytePos = writeInt4((int) crc.getValue(), bytePos); if (i % width == 0) scanLines[scanPos++] = (byte) 0;
try { scanLines[scanPos++] = (byte) ((pixels[i] >> 16) & 0xff);
int rowsLeft = height; // number of rows remaining to write scanLines[scanPos++] = (byte) ((pixels[i] >> 8) & 0xff);
int startRow = 0; // starting row to process this time through scanLines[scanPos++] = (byte) ((pixels[i]) & 0xff);
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;
} }
compBytes.close(); compBytes.write(scanLines, 0, scanPos);
compressedLines = outBytes.toByteArray(); startRow += nRows;
nCompressed = compressedLines.length; rowsLeft -= nRows;
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.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) { private final static int writeInt4(final byte[] target, final int n, final int offset) {
byte[] newArray = new byte[newLength]; return writeBytes(target, new byte[]{(byte) ((n >> 24) & 0xff), (byte) ((n >> 16) & 0xff), (byte) ((n >> 8) & 0xff), (byte) (n & 0xff)}, offset);
System.arraycopy(array, 0, newArray, 0, Math.min(array.length, newLength));
return newArray;
} }
private int writeBytes(byte[] data, int offset) { private final static int writeBytes(final byte[] target, final byte[] data, final int offset) {
maxPos = Math.max(maxPos, offset + data.length); System.arraycopy(data, 0, target, 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; 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);
}
} }

@ -718,10 +718,9 @@ public class RasterPlotter {
} }
public static ByteBuffer exportPng(final BufferedImage image) { public static ByteBuffer exportPng(final BufferedImage image) {
PngEncoder png = new PngEncoder(image);
try { try {
final ByteBuffer baos = new ByteBuffer(); final ByteBuffer baos = new ByteBuffer();
byte[] pngbytes = png.pngEncode(1); byte[] pngbytes = PngEncoder.pngEncode(image, 1);
if (pngbytes == null) return null; if (pngbytes == null) return null;
baos.write(pngbytes); baos.write(pngbytes);
baos.flush(); baos.flush();

Loading…
Cancel
Save