some memory enhancements when generating and displaying ymage objects

git-svn-id: https://svn.berlios.de/svnroot/repos/yacy/trunk@4253 6c8d7289-2bf4-0310-a012-ef5d649a1542
pull/1/head
orbiter 18 years ago
parent f243e338cf
commit e22014dc83

@ -192,7 +192,8 @@
<!--[if IE]> <!--[if IE]>
</div> </div>
<![endif]--> <![endif]-->
<img src="http://localhost:8080/Banner.png?textcolor=ffffff&bgcolor=ddddee&bordercolor=ccccff">
#%env/templates/footer.template%# #%env/templates/footer.template%#
</body> </body>
</html> </html>

@ -157,7 +157,7 @@ public final class httpChunkedInputStream extends InputStream {
if (bout.length() <= 2) break; if (bout.length() <= 2) break;
} while(true); } while(true);
ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray()); ByteArrayInputStream bin = new ByteArrayInputStream(bout.getBytes());
reader = new BufferedReader(new InputStreamReader(bin)); reader = new BufferedReader(new InputStreamReader(bin));
this.httpTrailer = httpHeader.readHttpHeader(reader); this.httpTrailer = httpHeader.readHttpHeader(reader);
} finally { } finally {

@ -875,7 +875,7 @@ public final class httpc {
} }
// create body array // create body array
out.close(); out.close();
byte[] body = buf.toByteArray(); byte[] body = buf.getBytes();
buf = null; out = null; buf = null; out = null;
//System.out.println("DEBUG: PUT BODY=" + new String(body)); //System.out.println("DEBUG: PUT BODY=" + new String(body));

@ -872,7 +872,7 @@ public final class httpd implements serverHandler {
} else { } else {
serverByteBuffer bout = new serverByteBuffer(); serverByteBuffer bout = new serverByteBuffer();
serverFileUtils.copy(in,bout); serverFileUtils.copy(in,bout);
buffer = bout.toByteArray(); buffer = bout.getBytes();
bout.close(); bout = null; bout.close(); bout = null;
} }

