-/* $RCSfile$\r
- * $Author: nicove $\r
- * $Date: 2007-03-30 12:26:16 -0500 (Fri, 30 Mar 2007) $\r
- * $Revision: 7275 $\r
- *\r
- * Copyright (C) 2002-2005 The Jmol Development Team\r
- *\r
- * Contact: jmol-developers@lists.sf.net\r
- *\r
- * This library is free software; you can redistribute it and/or\r
- * modify it under the terms of the GNU Lesser General Public\r
- * License as published by the Free Software Foundation; either\r
- * version 2.1 of the License, or (at your option) any later version.\r
- *\r
- * This library is distributed in the hope that it will be useful,\r
- * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
- * Lesser General Public License for more details.\r
- *\r
- * You should have received a copy of the GNU Lesser General Public\r
- * License along with this library; if not, write to the Free Software\r
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\r
- */\r
-package javajs.img;\r
-\r
-import java.util.Map;\r
-import java.util.zip.Deflater;\r
-import java.util.zip.DeflaterOutputStream;\r
-import java.io.ByteArrayOutputStream;\r
-import java.io.IOException;\r
-\r
-\r
-\r
-/**\r
- * \r
- * Modified by Bob Hanson hansonr@stolaf.edu to be a subclass of ImageEncoder\r
- * and to use javajs.util.OutputChannel instead of just returning bytes. Also includes: \r
- * \r
- * -- JavaScript-compatible image processing\r
- * \r
- * -- transparent background option\r
- * \r
- * -- more efficient calculation of needs for pngBytes \r
- * \r
- * -- option to use pre-created PNGJ image data (3/19/14; Jmol 14.1.12)\r
- * \r
- * -- PNGJ format:\r
- * \r
- * // IHDR chunk \r
- * \r
- * // tEXt chunk "Jmol type - <PNG0|PNGJ><0000000pt>+<000000len>" \r
- * \r
- * // tEXt chunk "Software - Jmol <version>"\r
- * \r
- * // tEXt chunk "Creation Time - <date>"\r
- * \r
- * // tRNS chunk transparent color, if desired\r
- *\r
- * // IDAT chunk (image data)\r
- * \r
- * // IEND chunk \r
- * \r
- * // [JMOL ZIP FILE APPENDIX]\r
- * \r
- * Original Comment:\r
- * \r
- * PngEncoder takes a Java Image object and creates a byte string which can be\r
- * saved as a PNG file. The Image is presumed to use the DirectColorModel.\r
- * \r
- * Thanks to Jay Denny at KeyPoint Software http://www.keypoint.com/ who let me\r
- * develop this code on company time.\r
- * \r
- * You may contact me with (probably very-much-needed) improvements, comments,\r
- * and bug fixes at:\r
- * \r
- * david@catcode.com\r
- * \r
- * @author J. David Eisenberg\r
- * @author http://catcode.com/pngencoder/\r
- * @author Christian Ribeaud (christian.ribeaud@genedata.com)\r
- * @author Bob Hanson (hansonr@stolaf.edu)\r
- * \r
- * @version 1.4, 31 March 2000\r
- */\r
-public class PngEncoder extends CRCEncoder {\r
-\r
- /** Constants for filters */\r
- public static final int FILTER_NONE = 0;\r
- public static final int FILTER_SUB = 1;\r
- public static final int FILTER_UP = 2;\r
- public static final int FILTER_LAST = 2;\r
- \r
- private static final int PT_FIRST_TAG = 37;\r
-\r
- private boolean encodeAlpha;\r
- private int filter = FILTER_NONE;\r
- private int bytesPerPixel;\r
- private int compressionLevel;\r
- private String type;\r
- private Integer transparentColor;\r
-\r
- private byte[] appData;\r
- private String appPrefix;\r
- private String comment;\r
- private byte[] bytes;\r
-\r
- \r
- public PngEncoder() {\r
- super();\r
- }\r
-\r
- @Override\r
- protected void setParams(Map<String, Object> params) {\r
- if (quality < 0)\r
- quality = (params.containsKey("qualityPNG") ? ((Integer) params\r
- .get("qualityPNG")).intValue() : 2);\r
- if (quality > 9)\r
- quality = 9;\r
- encodeAlpha = false;\r
- filter = FILTER_NONE;\r
- compressionLevel = quality;\r
- transparentColor = (Integer) params.get("transparentColor");\r
- comment = (String) params.get("comment");\r
- type = (params.get("type") + "0000").substring(0, 4);\r
- bytes = (byte[]) params.get("pngImgData");\r
- appData = (byte[]) params.get("pngAppData");\r
- appPrefix = (String) params.get("pngAppPrefix");\r
- }\r
-\r
- \r
-\r
- @Override\r
- protected void generate() throws IOException {\r
- if (bytes == null) {\r
- if (!pngEncode()) {\r
- out.cancel();\r
- return;\r
- }\r
- bytes = getBytes();\r
- } else {\r
- dataLen = bytes.length;\r
- }\r
- int len = dataLen;\r
- if (appData != null) {\r
- setJmolTypeText(appPrefix, bytes, len, appData.length,\r
- type);\r
- out.write(bytes, 0, len);\r
- len = (bytes = appData).length;\r
- }\r
- out.write(bytes, 0, len);\r
- }\r
-\r
-\r
- /**\r
- * Creates an array of bytes that is the PNG equivalent of the current image,\r
- * specifying whether to encode alpha or not.\r
- * \r
- * @return true if successful\r
- * \r
- */\r
- private boolean pngEncode() {\r
-\r
- byte[] pngIdBytes = { -119, 80, 78, 71, 13, 10, 26, 10 };\r
-\r
- writeBytes(pngIdBytes);\r
- //hdrPos = bytePos;\r
- writeHeader();\r
- writeText(getApplicationText(appPrefix, type, 0, 0));\r
-\r
- writeText("Software\0Jmol " + comment);\r
- writeText("Creation Time\0" + date);\r
-\r
- if (!encodeAlpha && transparentColor != null)\r
- writeTransparentColor(transparentColor.intValue());\r
- //dataPos = bytePos;\r
- return writeImageData();\r
- }\r
-\r
- /**\r
- * Fill in the Jmol type text area with number of bytes of PNG data and number\r
- * of bytes of Jmol state data and fix checksum.\r
- * \r
- * If we do not do this, then the checksum will be wrong, and Jmol and some\r
- * other programs may not be able to read the PNG image.\r
- * \r
- * This was corrected for Jmol 12.3.30. Between 12.3.7 and 12.3.29, PNG files\r
- * created by Jmol have incorrect checksums.\r
- * \r
- * @param prefix \r
- * \r
- * @param b\r
- * @param nPNG\r
- * @param nState\r
- * @param type\r
- */\r
- private static void setJmolTypeText(String prefix, byte[] b, int nPNG, int nState, String type) {\r
- String s = "tEXt" + getApplicationText(prefix, type, nPNG, nState);\r
- CRCEncoder encoder = new PngEncoder();\r
- byte[] test = s.substring(0, 4 + prefix.length()).getBytes();\r
- for (int i = test.length; -- i >= 0;) \r
- if (b[i + PT_FIRST_TAG] != test[i]) {\r
- System.out.println("image is not of the right form; appending data, but not adding tEXt tag.");\r
- return;\r
- }\r
- encoder.setData(b, PT_FIRST_TAG);\r
- encoder.writeString(s);\r
- encoder.writeCRC();\r
- }\r
-\r
- private static String getApplicationText(String prefix, String type, int nPNG, int nState) {\r
- String sPNG = "000000000" + nPNG;\r
- sPNG = sPNG.substring(sPNG.length() - 9);\r
- String sState = "000000000" + nState;\r
- sState = sState.substring(sState.length() - 9);\r
- return prefix + "\0" + type + (type.equals("PNG") ? "0" : "") + sPNG + "+"\r
- + sState;\r
- }\r
-\r
- // /**\r
- // * Set the filter to use\r
- // *\r
- // * @param whichFilter from constant list\r
- // */\r
- // public void setFilter(int whichFilter) {\r
- // this.filter = (whichFilter <= FILTER_LAST ? whichFilter : FILTER_NONE);\r
- // }\r
-\r
- // /**\r
- // * Retrieve filtering scheme\r
- // *\r
- // * @return int (see constant list)\r
- // */\r
- // public int getFilter() {\r
- // return filter;\r
- // }\r
-\r
- // /**\r
- // * Set the compression level to use\r
- // *\r
- // * @param level 0 through 9\r
- // */\r
- // public void setCompressionLevel(int level) {\r
- // if ((level >= 0) && (level <= 9)) {\r
- // this.compressionLevel = level;\r
- // }\r
- // }\r
-\r
- // /**\r
- // * Retrieve compression level\r
- // *\r
- // * @return int in range 0-9\r
- // */\r
- // public int getCompressionLevel() {\r
- // return compressionLevel;\r
- // }\r
-\r
- /**\r
- * Write a PNG "IHDR" chunk into the pngBytes array.\r
- */\r
- private void writeHeader() {\r
-\r
- writeInt4(13);\r
- startPos = bytePos;\r
- writeString("IHDR");\r
- writeInt4(width);\r
- writeInt4(height);\r
- writeByte(8); // bit depth\r
- writeByte(encodeAlpha ? 6 : 2); // color type or direct model\r
- writeByte(0); // compression method\r
- writeByte(0); // filter method\r
- writeByte(0); // no interlace\r
- writeCRC();\r
- }\r
-\r
- private void writeText(String msg) {\r
- writeInt4(msg.length());\r
- startPos = bytePos;\r
- writeString("tEXt" + msg);\r
- writeCRC();\r
- }\r
-\r
- /**\r
- * Write a PNG "tRNS" chunk into the pngBytes array.\r
- * \r
- * @param icolor\r
- */\r
- private void writeTransparentColor(int icolor) {\r
-\r
- writeInt4(6);\r
- startPos = bytePos;\r
- writeString("tRNS");\r
- writeInt2((icolor >> 16) & 0xFF);\r
- writeInt2((icolor >> 8) & 0xFF);\r
- writeInt2(icolor & 0xFF);\r
- writeCRC();\r
- }\r
-\r
- private byte[] scanLines; // the scan lines to be compressed\r
- private int byteWidth; // width * bytesPerPixel\r
-\r
- //private int hdrPos, dataPos, endPos;\r
- //private byte[] priorRow;\r
- //private byte[] leftBytes;\r
-\r
-\r
- /**\r
- * Write the image data into the pngBytes array. This will write one or more\r
- * PNG "IDAT" chunks. In order to conserve memory, this method grabs as many\r
- * rows as will fit into 32K bytes, or the whole image; whichever is less.\r
- * \r
- * \r
- * @return true if no errors; false if error grabbing pixels\r
- */\r
- private boolean writeImageData() {\r
-\r
- bytesPerPixel = (encodeAlpha ? 4 : 3);\r
- byteWidth = width * bytesPerPixel;\r
-\r
- int scanWidth = byteWidth + 1; // the added 1 is for the filter byte\r
-\r
- //boolean doFilter = (filter != FILTER_NONE);\r
-\r
- int rowsLeft = height; // number of rows remaining to write\r
- //int startRow = 0; // starting row to process this time through\r
- int nRows; // how many rows to grab at a time\r
-\r
- int scanPos; // where we are in the scan lines\r
-\r
- Deflater deflater = new Deflater(compressionLevel);\r
- ByteArrayOutputStream outBytes = new ByteArrayOutputStream(1024);\r
-\r
- DeflaterOutputStream compBytes = new DeflaterOutputStream(outBytes,\r
- deflater);\r
-\r
- int pt = 0; // overall image byte pointer\r
- \r
- // Jmol note: The entire image has been stored in pixels[] already\r
- \r
- try {\r
- while (rowsLeft > 0) {\r
- nRows = Math.max(1, Math.min(32767 / scanWidth, rowsLeft));\r
- scanLines = new byte[scanWidth * nRows];\r
- // if (doFilter)\r
- // switch (filter) {\r
- // case FILTER_SUB:\r
- // leftBytes = new byte[16];\r
- // break;\r
- // case FILTER_UP:\r
- // priorRow = new byte[scanWidth - 1];\r
- // break;\r
- // }\r
- int nPixels = width * nRows;\r
- scanPos = 0;\r
- //startPos = 1;\r
- for (int i = 0; i < nPixels; i++, pt++) {\r
- if (i % width == 0) {\r
- scanLines[scanPos++] = (byte) filter;\r
- //startPos = scanPos;\r
- }\r
- scanLines[scanPos++] = (byte) ((pixels[pt] >> 16) & 0xff);\r
- scanLines[scanPos++] = (byte) ((pixels[pt] >> 8) & 0xff);\r
- scanLines[scanPos++] = (byte) ((pixels[pt]) & 0xff);\r
- if (encodeAlpha) {\r
- scanLines[scanPos++] = (byte) ((pixels[pt] >> 24) & 0xff);\r
- }\r
- // if (doFilter && i % width == width - 1) {\r
- // switch (filter) {\r
- // case FILTER_SUB:\r
- // filterSub();\r
- // break;\r
- // case FILTER_UP:\r
- // filterUp();\r
- // break;\r
- // }\r
- // }\r
- }\r
-\r
- /*\r
- * Write these lines to the output area\r
- */\r
- compBytes.write(scanLines, 0, scanPos);\r
-\r
- //startRow += nRows;\r
- rowsLeft -= nRows;\r
- }\r
- compBytes.close();\r
-\r
- /*\r
- * Write the compressed bytes\r
- */\r
- byte[] compressedLines = outBytes.toByteArray();\r
- writeInt4(compressedLines.length);\r
- startPos = bytePos;\r
- writeString("IDAT");\r
- writeBytes(compressedLines);\r
- writeCRC();\r
- writeEnd();\r
- deflater.finish();\r
- return true;\r
- } catch (IOException e) {\r
- System.err.println(e.toString());\r
- return false;\r
- }\r
- }\r
-\r
- /**\r
- * Write a PNG "IEND" chunk into the pngBytes array.\r
- */\r
- private void writeEnd() {\r
- writeInt4(0);\r
- startPos = bytePos;\r
- writeString("IEND");\r
- writeCRC();\r
- }\r
-\r
- ///**\r
- //* Perform "sub" filtering on the given row.\r
- //* Uses temporary array leftBytes to store the original values\r
- //* of the previous pixels. The array is 16 bytes long, which\r
- //* will easily hold two-byte samples plus two-byte alpha.\r
- //*\r
- //*/\r
- //private void filterSub() {\r
- // int offset = bytesPerPixel;\r
- // int actualStart = startPos + offset;\r
- // int leftInsert = offset;\r
- // int leftExtract = 0;\r
- // //byte current_byte;\r
- //\r
- // for (int i = actualStart; i < startPos + byteWidth; i++) {\r
- // leftBytes[leftInsert] = scanLines[i];\r
- // scanLines[i] = (byte) ((scanLines[i] - leftBytes[leftExtract]) % 256);\r
- // leftInsert = (leftInsert + 1) % 0x0f;\r
- // leftExtract = (leftExtract + 1) % 0x0f;\r
- // }\r
- //}\r
- //\r
- ///**\r
- //* Perform "up" filtering on the given row. Side effect: refills the prior row\r
- //* with current row\r
- //* \r
- //*/\r
- //private void filterUp() {\r
- // int nBytes = width * bytesPerPixel;\r
- // for (int i = 0; i < nBytes; i++) {\r
- // int pt = startPos + i;\r
- // byte b = scanLines[pt];\r
- // scanLines[pt] = (byte) ((scanLines[pt] - priorRow[i]) % 256);\r
- // priorRow[i] = b;\r
- // }\r
- //}\r
-\r
-}\r
+/* $RCSfile$
+ * $Author: nicove $
+ * $Date: 2007-03-30 12:26:16 -0500 (Fri, 30 Mar 2007) $
+ * $Revision: 7275 $
+ *
+ * Copyright (C) 2002-2005 The Jmol Development Team
+ *
+ * Contact: jmol-developers@lists.sf.net
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+package javajs.img;
+
+import java.util.Map;
+import java.util.zip.Deflater;
+import java.util.zip.DeflaterOutputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+
+
+/**
+ *
+ * Modified by Bob Hanson hansonr@stolaf.edu to be a subclass of ImageEncoder
+ * and to use javajs.util.OutputChannel instead of just returning bytes. Also includes:
+ *
+ * -- JavaScript-compatible image processing
+ *
+ * -- transparent background option
+ *
+ * -- more efficient calculation of needs for pngBytes
+ *
+ * -- option to use pre-created PNGJ image data (3/19/14; Jmol 14.1.12)
+ *
+ * -- PNGJ format:
+ *
+ * // IHDR chunk
+ *
+ * // tEXt chunk "Jmol type - <PNG0|PNGJ><0000000pt>+<000000len>"
+ *
+ * // tEXt chunk "Software - Jmol <version>"
+ *
+ * // tEXt chunk "Creation Time - <date>"
+ *
+ * // tRNS chunk transparent color, if desired
+ *
+ * // IDAT chunk (image data)
+ *
+ * // IEND chunk
+ *
+ * // [JMOL ZIP FILE APPENDIX]
+ *
+ * Original Comment:
+ *
+ * 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
+ *
+ * @author J. David Eisenberg
+ * @author http://catcode.com/pngencoder/
+ * @author Christian Ribeaud (christian.ribeaud@genedata.com)
+ * @author Bob Hanson (hansonr@stolaf.edu)
+ *
+ * @version 1.4, 31 March 2000
+ */
+public class PngEncoder extends CRCEncoder {
+
+ /** 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;
+
+ private static final int PT_FIRST_TAG = 37;
+
+ private boolean encodeAlpha;
+ private int filter = FILTER_NONE;
+ private int bytesPerPixel;
+ private int compressionLevel;
+ private String type;
+ private Integer transparentColor;
+
+ private byte[] appData;
+ private String appPrefix;
+ private String comment;
+ private byte[] bytes;
+
+
+ public PngEncoder() {
+ super();
+ }
+
+ @Override
+ protected void setParams(Map<String, Object> params) {
+ if (quality < 0)
+ quality = (params.containsKey("qualityPNG") ? ((Integer) params
+ .get("qualityPNG")).intValue() : 2);
+ if (quality > 9)
+ quality = 9;
+ encodeAlpha = false;
+ filter = FILTER_NONE;
+ compressionLevel = quality;
+ transparentColor = (Integer) params.get("transparentColor");
+ comment = (String) params.get("comment");
+ type = (params.get("type") + "0000").substring(0, 4);
+ bytes = (byte[]) params.get("pngImgData");
+ appData = (byte[]) params.get("pngAppData");
+ appPrefix = (String) params.get("pngAppPrefix");
+ }
+
+
+
+ @Override
+ protected void generate() throws IOException {
+ if (bytes == null) {
+ if (!pngEncode()) {
+ out.cancel();
+ return;
+ }
+ bytes = getBytes();
+ } else {
+ dataLen = bytes.length;
+ }
+ int len = dataLen;
+ if (appData != null) {
+ setJmolTypeText(appPrefix, bytes, len, appData.length,
+ type);
+ out.write(bytes, 0, len);
+ len = (bytes = appData).length;
+ }
+ out.write(bytes, 0, len);
+ }
+
+
+ /**
+ * Creates an array of bytes that is the PNG equivalent of the current image,
+ * specifying whether to encode alpha or not.
+ *
+ * @return true if successful
+ *
+ */
+ private boolean pngEncode() {
+
+ byte[] pngIdBytes = { -119, 80, 78, 71, 13, 10, 26, 10 };
+
+ writeBytes(pngIdBytes);
+ //hdrPos = bytePos;
+ writeHeader();
+ writeText(getApplicationText(appPrefix, type, 0, 0));
+
+ writeText("Software\0Jmol " + comment);
+ writeText("Creation Time\0" + date);
+
+ if (!encodeAlpha && transparentColor != null)
+ writeTransparentColor(transparentColor.intValue());
+ //dataPos = bytePos;
+ return writeImageData();
+ }
+
+ /**
+ * Fill in the Jmol type text area with number of bytes of PNG data and number
+ * of bytes of Jmol state data and fix checksum.
+ *
+ * If we do not do this, then the checksum will be wrong, and Jmol and some
+ * other programs may not be able to read the PNG image.
+ *
+ * This was corrected for Jmol 12.3.30. Between 12.3.7 and 12.3.29, PNG files
+ * created by Jmol have incorrect checksums.
+ *
+ * @param prefix
+ *
+ * @param b
+ * @param nPNG
+ * @param nState
+ * @param type
+ */
+ private static void setJmolTypeText(String prefix, byte[] b, int nPNG, int nState, String type) {
+ String s = "tEXt" + getApplicationText(prefix, type, nPNG, nState);
+ CRCEncoder encoder = new PngEncoder();
+ byte[] test = s.substring(0, 4 + prefix.length()).getBytes();
+ for (int i = test.length; -- i >= 0;)
+ if (b[i + PT_FIRST_TAG] != test[i]) {
+ System.out.println("image is not of the right form; appending data, but not adding tEXt tag.");
+ return;
+ }
+ encoder.setData(b, PT_FIRST_TAG);
+ encoder.writeString(s);
+ encoder.writeCRC();
+ }
+
+ private static String getApplicationText(String prefix, String type, int nPNG, int nState) {
+ String sPNG = "000000000" + nPNG;
+ sPNG = sPNG.substring(sPNG.length() - 9);
+ String sState = "000000000" + nState;
+ sState = sState.substring(sState.length() - 9);
+ return prefix + "\0" + type + (type.equals("PNG") ? "0" : "") + sPNG + "+"
+ + sState;
+ }
+
+ // /**
+ // * Set the filter to use
+ // *
+ // * @param whichFilter from constant list
+ // */
+ // public void setFilter(int whichFilter) {
+ // this.filter = (whichFilter <= FILTER_LAST ? whichFilter : FILTER_NONE);
+ // }
+
+ // /**
+ // * 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;
+ // }
+
+ /**
+ * Write a PNG "IHDR" chunk into the pngBytes array.
+ */
+ private void writeHeader() {
+
+ writeInt4(13);
+ startPos = bytePos;
+ writeString("IHDR");
+ writeInt4(width);
+ writeInt4(height);
+ writeByte(8); // bit depth
+ writeByte(encodeAlpha ? 6 : 2); // color type or direct model
+ writeByte(0); // compression method
+ writeByte(0); // filter method
+ writeByte(0); // no interlace
+ writeCRC();
+ }
+
+ private void writeText(String msg) {
+ writeInt4(msg.length());
+ startPos = bytePos;
+ writeString("tEXt" + msg);
+ writeCRC();
+ }
+
+ /**
+ * Write a PNG "tRNS" chunk into the pngBytes array.
+ *
+ * @param icolor
+ */
+ private void writeTransparentColor(int icolor) {
+
+ writeInt4(6);
+ startPos = bytePos;
+ writeString("tRNS");
+ writeInt2((icolor >> 16) & 0xFF);
+ writeInt2((icolor >> 8) & 0xFF);
+ writeInt2(icolor & 0xFF);
+ writeCRC();
+ }
+
+ private byte[] scanLines; // the scan lines to be compressed
+ private int byteWidth; // width * bytesPerPixel
+
+ //private int hdrPos, dataPos, endPos;
+ //private byte[] priorRow;
+ //private byte[] leftBytes;
+
+
+ /**
+ * 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
+ */
+ private boolean writeImageData() {
+
+ bytesPerPixel = (encodeAlpha ? 4 : 3);
+ byteWidth = width * bytesPerPixel;
+
+ int scanWidth = byteWidth + 1; // the added 1 is for the filter byte
+
+ //boolean doFilter = (filter != FILTER_NONE);
+
+ 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
+
+ int scanPos; // where we are in the scan lines
+
+ Deflater deflater = new Deflater(compressionLevel);
+ ByteArrayOutputStream outBytes = new ByteArrayOutputStream(1024);
+
+ DeflaterOutputStream compBytes = new DeflaterOutputStream(outBytes,
+ deflater);
+
+ int pt = 0; // overall image byte pointer
+
+ // Jmol note: The entire image has been stored in pixels[] already
+
+ try {
+ while (rowsLeft > 0) {
+ nRows = Math.max(1, Math.min(32767 / scanWidth, rowsLeft));
+ scanLines = new byte[scanWidth * nRows];
+ // if (doFilter)
+ // switch (filter) {
+ // case FILTER_SUB:
+ // leftBytes = new byte[16];
+ // break;
+ // case FILTER_UP:
+ // priorRow = new byte[scanWidth - 1];
+ // break;
+ // }
+ int nPixels = width * nRows;
+ scanPos = 0;
+ //startPos = 1;
+ for (int i = 0; i < nPixels; i++, pt++) {
+ if (i % width == 0) {
+ scanLines[scanPos++] = (byte) filter;
+ //startPos = scanPos;
+ }
+ scanLines[scanPos++] = (byte) ((pixels[pt] >> 16) & 0xff);
+ scanLines[scanPos++] = (byte) ((pixels[pt] >> 8) & 0xff);
+ scanLines[scanPos++] = (byte) ((pixels[pt]) & 0xff);
+ if (encodeAlpha) {
+ scanLines[scanPos++] = (byte) ((pixels[pt] >> 24) & 0xff);
+ }
+ // if (doFilter && i % width == width - 1) {
+ // switch (filter) {
+ // case FILTER_SUB:
+ // filterSub();
+ // break;
+ // case FILTER_UP:
+ // filterUp();
+ // break;
+ // }
+ // }
+ }
+
+ /*
+ * Write these lines to the output area
+ */
+ compBytes.write(scanLines, 0, scanPos);
+
+ //startRow += nRows;
+ rowsLeft -= nRows;
+ }
+ compBytes.close();
+
+ /*
+ * Write the compressed bytes
+ */
+ byte[] compressedLines = outBytes.toByteArray();
+ writeInt4(compressedLines.length);
+ startPos = bytePos;
+ writeString("IDAT");
+ writeBytes(compressedLines);
+ writeCRC();
+ writeEnd();
+ deflater.finish();
+ return true;
+ } catch (IOException e) {
+ System.err.println(e.toString());
+ return false;
+ }
+ }
+
+ /**
+ * Write a PNG "IEND" chunk into the pngBytes array.
+ */
+ private void writeEnd() {
+ writeInt4(0);
+ startPos = bytePos;
+ writeString("IEND");
+ writeCRC();
+ }
+
+ ///**
+ //* 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.
+ //*
+ //*/
+ //private void filterSub() {
+ // int offset = bytesPerPixel;
+ // int actualStart = startPos + offset;
+ // int leftInsert = offset;
+ // int leftExtract = 0;
+ // //byte current_byte;
+ //
+ // for (int i = actualStart; i < startPos + byteWidth; i++) {
+ // leftBytes[leftInsert] = scanLines[i];
+ // scanLines[i] = (byte) ((scanLines[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
+ //*
+ //*/
+ //private void filterUp() {
+ // int nBytes = width * bytesPerPixel;
+ // for (int i = 0; i < nBytes; i++) {
+ // int pt = startPos + i;
+ // byte b = scanLines[pt];
+ // scanLines[pt] = (byte) ((scanLines[pt] - priorRow[i]) % 256);
+ // priorRow[i] = b;
+ // }
+ //}
+
+}