| Constructor: |
public PngEncoder() {
this(null, false, FILTER_NONE, 0);
}
|
public PngEncoder(Image image) {
this(image, false, FILTER_NONE, 0);
}
Class constructor specifying Image to encode, with no alpha channel
encoding. Parameters:
image - A Java Image object which uses the DirectColorModel
Also see:
- java.awt.Image
|
public PngEncoder(Image image,
boolean encodeAlpha) {
this(image, encodeAlpha, FILTER_NONE, 0);
}
Class constructor specifying Image to encode, and whether to encode
alpha. Parameters:
image - A Java Image object which uses the DirectColorModel
encodeAlpha - Encode the alpha channel? false=no; true=yes
Also see:
- java.awt.Image
|
public PngEncoder(Image image,
boolean encodeAlpha,
int whichFilter) {
this(image, encodeAlpha, whichFilter, 0);
}
Class constructor specifying Image to encode, whether to encode alpha,
and filter to use. Parameters:
image - A Java Image object which uses the DirectColorModel
encodeAlpha - Encode the alpha channel? false=no; true=yes
whichFilter - 0=none, 1=sub, 2=up
Also 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;
}
}
Class constructor specifying Image source to encode, whether to encode
alpha, filter to use, and compression level. Parameters:
image - A Java Image object
encodeAlpha - Encode the alpha channel? false=no; true=yes
whichFilter - 0=none, 1=sub, 2=up
compLevel - 0..9 (1 = best speed, 9 = best compression, 0 = no
compression)
Also see:
- java.awt.Image
|
| Method from com.keypoint.PngEncoder Detail: |
protected void filterSub(byte[] pixels,
int startPos,
int width) {
int offset = this.bytesPerPixel;
int actualStart = startPos + offset;
int nBytes = width * this.bytesPerPixel;
int leftInsert = offset;
int leftExtract = 0;
for (int 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 "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. |
protected void filterUp(byte[] pixels,
int startPos,
int width) {
final int nBytes = width * this.bytesPerPixel;
for (int i = 0; i < nBytes; i++) {
final byte currentByte = pixels[startPos + i];
pixels[startPos + i] = (byte) ((pixels[startPos + i]
- this.priorRow[i]) % 256);
this.priorRow[i] = currentByte;
}
}
Perform "up" filtering on the given row.
Side effect: refills the prior row with current row |
public int getCompressionLevel() {
return this.compressionLevel;
}
Retrieve compression level. |
public boolean getEncodeAlpha() {
return this.encodeAlpha;
}
Retrieve alpha encoding status. |
public int getFilter() {
return this.filter;
}
Retrieve filtering scheme. |
public Image getImage() {
return image;
}
Returns the image to be encoded. |
public int getXDpi() {
return Math.round(xDpi * INCH_IN_METER_UNIT);
}
Get the DPI for the X axis. |
public int getYDpi() {
return Math.round(yDpi * INCH_IN_METER_UNIT);
}
Get the DPI for the Y axis. |
public byte[] pngEncode() {
return pngEncode(this.encodeAlpha);
}
Creates an array of bytes that is the PNG equivalent of the current
image. Alpha encoding is determined by its setting in the constructor. |
public byte[] pngEncode(boolean encodeAlpha) {
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 = writeBytes(pngIdBytes, 0);
//hdrPos = bytePos;
writeHeader();
writeResolution();
//dataPos = bytePos;
if (writeImageData()) {
writeEnd();
this.pngBytes = resizeByteArray(this.pngBytes, this.maxPos);
}
else {
this.pngBytes = null;
}
return this.pngBytes;
}
Creates an array of bytes that is the PNG equivalent of the current
image, specifying whether to encode alpha or not. |
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;
}
Increase or decrease the length of a byte array. |
public void setCompressionLevel(int level) {
if (level >= 0 && level < = 9) {
this.compressionLevel = level;
}
}
Set the compression level to use. |
public void setDpi(int xDpi,
int yDpi) {
this.xDpi = Math.round(xDpi / INCH_IN_METER_UNIT);
this.yDpi = Math.round(yDpi / INCH_IN_METER_UNIT);
}
|
public void setEncodeAlpha(boolean encodeAlpha) {
this.encodeAlpha = encodeAlpha;
}
Set the alpha encoding on or off. |
public void setFilter(int whichFilter) {
this.filter = FILTER_NONE;
if (whichFilter < = FILTER_LAST) {
this.filter = whichFilter;
}
}
|
public void setImage(Image image) {
this.image = image;
this.pngBytes = null;
}
Set the image to be encoded. |
public void setXDpi(int xDpi) {
this.xDpi = Math.round(xDpi / INCH_IN_METER_UNIT);
}
Set the DPI for the X axis. |
public void setYDpi(int yDpi) {
this.yDpi = Math.round(yDpi / INCH_IN_METER_UNIT);
}
Set the DPI for the Y axis. |
protected int writeByte(int b,
int offset) {
byte[] temp = {(byte) b};
return writeBytes(temp, offset);
}
Write a single byte into the pngBytes array at a given position. |
protected int writeBytes(byte[] data,
int offset) {
this.maxPos = Math.max(this.maxPos, offset + data.length);
if (data.length + offset > this.pngBytes.length) {
this.pngBytes = resizeByteArray(this.pngBytes, this.pngBytes.length
+ Math.max(1000, data.length));
}
System.arraycopy(data, 0, this.pngBytes, offset, data.length);
return offset + data.length;
}
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. |
protected int writeBytes(byte[] data,
int nBytes,
int offset) {
this.maxPos = Math.max(this.maxPos, offset + nBytes);
if (nBytes + offset > this.pngBytes.length) {
this.pngBytes = resizeByteArray(this.pngBytes, this.pngBytes.length
+ Math.max(1000, nBytes));
}
System.arraycopy(data, 0, this.pngBytes, offset, nBytes);
return offset + nBytes;
}
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. |
protected void writeEnd() {
this.bytePos = writeInt4(0, this.bytePos);
this.bytePos = writeBytes(IEND, this.bytePos);
this.crc.reset();
this.crc.update(IEND);
this.crcValue = this.crc.getValue();
this.bytePos = writeInt4((int) this.crcValue, this.bytePos);
}
Write a PNG "IEND" chunk into the pngBytes array. |
protected void writeHeader() {
int startPos = this.bytePos = writeInt4(13, this.bytePos);
this.bytePos = writeBytes(IHDR, this.bytePos);
this.width = this.image.getWidth(null);
this.height = this.image.getHeight(null);
this.bytePos = writeInt4(this.width, this.bytePos);
this.bytePos = writeInt4(this.height, this.bytePos);
this.bytePos = writeByte(8, this.bytePos); // bit depth
this.bytePos = writeByte((this.encodeAlpha) ? 6 : 2, this.bytePos);
// direct model
this.bytePos = writeByte(0, this.bytePos); // compression method
this.bytePos = writeByte(0, this.bytePos); // filter method
this.bytePos = writeByte(0, this.bytePos); // no interlace
this.crc.reset();
this.crc.update(this.pngBytes, startPos, this.bytePos - startPos);
this.crcValue = this.crc.getValue();
this.bytePos = writeInt4((int) this.crcValue, this.bytePos);
}
Write a PNG "IHDR" chunk into the pngBytes array. |
protected boolean writeImageData() {
int rowsLeft = this.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;
this.bytesPerPixel = (this.encodeAlpha) ? 4 : 3;
Deflater scrunch = new Deflater(this.compressionLevel);
ByteArrayOutputStream outBytes = new ByteArrayOutputStream(1024);
DeflaterOutputStream compBytes = new DeflaterOutputStream(outBytes,
scrunch);
try {
while (rowsLeft > 0) {
nRows = Math.min(32767 / (this.width
* (this.bytesPerPixel + 1)), rowsLeft);
nRows = Math.max(nRows, 1);
int[] pixels = new int[this.width * nRows];
pg = new PixelGrabber(this.image, 0, startRow,
this.width, nRows, pixels, 0, this.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[this.width * nRows * this.bytesPerPixel
+ nRows];
if (this.filter == FILTER_SUB) {
this.leftBytes = new byte[16];
}
if (this.filter == FILTER_UP) {
this.priorRow = new byte[this.width * this.bytesPerPixel];
}
scanPos = 0;
startPos = 1;
for (int i = 0; i < this.width * nRows; i++) {
if (i % this.width == 0) {
scanLines[scanPos++] = (byte) this.filter;
startPos = scanPos;
}
scanLines[scanPos++] = (byte) ((pixels[i] > > 16) & 0xff);
scanLines[scanPos++] = (byte) ((pixels[i] > > 8) & 0xff);
scanLines[scanPos++] = (byte) ((pixels[i]) & 0xff);
if (this.encodeAlpha) {
scanLines[scanPos++] = (byte) ((pixels[i] > > 24)
& 0xff);
}
if ((i % this.width == this.width - 1)
&& (this.filter != FILTER_NONE)) {
if (this.filter == FILTER_SUB) {
filterSub(scanLines, startPos, this.width);
}
if (this.filter == FILTER_UP) {
filterUp(scanLines, startPos, this.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;
this.crc.reset();
this.bytePos = writeInt4(nCompressed, this.bytePos);
this.bytePos = writeBytes(IDAT, this.bytePos);
this.crc.update(IDAT);
this.bytePos = writeBytes(compressedLines, nCompressed,
this.bytePos);
this.crc.update(compressedLines, 0, nCompressed);
this.crcValue = this.crc.getValue();
this.bytePos = writeInt4((int) this.crcValue, this.bytePos);
scrunch.finish();
return true;
}
catch (IOException e) {
System.err.println(e.toString());
return false;
}
}
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. |
protected int writeInt2(int n,
int offset) {
byte[] temp = {(byte) ((n > > 8) & 0xff), (byte) (n & 0xff)};
return writeBytes(temp, offset);
}
Write a two-byte integer into the pngBytes array at a given position. |
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 four-byte integer into the pngBytes array at a given position. |
protected void writeResolution() {
if (xDpi > 0 && yDpi > 0) {
final int startPos = bytePos = writeInt4(9, bytePos);
bytePos = writeBytes(PHYS, bytePos);
bytePos = writeInt4(xDpi, bytePos);
bytePos = writeInt4(yDpi, bytePos);
bytePos = writeByte(1, bytePos); // unit is the meter.
crc.reset();
crc.update(pngBytes, startPos, bytePos - startPos);
crcValue = crc.getValue();
bytePos = writeInt4((int) crcValue, bytePos);
}
}
Write a PNG "pHYs" chunk into the pngBytes array. |