@ -99,8 +99,6 @@ import java.util.logging.Level;
import java.util.zip.GZIPInputStream; import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream; import java.util.zip.GZIPOutputStream;
import javax.imageio.ImageIO;
import de.anomic.plasma.plasmaParser; import de.anomic.plasma.plasmaParser;
import de.anomic.plasma.plasmaSwitchboard; import de.anomic.plasma.plasmaSwitchboard;
import de.anomic.server.serverByteBuffer; import de.anomic.server.serverByteBuffer;
@ -501,23 +499,12 @@ public final class httpdFileHandler {
targetDate = new Date(System.currentTimeMillis()); targetDate = new Date(System.currentTimeMillis());
nocache = true; nocache = true;
String mimeType = mimeTable.getProperty(targetExt, "text/html"); String mimeType = mimeTable.getProperty(targetExt, "text/html");
serverByteBuffer result = ymageMatrix.exportImage(yp.getImage(), targetExt);
// generate an byte array from the generated image
serverByteBuffer baos = new serverByteBuffer();
// ymagePNGEncoderJDE jde = new
// ymagePNGEncoderJDE((ymageMatrixPainter) yp,
// ymagePNGEncoderJDE.FILTER_NONE, 0);
// byte[] result = jde.pngEncode();
ImageIO.write(yp.getImage(), targetExt, baos);
byte[] result = baos.toByteArray();
baos.close();
baos = null;
// write the array to the client // write the array to the client
httpd.sendRespondHeader(conProp, out, httpVersion, 200, null, mimeType, result.length, targetDate, null, null, null, null, nocache); httpd.sendRespondHeader(conProp, out, httpVersion, 200, null, mimeType, result.length(), targetDate, null, null, null, null, nocache);
if (!method.equals(httpHeader.METHOD_HEAD)) { if (!method.equals(httpHeader.METHOD_HEAD)) {
//Thread.sleep(200); // see below result.writeTo(out);
serverFileUtils.write(result, out);
} }
} }
if (img instanceof Image) { if (img instanceof Image) {
@ -532,18 +519,12 @@ public final class httpdFileHandler {
int height = i.getHeight(null); if (height < 0) height = 96; // bad hack int height = i.getHeight(null); if (height < 0) height = 96; // bad hack
BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
bi.createGraphics().drawImage(i, 0, 0, width, height, null); bi.createGraphics().drawImage(i, 0, 0, width, height, null);
serverByteBuffer baos = new serverByteBuffer(); serverByteBuffer result = ymageMatrix.exportImage(bi, targetExt);
ImageIO.write(bi, targetExt, baos);
byte[] result = baos.toByteArray();
baos.close();
baos = null;
// write the array to the client // write the array to the client
httpd.sendRespondHeader(conProp, out, httpVersion, 200, null, mimeType, result.length, targetDate, null, null, null, null, nocache); httpd.sendRespondHeader(conProp, out, httpVersion, 200, null, mimeType, result.length(), targetDate, null, null, null, null, nocache);
if (!method.equals(httpHeader.METHOD_HEAD)) { if (!method.equals(httpHeader.METHOD_HEAD)) {
//Thread.sleep(200); // see below result.writeTo(out);
serverFileUtils.write(result, out);
} }
} }
} }
@ -740,7 +721,7 @@ public final class httpdFileHandler {
targetDate, null, tp.getOutgoingHeader(), targetDate, null, tp.getOutgoingHeader(),
contentEncoding, null, nocache); contentEncoding, null, nocache);
} else { } else {
byte[] result = o.toByteArray(); // this interrupts streaming (bad idea!) byte[] result = o.getBytes(); // this interrupts streaming (bad idea!)
httpd.sendRespondHeader(conProp, out, httpd.sendRespondHeader(conProp, out,
httpVersion, 200, null, mimeType, result.length, httpVersion, 200, null, mimeType, result.length,
targetDate, null, tp.getOutgoingHeader(), targetDate, null, tp.getOutgoingHeader(),

@ -211,7 +211,7 @@ public class tarParser extends AbstractParser implements Parser {
"", // TODO: AUTHOR "", // TODO: AUTHOR
(String[])docSections.toArray(new String[docSections.size()]), (String[])docSections.toArray(new String[docSections.size()]),
docAbstrct.toString(), docAbstrct.toString(),
((serverByteBuffer)docText).toByteArray(), ((serverByteBuffer)docText).getBytes(),
docAnchors, docAnchors,
docImages); docImages);
} else { } else {

@ -195,7 +195,7 @@ public class zipParser extends AbstractParser implements Parser {
"", // TODO: AUTHOR "", // TODO: AUTHOR
(String[])docSections.toArray(new String[docSections.size()]), (String[])docSections.toArray(new String[docSections.size()]),
docAbstrct.toString(), docAbstrct.toString(),
((serverByteBuffer)docText).toByteArray(), ((serverByteBuffer)docText).getBytes(),
docAnchors, docAnchors,
docImages); docImages);
} else { } else {

@ -35,12 +35,7 @@ import de.anomic.ymage.ymageMatrix;
public class plasmaProfiling { public class plasmaProfiling {
public static long lastPPMUpdate; public static long lastPPMUpdate = System.currentTimeMillis()- 30000;
static {
// initialize memory profiling
lastPPMUpdate = System.currentTimeMillis()- 30000;
}
public static void updateIndexedPage(plasmaSwitchboardQueue.Entry entry) { public static void updateIndexedPage(plasmaSwitchboardQueue.Entry entry) {
if (System.currentTimeMillis() - lastPPMUpdate > 30000) { if (System.currentTimeMillis() - lastPPMUpdate > 30000) {
@ -80,10 +75,10 @@ public class plasmaProfiling {
int vspace = height - topborder - bottomborder; int vspace = height - topborder - bottomborder;
int hspace = width - leftborder - rightborder; int hspace = width - leftborder - rightborder;
int maxtime = 600; int maxtime = 600;
ymageChart ip = new ymageChart(width, height, "FFFFFF", "000000", leftborder, rightborder, topborder, bottomborder, "PEER PERFORMANCE GRAPH: PAGES/MINUTE and USED MEMORY"); ymageChart chart = new ymageChart(width, height, "FFFFFF", "000000", leftborder, rightborder, topborder, bottomborder, "PEER PERFORMANCE GRAPH: PAGES/MINUTE and USED MEMORY");
ip.declareDimension(ymageChart.DIMENSION_BOTTOM, bottomscale, hspace / (maxtime / bottomscale), -maxtime, "000000", "CCCCCC", "TIME/SECONDS"); chart.declareDimension(ymageChart.DIMENSION_BOTTOM, bottomscale, hspace / (maxtime / bottomscale), -maxtime, "000000", "CCCCCC", "TIME/SECONDS");
ip.declareDimension(ymageChart.DIMENSION_LEFT, leftscale, vspace * leftscale / maxppm, 0, "008800", null , "PPM [PAGES/MINUTE]"); chart.declareDimension(ymageChart.DIMENSION_LEFT, leftscale, vspace * leftscale / maxppm, 0, "008800", null , "PPM [PAGES/MINUTE]");
ip.declareDimension(ymageChart.DIMENSION_RIGHT, rightscale, vspace * rightscale / (int)(maxbytes / 1024 / 1024), 0, "0000FF", "CCCCCC", "MEMORY/MEGABYTE"); chart.declareDimension(ymageChart.DIMENSION_RIGHT, rightscale, vspace * rightscale / (int)(maxbytes / 1024 / 1024), 0, "0000FF", "CCCCCC", "MEMORY/MEGABYTE");
// draw ppm // draw ppm
Iterator i = serverProfiling.history("ppm"); Iterator i = serverProfiling.history("ppm");
@ -96,10 +91,10 @@ public class plasmaProfiling {
ppm = (int) ((Long) event.payload).longValue(); ppm = (int) ((Long) event.payload).longValue();
x1 = (int) (time/1000); x1 = (int) (time/1000);
y1 = ppm; y1 = ppm;
ip.setColor("228822"); chart.setColor("228822");
ip.chartDot(ymageChart.DIMENSION_BOTTOM, ymageChart.DIMENSION_LEFT, x1, y1, 2); chart.chartDot(ymageChart.DIMENSION_BOTTOM, ymageChart.DIMENSION_LEFT, x1, y1, 2);
ip.setColor("008800"); chart.setColor("008800");
if (x0 < 0) ip.chartLine(ymageChart.DIMENSION_BOTTOM, ymageChart.DIMENSION_LEFT, x0, y0, x1, y1); if (x0 < 0) chart.chartLine(ymageChart.DIMENSION_BOTTOM, ymageChart.DIMENSION_LEFT, x0, y0, x1, y1);
x0 = x1; y0 = y1; x0 = x1; y0 = y1;
} }
@ -112,14 +107,14 @@ public class plasmaProfiling {
bytes = ((Long) event.payload).longValue(); bytes = ((Long) event.payload).longValue();
x1 = (int) (time/1000); x1 = (int) (time/1000);
y1 = (int) (bytes / 1024 / 1024); y1 = (int) (bytes / 1024 / 1024);
ip.setColor("AAAAFF"); chart.setColor("AAAAFF");
ip.chartDot(ymageChart.DIMENSION_BOTTOM, ymageChart.DIMENSION_RIGHT, x1, y1, 2); chart.chartDot(ymageChart.DIMENSION_BOTTOM, ymageChart.DIMENSION_RIGHT, x1, y1, 2);
ip.setColor("0000FF"); chart.setColor("0000FF");
if (x0 < 0) ip.chartLine(ymageChart.DIMENSION_BOTTOM, ymageChart.DIMENSION_RIGHT, x0, y0, x1, y1); if (x0 < 0) chart.chartLine(ymageChart.DIMENSION_BOTTOM, ymageChart.DIMENSION_RIGHT, x0, y0, x1, y1);
x0 = x1; y0 = y1; x0 = x1; y0 = y1;
} }
return ip; return chart;
} }
public static class searchEvent { public static class searchEvent {

@ -127,9 +127,9 @@ public final class serverByteBuffer extends OutputStream {
} }
public void clear() { public void clear() {
this.buffer = new byte[0]; // we keep the byte[] and just set the pointer to write positions to zero
length = 0; this.length = 0;
offset = 0; this.offset = 0;
} }
public int length() { public int length() {
@ -293,7 +293,7 @@ public final class serverByteBuffer extends OutputStream {
} }
public byte[] getBytes(int start) { public byte[] getBytes(int start) {
return getBytes(start, length); return getBytes(start, this.length);
} }
public byte[] getBytes(int start, int len) { public byte[] getBytes(int start, int len) {
@ -305,7 +305,7 @@ public final class serverByteBuffer extends OutputStream {
System.arraycopy(buffer, offset + start, tmp, 0, len); System.arraycopy(buffer, offset + start, tmp, 0, len);
return tmp; return tmp;
} }
public serverByteBuffer trim(int start) { public serverByteBuffer trim(int start) {
trim(start, this.length - start); trim(start, this.length - start);
return this; return this;
@ -507,10 +507,9 @@ public final class serverByteBuffer extends OutputStream {
this.buffer = v; this.buffer = v;
} }
public byte toByteArray()[] { public void writeTo(OutputStream dest) throws IOException {
byte[] newbuf = new byte[this.length]; dest.write(this.buffer, this.offset, this.length);
System.arraycopy(this.buffer, 0, newbuf, 0, this.length); dest.flush();
return newbuf; }
}
} }

@ -1190,7 +1190,7 @@ public final class serverCore extends serverAbstractThread implements serverThre
} }
if ((readLineBuffer.length()==0)&&(b == -1)) return null; if ((readLineBuffer.length()==0)&&(b == -1)) return null;
return readLineBuffer.toByteArray(); return readLineBuffer.getBytes();
} catch (ClosedByInterruptException e) { } catch (ClosedByInterruptException e) {
if (logerr) serverLog.logSevere("SERVER", "receive interrupted - timeout"); if (logerr) serverLog.logSevere("SERVER", "receive interrupted - timeout");
return null; return null;

@ -64,6 +64,8 @@ public class ymageChart extends ymageMatrix {
String[] colnames = new String[]{"FFFFFF","FFFFFF","FFFFFF","FFFFFF"}; String[] colnames = new String[]{"FFFFFF","FFFFFF","FFFFFF","FFFFFF"};
String[] colscale = new String[]{null,null,null,null}; String[] colscale = new String[]{null,null,null,null};
String[] tablenames = new String[]{"","","",""}; String[] tablenames = new String[]{"","","",""};
String name;
String backgroundColor, foregroundColor;
public ymageChart(int width, int height, String backgroundColor, String foregroundColor, public ymageChart(int width, int height, String backgroundColor, String foregroundColor,
int leftborder, int rightborder, int topborder, int bottomborder, int leftborder, int rightborder, int topborder, int bottomborder,
@ -73,6 +75,9 @@ public class ymageChart extends ymageMatrix {
this.rightborder = rightborder; this.rightborder = rightborder;
this.topborder = topborder; this.topborder = topborder;
this.bottomborder = bottomborder; this.bottomborder = bottomborder;
this.name = name;
this.backgroundColor = backgroundColor;
this.foregroundColor = foregroundColor;
if (name != null) { if (name != null) {
this.setColor(foregroundColor); this.setColor(foregroundColor);
ymageToolPrint.print(this, width / 2 - name.length() * 3, 6, 0, name, -1); ymageToolPrint.print(this, width / 2 - name.length() * 3, 6, 0, name, -1);

@ -40,9 +40,11 @@ import java.awt.image.WritableRaster;
import java.io.File; import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import de.anomic.server.serverByteBuffer;
import de.anomic.server.serverMemory; import de.anomic.server.serverMemory;
public class ymageMatrix { public class ymageMatrix {
@ -56,14 +58,14 @@ public class ymageMatrix {
public static final byte MODE_REPLACE = 0; public static final byte MODE_REPLACE = 0;
public static final byte MODE_ADD = 1; public static final byte MODE_ADD = 1;
public static final byte MODE_SUB = 2; public static final byte MODE_SUB = 2;
protected int width, height; protected int width, height;
private BufferedImage image; private BufferedImage image;
private WritableRaster grid; private WritableRaster grid;
private int[] defaultCol; private int[] defaultCol;
private long backgroundCol;
private byte defaultMode; private byte defaultMode;
public ymageMatrix(int width, int height, byte drawMode, String backgroundColor) { public ymageMatrix(int width, int height, byte drawMode, String backgroundColor) {
this(width, height, drawMode, Long.parseLong(backgroundColor, 16)); this(width, height, drawMode, Long.parseLong(backgroundColor, 16));
} }
@ -72,21 +74,27 @@ public class ymageMatrix {
if (!(serverMemory.request(1024 * 1024 + 3 * width * height, false))) throw new RuntimeException("ymage: not enough memory (" + serverMemory.available() + ") available"); if (!(serverMemory.request(1024 * 1024 + 3 * width * height, false))) throw new RuntimeException("ymage: not enough memory (" + serverMemory.available() + ") available");
this.width = width; this.width = width;
this.height = height; this.height = height;
this.backgroundCol = backgroundColor;
this.defaultCol = new int[]{0xFF, 0xFF, 0xFF}; this.defaultCol = new int[]{0xFF, 0xFF, 0xFF};
this.defaultMode = drawMode; this.defaultMode = drawMode;
image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); this.image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
grid = image.getRaster(); //this.image = imageFromPool(width, height, 1000);
// fill grid with background color this.clear();
this.grid = image.getRaster();
}
public void clear() {
// fill grid with background color
int bgR, bgG, bgB; int bgR, bgG, bgB;
/*if (drawMode == MODE_SUB) { /*if (drawMode == MODE_SUB) {
bgR = (int) (0xFF - (backgroundColor >> 16)); bgR = (int) (0xFF - (this.backgroundCol >> 16));
bgG = (int) (0xFF - ((backgroundColor >> 8) & 0xff)); bgG = (int) (0xFF - ((this.backgroundCol >> 8) & 0xff));
bgB = (int) (0xFF - (backgroundColor & 0xff)); bgB = (int) (0xFF - (this.backgroundCol & 0xff));
} else {*/ } else {*/
bgR = (int) (backgroundColor >> 16); bgR = (int) (this.backgroundCol >> 16);
bgG = (int) ((backgroundColor >> 8) & 0xff); bgG = (int) ((this.backgroundCol >> 8) & 0xff);
bgB = (int) (backgroundColor & 0xff); bgB = (int) (this.backgroundCol & 0xff);
//} //}
Graphics2D gr = image.createGraphics(); Graphics2D gr = image.createGraphics();
gr.setBackground(new Color(bgR, bgG, bgB)); gr.setBackground(new Color(bgR, bgG, bgB));
@ -328,6 +336,94 @@ public class ymageMatrix {
m.line(0, 130, 100, 130); ymageToolPrint.print(m, 0, 125, 0, "Blue", -1); m.line(0, 130, 100, 130); ymageToolPrint.print(m, 0, 125, 0, "Blue", -1);
m.line(80, 0, 80, 300); m.line(80, 0, 80, 300);
} }
/*
private static class imageBuffer {
protected BufferedImage image;
protected long access;
public imageBuffer(BufferedImage image) {
this.image = image;
this.access = System.currentTimeMillis();
}
public boolean sameSize(int width, int height) {
return (this.image.getWidth() == width) && (this.image.getHeight() == height);
}
public boolean olderThan(long timeout) {
return System.currentTimeMillis() - this.access > timeout;
}
}
private static final ArrayList imagePool = new ArrayList();
private static BufferedImage imageFromPool(int width, int height, long timeout) {
// returns an Image object from the image pool
// if the pooled Image was created recently (before timeout), it is not used
synchronized (imagePool) {
imageBuffer buffer;
for (int i = 0; i < imagePool.size(); i++) {
buffer = (imageBuffer) imagePool.get(i);
if ((buffer.sameSize(width, height)) && (buffer.olderThan(timeout))) {
// use this buffer
System.out.println("### using imageBuffer from pool " + i);
buffer.access = System.currentTimeMillis();
return buffer.image;
}
}
// no buffered image found, create a new one
buffer = new imageBuffer(new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB));
imagePool.add(buffer);
return buffer.image;
}
}
*/
private static class sbbBuffer {
protected serverByteBuffer buffer;
protected int pixel;
protected long access;
public sbbBuffer(int width, int height) {
this.buffer = new serverByteBuffer();
this.access = System.currentTimeMillis();
this.pixel = width * height;
}
public boolean enoughSize(int width, int height) {
return this.pixel >= width * height;
}
public boolean olderThan(long timeout) {
return System.currentTimeMillis() - this.access > timeout;
}
}
private static final ArrayList sbbPool = new ArrayList();
private static serverByteBuffer sbbFromPool(int width, int height, long timeout) {
// returns an Image object from the image pool
// if the pooled Image was created recently (before timeout), it is not used
synchronized (sbbPool) {
sbbBuffer b;
for (int i = 0; i < sbbPool.size(); i++) {
b = (sbbBuffer) sbbPool.get(i);
if ((b.enoughSize(width, height)) && (b.olderThan(timeout))) {
// use this buffer
b.access = System.currentTimeMillis();
b.buffer.clear(); // this makes only sense if the byteBuffer keeps its buffer
return b.buffer;
}
}
// no buffered image found, create a new one
b = new sbbBuffer(width, height);
sbbPool.add(b);
return b.buffer;
}
}
public static serverByteBuffer exportImage(BufferedImage image, String targetExt) {
// generate an byte array from the given image
//serverByteBuffer baos = new serverByteBuffer();
serverByteBuffer baos = sbbFromPool(image.getWidth(), image.getHeight(), 1000);
try {
ImageIO.write(image, targetExt, baos);
return baos;
} catch (IOException e) {
// should not happen
e.printStackTrace();
return null;
}
}
public static void main(String[] args) { public static void main(String[] args) {
// go into headless awt mode // go into headless awt mode

@ -1,544 +0,0 @@
/**
* 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
*
* $LastChangedDate$
* $LastChangedRevision$
* $LastChangedBy$
*/
/*
* this file was taken from
* http://chem.sis.nlm.nih.gov/chemidplus/applet/chemaxon/marvin/help/PngEncoder.java.txt
* and modified by Michael Peter Christen, Frankfurt, 2005
* to work with ImagePainter Objects instead of awt.image Objetcs
* this avoids using any awt routines
*/
package de.anomic.ymage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.zip.CRC32;
import java.util.zip.Deflater;
import java.util.zip.DeflaterOutputStream;
public class ymagePNGEncoderJDE extends Object
{
/** 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 ymageMatrix matrix;
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 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 ymagePNGEncoderJDE(ymageMatrix matrix, int whichFilter, int compLevel)
{
this.matrix = matrix;
this.encodeAlpha = false;
setFilter( whichFilter );
if (compLevel >=0 && compLevel <=9)
{
this.compressionLevel = compLevel;
}
}
/**
* 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()
{
byte[] pngIdBytes = { -119, 80, 78, 71, 13, 10, 26, 10 };
if (matrix == null)
{
return null;
}
width = matrix.getWidth();
height = matrix.getHeight();
/*
* 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;
}
/**
* 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 = matrix.getWidth();
height = matrix.getHeight();
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;
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 )
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;
/*
* 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;
int[] pixel;
//matrix.getColorMode(true);
for (int i=0; i<width*nRows; i++)
{
if (i % width == 0)
{
scanLines[scanPos++] = (byte) filter;
startPos = scanPos;
}
pixel = matrix.getColor(i % width, i / width);
scanLines[scanPos++] = (byte) pixel[0]; // red
scanLines[scanPos++] = (byte) pixel[1]; // green
scanLines[scanPos++] = (byte) pixel[2]; // blue
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;
} finally {
}
}
/**
* 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 );
}
}
Loading…
Cancel
Save