PNGImageEncoder.java
上传用户:btjssb159
上传日期:2018-01-04
资源大小:241k
文件大小:32k
源码类别:

DNA

开发平台:

Java

  1. /*
  2.  * Copyright (c) 2001 Sun Microsystems, Inc. All Rights Reserved.
  3.  *
  4.  * Redistribution and use in source and binary forms, with or without 
  5.  * modification, are permitted provided that the following conditions are met:
  6.  * 
  7.  * -Redistributions of source code must retain the above copyright notice, this 
  8.  * list of conditions and the following disclaimer.
  9.  *
  10.  * -Redistribution in binary form must reproduct the above copyright notice,
  11.  * this list of conditions and the following disclaimer in the documentation
  12.  * and/or other materials provided with the distribution.
  13.  * 
  14.  * Neither the name of Sun Microsystems, Inc. or the names of contributors may
  15.  * be used to endorse or promote products derived from this software without
  16.  * specific prior written permission.
  17.  * 
  18.  * This software is provided "AS IS," without a warranty of any kind. ALL
  19.  * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
  20.  * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
  21.  * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE
  22.  * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
  23.  * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS
  24.  * LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
  25.  * INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
  26.  * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
  27.  * OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
  28.  * POSSIBILITY OF SUCH DAMAGES.
  29.  * 
  30.  * You acknowledge that Software is not designed,licensed or intended for use in 
  31.  * the design, construction, operation or maintenance of any nuclear facility.
  32.  */
  33. import java.awt.image.IndexColorModel;
  34. import java.awt.image.ColorModel;
  35. import java.awt.image.Raster;
  36. import java.awt.image.RenderedImage;
  37. import java.awt.image.SampleModel;
  38. import java.io.ByteArrayOutputStream;
  39. import java.io.DataOutput;
  40. import java.io.DataOutputStream;
  41. import java.io.FileOutputStream;
  42. import java.io.FilterOutputStream;
  43. import java.io.IOException;
  44. import java.io.OutputStream;
  45. import java.util.Calendar;
  46. import java.util.Date;
  47. import java.util.GregorianCalendar;
  48. import java.util.TimeZone;
  49. import java.util.zip.Deflater;
  50. import java.util.zip.DeflaterOutputStream;
  51. class CRC {
  52.     private static int[] crcTable = new int[256];
  53.     static {
  54.         // Initialize CRC table
  55.         for (int n = 0; n < 256; n++) {
  56.             int c = n;
  57.             for (int k = 0; k < 8; k++) {
  58.                 if ((c & 1) == 1) {
  59.                     c = 0xedb88320 ^ (c >>> 1);
  60.                 } else {
  61.                     c >>>= 1;
  62.                 }
  63.                 crcTable[n] = c;
  64.             }
  65.         }
  66.     }
  67.     public static int updateCRC(int crc, byte[] data, int off, int len) {
  68.         int c = crc;
  69.         for (int n = 0; n < len; n++) {
  70.              c = crcTable[(c ^ data[off + n]) & 0xff] ^ (c >>> 8);
  71.         }
  72.         return c;
  73.     }
  74. }
  75. class ChunkStream extends OutputStream implements DataOutput {
  76.     
  77.     private String type;
  78.     private ByteArrayOutputStream baos;
  79.     private DataOutputStream dos;
  80.     public ChunkStream(String type) throws IOException {
  81.         this.type = type;
  82.         
  83.         this.baos = new ByteArrayOutputStream();
  84.         this.dos = new DataOutputStream(baos);
  85.     }
  86.     public void write(byte[] b) throws IOException {
  87.         dos.write(b);
  88.     }
  89.     public void write(byte[] b, int off, int len) throws IOException {
  90.         dos.write(b, off, len);
  91.     }
  92.     public void write(int b) throws IOException {
  93.         dos.write(b);
  94.     }
  95.     public void writeBoolean(boolean v) throws IOException {
  96.         dos.writeBoolean(v);
  97.     }
  98.     public void writeByte(int v) throws IOException {
  99.         dos.writeByte(v);
  100.     }
  101.     
  102.     public void writeBytes(String s) throws IOException {
  103.         dos.writeBytes(s);
  104.     }
  105.     public void writeChar(int v) throws IOException {
  106.         dos.writeChar(v);
  107.     }
  108.     public void writeChars(String s) throws IOException {
  109.         dos.writeChars(s);
  110.     }
  111.     public void writeDouble(double v) throws IOException {
  112.         dos.writeDouble(v);
  113.     }
  114.     public void writeFloat(float v) throws IOException {
  115.         dos.writeFloat(v);
  116.     }
  117.     public void writeInt(int v) throws IOException {
  118.         dos.writeInt(v);
  119.     }
  120.     public void writeLong(long v) throws IOException {
  121.         dos.writeLong(v);
  122.     }
  123.     public void writeShort(int v) throws IOException {
  124.         dos.writeShort(v);
  125.     }
  126.     public void writeUTF(String str) throws IOException {
  127.         dos.writeUTF(str);
  128.     }
  129.     public void writeToStream(DataOutputStream output) throws IOException {
  130.         byte[] typeSignature = new byte[4];
  131.         typeSignature[0] = (byte)type.charAt(0);
  132.         typeSignature[1] = (byte)type.charAt(1);
  133.         typeSignature[2] = (byte)type.charAt(2);
  134.         typeSignature[3] = (byte)type.charAt(3);
  135.         dos.flush();
  136.         baos.flush();
  137.         byte[] data = baos.toByteArray();
  138.         int len = data.length;
  139.         output.writeInt(len);
  140.         output.write(typeSignature);
  141.         output.write(data, 0, len);
  142.         int crc = 0xffffffff;
  143.         crc = CRC.updateCRC(crc, typeSignature, 0, 4);
  144.         crc = CRC.updateCRC(crc, data, 0, len);
  145.         output.writeInt(crc ^ 0xffffffff);
  146.     }
  147. }
  148. class IDATOutputStream extends FilterOutputStream {
  149.     private static final byte[] typeSignature =
  150.       {(byte)'I', (byte)'D', (byte)'A', (byte)'T'};
  151.     private int bytesWritten = 0;
  152.     private int segmentLength;
  153.     byte[] buffer;
  154.     public IDATOutputStream(OutputStream output,
  155.                             int segmentLength) {
  156.         super(output);
  157.         this.segmentLength = segmentLength;
  158.         this.buffer = new byte[segmentLength];
  159.     }
  160.     public void close() throws IOException {
  161.         flush();
  162.     }
  163.     private void writeInt(int x) throws IOException {
  164.         out.write(x >> 24);
  165.         out.write((x >> 16) & 0xff);
  166.         out.write((x >> 8) & 0xff);
  167.         out.write(x & 0xff);
  168.     }
  169.     public void flush() throws IOException {
  170.         // Length
  171.         writeInt(bytesWritten);
  172.         // 'IDAT' signature
  173.         out.write(typeSignature);
  174.         // Data
  175.         out.write(buffer, 0, bytesWritten);
  176.         int crc = 0xffffffff;
  177.         crc = CRC.updateCRC(crc, typeSignature, 0, 4);
  178.         crc = CRC.updateCRC(crc, buffer, 0, bytesWritten);
  179.         // CRC
  180.         writeInt(crc ^ 0xffffffff);
  181.         // Reset buffer
  182.         bytesWritten = 0;
  183.     }
  184.     public void write(byte[] b) throws IOException {
  185.         this.write(b, 0, b.length);
  186.     }
  187.     public void write(byte[] b, int off, int len) throws IOException {
  188.         while (len > 0) {
  189.             int bytes = Math.min(segmentLength - bytesWritten, len);
  190.             System.arraycopy(b, off, buffer, bytesWritten, bytes);
  191.             off += bytes;
  192.             len -= bytes;
  193.             bytesWritten += bytes;
  194.             if (bytesWritten == segmentLength) {
  195.                 flush();
  196.             }
  197.         }
  198.     }
  199.     public void write(int b) throws IOException {
  200.         buffer[bytesWritten++] = (byte)b;
  201.         if (bytesWritten == segmentLength) {
  202.             flush();
  203.         }
  204.     }
  205. }
  206. /**
  207.  * An ImageEncoder for the PNG file format.
  208.  *
  209.  */
  210. public class PNGImageEncoder extends ImageEncoderImpl {
  211.     private static final int PNG_COLOR_GRAY = 0;
  212.     private static final int PNG_COLOR_RGB = 2;
  213.     private static final int PNG_COLOR_PALETTE = 3;
  214.     private static final int PNG_COLOR_GRAY_ALPHA = 4;
  215.     private static final int PNG_COLOR_RGB_ALPHA = 6;
  216.     private static final byte[] magic = {
  217.         (byte)137, (byte) 80, (byte) 78, (byte) 71,
  218.         (byte) 13, (byte) 10, (byte) 26, (byte) 10
  219.     };
  220.     private PNGEncodeParam param;
  221.     private RenderedImage image;
  222.     private int width;
  223.     private int height;
  224.     private int bitDepth;
  225.     private int bitShift;
  226.     private int numBands;
  227.     private int colorType;
  228.     private int bpp; // bytes per pixel, rounded up
  229.     private boolean skipAlpha = false;
  230.     private boolean compressGray = false;
  231.     private boolean interlace;
  232.     private byte[] redPalette = null;
  233.     private byte[] greenPalette = null;
  234.     private byte[] bluePalette = null;
  235.     private byte[] alphaPalette = null;
  236.     
  237.     private DataOutputStream dataOutput;
  238.     public PNGImageEncoder(OutputStream output,
  239.                            PNGEncodeParam param) {
  240.         super(output, param);
  241.         if (param != null) {
  242.             this.param = (PNGEncodeParam)param;
  243.         }
  244.         this.dataOutput = new DataOutputStream(output);
  245.     }
  246.     private void writeMagic() throws IOException {
  247.         dataOutput.write(magic);
  248.     }
  249.     private void writeIHDR() throws IOException {
  250.         ChunkStream cs = new ChunkStream("IHDR");
  251.         cs.writeInt(width);
  252.         cs.writeInt(height);
  253.         cs.writeByte((byte)bitDepth);
  254.         cs.writeByte((byte)colorType);
  255.         cs.writeByte((byte)0);
  256.         cs.writeByte((byte)0);
  257.         cs.writeByte(interlace ? (byte)1 : (byte)0);
  258.         
  259.         cs.writeToStream(dataOutput);
  260.     }
  261.     private byte[] prevRow = null;
  262.     private byte[] currRow = null;
  263.     private byte[][] filteredRows = null;
  264.     private static int clamp(int val, int maxValue) {
  265.         return (val > maxValue) ? maxValue : val;
  266.     }
  267.     private void encodePass(OutputStream os,
  268.                             Raster ras,
  269.                             int xOffset, int yOffset,
  270.                             int xSkip, int ySkip) throws IOException {
  271.         int minX = ras.getMinX();
  272.         int minY = ras.getMinY();
  273.         int width = ras.getWidth();
  274.         int height = ras.getHeight();
  275.         
  276.         xOffset *= numBands;
  277.         xSkip *= numBands;
  278.         int samplesPerByte = 8/bitDepth;
  279.         
  280.         int numSamples = width*numBands;
  281.         int[] samples = new int[numSamples];
  282.         int pixels = (numSamples - xOffset + xSkip - 1)/xSkip;
  283.         int bytesPerRow = pixels*numBands;
  284.         if (bitDepth < 8) {
  285.             bytesPerRow = (bytesPerRow + samplesPerByte - 1)/samplesPerByte;
  286.         } else if (bitDepth == 16) {
  287.             bytesPerRow *= 2;
  288.         }
  289.         if (bytesPerRow == 0) {
  290.             return;
  291.         }
  292.         currRow = new byte[bytesPerRow + bpp];
  293.         prevRow = new byte[bytesPerRow + bpp];
  294.         filteredRows = new byte[5][bytesPerRow + bpp];
  295.         int maxValue = (1 << bitDepth) - 1;
  296.         for (int row = minY + yOffset; row < minY + height; row += ySkip) {
  297.             ras.getPixels(minX, row, width, 1, samples);
  298.             if (compressGray) {
  299.                 int shift = 8 - bitDepth;
  300.                 for (int i = 0; i < width; i++) {
  301.                     samples[i] >>= shift;
  302.                 }
  303.             }
  304.             int count = bpp; // leave first 'bpp' bytes zero
  305.             int pos = 0;
  306.             int tmp = 0;
  307.             switch (bitDepth) {
  308.             case 1: case 2: case 4:
  309.                 // Image can only have a single band
  310.                 
  311.                 int mask = samplesPerByte - 1;
  312.                 for (int s = xOffset; s < numSamples; s += xSkip) {
  313.                     int val = clamp(samples[s] >> bitShift, maxValue);
  314.                     tmp = (tmp << bitDepth) | val;
  315.                     if ((pos++ & mask) == mask) {
  316.                         currRow[count++] = (byte)tmp;
  317.                         tmp = 0;
  318.                     }
  319.                 }
  320.                 // Left shift the last byte
  321.                 if ((pos & mask) != 0) {
  322.                     tmp <<= ((8/bitDepth) - pos)*bitDepth;
  323.                     currRow[count++] = (byte)tmp;
  324.                 }
  325.                 break;
  326.             case 8:
  327.                 for (int s = xOffset; s < numSamples; s += xSkip) {
  328.                     for (int b = 0; b < numBands; b++) {
  329.                         currRow[count++] =
  330.                             (byte)clamp(samples[s + b] >> bitShift, maxValue);
  331.                     }
  332.                 }
  333.                 break;
  334.             case 16:
  335.                 for (int s = xOffset; s < numSamples; s += xSkip) {
  336.                     for (int b = 0; b < numBands; b++) {
  337.                         int val = clamp(samples[s + b] >> bitShift, maxValue);
  338.                         currRow[count++] = (byte)(val >> 8);
  339.                         currRow[count++] = (byte)(val & 0xff);
  340.                     }
  341.                 }
  342.                 break;
  343.             }
  344.             // Perform filtering
  345.             int filterType = param.filterRow(currRow, prevRow,
  346.                                              filteredRows,
  347.                                              bytesPerRow, bpp);
  348.             
  349.             os.write(filterType);
  350.             os.write(filteredRows[filterType], bpp, bytesPerRow);
  351.             
  352.             // Swap current and previous rows
  353.             byte[] swap = currRow;
  354.             currRow = prevRow;
  355.             prevRow = swap;
  356.         }
  357.     }
  358.     private void writeIDAT() throws IOException {
  359.         IDATOutputStream ios = new IDATOutputStream(dataOutput, 8192);
  360.         DeflaterOutputStream dos =
  361.             new DeflaterOutputStream(ios, new Deflater(9));
  362.         // Future work - don't convert entire image to a Raster
  363.         Raster ras = image.getData();
  364.         if (skipAlpha) {
  365.             int numBands = ras.getNumBands() - 1;
  366.             int[] bandList = new int[numBands];
  367.             for (int i = 0; i < numBands; i++) {
  368.                 bandList[i] = i;
  369.             }
  370.             ras = ras.createChild(0, 0,
  371.                                   ras.getWidth(), ras.getHeight(),
  372.                                   0, 0,
  373.                                   bandList);
  374.         }
  375.         if (interlace) {
  376.             // Interlacing pass 1
  377.             encodePass(dos, ras, 0, 0, 8, 8);
  378.             // Interlacing pass 2
  379.             encodePass(dos, ras, 4, 0, 8, 8);
  380.             // Interlacing pass 3
  381.             encodePass(dos, ras, 0, 4, 4, 8);
  382.             // Interlacing pass 4
  383.             encodePass(dos, ras, 2, 0, 4, 4);
  384.             // Interlacing pass 5
  385.             encodePass(dos, ras, 0, 2, 2, 4);
  386.             // Interlacing pass 6
  387.             encodePass(dos, ras, 1, 0, 2, 2);
  388.             // Interlacing pass 7
  389.             encodePass(dos, ras, 0, 1, 1, 2);
  390.         } else {
  391.             encodePass(dos, ras, 0, 0, 1, 1);
  392.         }
  393.         dos.finish();
  394.         ios.flush();
  395.     }
  396.     private void writeIEND() throws IOException {
  397.         ChunkStream cs = new ChunkStream("IEND");
  398.         cs.writeToStream(dataOutput);
  399.     }
  400.     private static final float[] srgbChroma = {
  401.         0.31270F, 0.329F, 0.64F, 0.33F, 0.3F, 0.6F, 0.15F, 0.06F
  402.     };
  403.     private void writeCHRM() throws IOException {
  404.         if (param.isChromaticitySet() || param.isSRGBIntentSet()) {
  405.             ChunkStream cs = new ChunkStream("cHRM");
  406.             
  407.             float[] chroma;
  408.             if (!param.isSRGBIntentSet()) {
  409.                 chroma = param.getChromaticity();
  410.             } else {
  411.                 chroma = srgbChroma; // SRGB chromaticities
  412.             }
  413.             for (int i = 0; i < 8; i++) {
  414.                 cs.writeInt((int)(chroma[i]*100000));
  415.             }
  416.             cs.writeToStream(dataOutput);
  417.         }
  418.     }
  419.     private void writeGAMA() throws IOException {
  420.         if (param.isGammaSet() || param.isSRGBIntentSet()) {
  421.             ChunkStream cs = new ChunkStream("gAMA");
  422.             float gamma;
  423.             if (!param.isSRGBIntentSet()) {
  424.                 gamma = param.getGamma();
  425.             } else {
  426.                 gamma = 1.0F/2.2F; // SRGB gamma
  427.             }
  428.             
  429.             cs.writeInt((int)(gamma*100000));
  430.             cs.writeToStream(dataOutput);
  431.         }
  432.     }
  433.     private void writeICCP() throws IOException {
  434.         if (param.isICCProfileDataSet()) {
  435.             ChunkStream cs = new ChunkStream("iCCP");
  436.             byte[] ICCProfileData = param.getICCProfileData();
  437.             cs.write(ICCProfileData);
  438.             cs.writeToStream(dataOutput);
  439.         }
  440.     }
  441.     private void writeSBIT() throws IOException {
  442.         if (param.isSignificantBitsSet()) {
  443.             ChunkStream cs = new ChunkStream("sBIT");
  444.             int[] significantBits = param.getSignificantBits();
  445.             int len = significantBits.length;
  446.             for (int i = 0; i < len; i++) {
  447.                 cs.writeByte(significantBits[i]);
  448.             }
  449.             cs.writeToStream(dataOutput);
  450.         }
  451.     }
  452.     private void writeSRGB() throws IOException {
  453.         if (param.isSRGBIntentSet()) {
  454.             ChunkStream cs = new ChunkStream("sRGB");
  455.             int intent = param.getSRGBIntent();
  456.             cs.write(intent);
  457.             cs.writeToStream(dataOutput);
  458.         }
  459.     }
  460.     private void writePLTE() throws IOException {
  461.         if (redPalette == null) {
  462.             return;
  463.         }
  464.         ChunkStream cs = new ChunkStream("PLTE");
  465.         for (int i = 0; i < redPalette.length; i++) {
  466.             cs.writeByte(redPalette[i]);
  467.             cs.writeByte(greenPalette[i]);
  468.             cs.writeByte(bluePalette[i]);
  469.         }
  470.             
  471.         cs.writeToStream(dataOutput);
  472.     }
  473.     private void writeBKGD() throws IOException {
  474.         if (param.isBackgroundSet()) {
  475.             ChunkStream cs = new ChunkStream("bKGD");
  476.             switch (colorType) {
  477.             case PNG_COLOR_GRAY:
  478.             case PNG_COLOR_GRAY_ALPHA:
  479.                 int gray = ((PNGEncodeParam.Gray)param).getBackgroundGray();
  480.                 cs.writeShort(gray);
  481.                 break;
  482.             case PNG_COLOR_PALETTE:
  483.                 int index =
  484.                    ((PNGEncodeParam.Palette)param).getBackgroundPaletteIndex();
  485.                 cs.writeByte(index);
  486.                 break;
  487.             case PNG_COLOR_RGB:
  488.             case PNG_COLOR_RGB_ALPHA:
  489.                 int[] rgb = ((PNGEncodeParam.RGB)param).getBackgroundRGB();
  490.                 cs.writeShort(rgb[0]);
  491.                 cs.writeShort(rgb[1]);
  492.                 cs.writeShort(rgb[2]);
  493.                 break;
  494.             }
  495.             
  496.             cs.writeToStream(dataOutput);
  497.         }
  498.     }
  499.     private void writeHIST() throws IOException {
  500.         if (param.isPaletteHistogramSet()) {
  501.             ChunkStream cs = new ChunkStream("hIST");
  502.             
  503.             int[] hist = param.getPaletteHistogram();
  504.             for (int i = 0; i < hist.length; i++) {
  505.                 cs.writeShort(hist[i]);
  506.             }
  507.             cs.writeToStream(dataOutput);
  508.         }
  509.     }
  510.     private void writeTRNS() throws IOException {
  511.         if (param.isTransparencySet() && 
  512.             (colorType != PNG_COLOR_GRAY_ALPHA) &&
  513.             (colorType != PNG_COLOR_RGB_ALPHA)) {
  514.             ChunkStream cs = new ChunkStream("tRNS");
  515.             if (param instanceof PNGEncodeParam.Palette) {
  516.                 byte[] t =
  517.                     ((PNGEncodeParam.Palette)param).getPaletteTransparency();
  518.                 for (int i = 0; i < t.length; i++) {
  519.                     cs.writeByte(t[i]);
  520.                 }
  521.             } else if (param instanceof PNGEncodeParam.Gray) {
  522.                 int t = ((PNGEncodeParam.Gray)param).getTransparentGray();
  523.                 cs.writeShort(t);
  524.             } else if (param instanceof PNGEncodeParam.RGB) {
  525.                 int[] t = ((PNGEncodeParam.RGB)param).getTransparentRGB();
  526.                 cs.writeShort(t[0]);
  527.                 cs.writeShort(t[1]);
  528.                 cs.writeShort(t[2]);
  529.             }
  530.             cs.writeToStream(dataOutput);
  531.         } else if (colorType == PNG_COLOR_PALETTE) {
  532.             int lastEntry = Math.min(255, alphaPalette.length - 1);
  533.             int nonOpaque;
  534.             for (nonOpaque = lastEntry; nonOpaque >= 0; nonOpaque--) {
  535.                 if (alphaPalette[nonOpaque] != (byte)255) {
  536.                     break;
  537.                 }
  538.             }
  539.             if (nonOpaque >= 0) {
  540.                 ChunkStream cs = new ChunkStream("tRNS");
  541.                 for (int i = 0; i <= nonOpaque; i++) {
  542.                     cs.writeByte(alphaPalette[i]);
  543.                 }
  544.                 cs.writeToStream(dataOutput);
  545.             }
  546.         }
  547.     }
  548.     private void writePHYS() throws IOException {
  549.         if (param.isPhysicalDimensionSet()) {
  550.             ChunkStream cs = new ChunkStream("pHYs");
  551.             int[] dims = param.getPhysicalDimension();
  552.             cs.writeInt(dims[0]);
  553.             cs.writeInt(dims[1]);
  554.             cs.writeByte((byte)dims[2]);
  555.             cs.writeToStream(dataOutput);
  556.         }
  557.     }
  558.     private void writeSPLT() throws IOException {
  559.         if (param.isSuggestedPaletteSet()) {
  560.             ChunkStream cs = new ChunkStream("sPLT");
  561.             System.out.println("sPLT not supported yet.");
  562.             cs.writeToStream(dataOutput);
  563.         }
  564.     }
  565.     private void writeTIME() throws IOException {
  566.         if (param.isModificationTimeSet()) {
  567.             ChunkStream cs = new ChunkStream("tIME");
  568.             Date date = param.getModificationTime();
  569.             TimeZone gmt = TimeZone.getTimeZone("GMT");
  570.             
  571.             GregorianCalendar cal = new GregorianCalendar(gmt);
  572.             cal.setTime(date);
  573.             int year = cal.get(Calendar.YEAR);
  574.             int month = cal.get(Calendar.MONTH);
  575.             int day = cal.get(Calendar.DAY_OF_MONTH);
  576.             int hour = cal.get(Calendar.HOUR_OF_DAY);
  577.             int minute = cal.get(Calendar.MINUTE);
  578.             int second = cal.get(Calendar.SECOND);
  579.             cs.writeShort(year);
  580.             cs.writeByte(month + 1);
  581.             cs.writeByte(day);
  582.             cs.writeByte(hour);
  583.             cs.writeByte(minute);
  584.             cs.writeByte(second);
  585.             cs.writeToStream(dataOutput);
  586.         }
  587.     }
  588.     private void writeTEXT() throws IOException {
  589.         if (param.isTextSet()) {
  590.             String[] text = param.getText();
  591.             
  592.             for (int i = 0; i < text.length/2; i++) {
  593.                 byte[] keyword = text[2*i].getBytes();
  594.                 byte[] value = text[2*i + 1].getBytes();
  595.                 ChunkStream cs = new ChunkStream("tEXt");
  596.                 cs.write(keyword, 0, Math.min(keyword.length, 79));
  597.                 cs.write(0);
  598.                 cs.write(value);
  599.                 cs.writeToStream(dataOutput);
  600.             }
  601.         }
  602.     }
  603.     private void writeZTXT() throws IOException {
  604.         if (param.isCompressedTextSet()) {
  605.             String[] text = param.getCompressedText();
  606.             
  607.             for (int i = 0; i < text.length/2; i++) {
  608.                 byte[] keyword = text[2*i].getBytes();
  609.                 byte[] value = text[2*i + 1].getBytes();
  610.                 ChunkStream cs = new ChunkStream("zTXt");
  611.                 cs.write(keyword, 0, Math.min(keyword.length, 79));
  612.                 cs.write(0);
  613.                 cs.write(0);
  614.                 DeflaterOutputStream dos = new DeflaterOutputStream(cs);
  615.                 dos.write(value);
  616.                 dos.finish();
  617.                 cs.writeToStream(dataOutput);
  618.             }
  619.         }
  620.     }
  621.     private void writePrivateChunks() throws IOException {
  622.         int numChunks = param.getNumPrivateChunks();
  623.         for (int i = 0; i < numChunks; i++) {
  624.             String type = param.getPrivateChunkType(i);
  625.             char char3 = type.charAt(3);
  626.             byte[] data = param.getPrivateChunkData(i);
  627.             
  628.             ChunkStream cs = new ChunkStream(type);
  629.             cs.write(data);
  630.             cs.writeToStream(dataOutput);
  631.         }
  632.     }
  633.     /**
  634.      * Analyzes a set of palettes and determines if it can be expressed
  635.      * as a standard set of gray values, with zero or one values being
  636.      * fully transparent and the rest being fully opaque.  If it
  637.      * is possible to express the data thusly, the method returns
  638.      * a suitable instance of PNGEncodeParam.Gray; otherwise it
  639.      * returns null.
  640.      */
  641.     private PNGEncodeParam.Gray createGrayParam(byte[] redPalette,
  642.                                                 byte[] greenPalette,
  643.                                                 byte[] bluePalette,
  644.                                                 byte[] alphaPalette) {
  645.         PNGEncodeParam.Gray param = new PNGEncodeParam.Gray();
  646.         int numTransparent = 0;
  647.         int grayFactor = 255/((1 << bitDepth) - 1);
  648.         int entries = 1 << bitDepth;
  649.         for (int i = 0; i < entries; i++) {
  650.             byte red = redPalette[i];
  651.             if ((red != i*grayFactor) ||
  652.                 (red != greenPalette[i]) ||
  653.                 (red != bluePalette[i])) {
  654.                 return null;
  655.             }
  656.             // All alphas must be 255 except at most 1 can be 0
  657.             byte alpha = alphaPalette[i];
  658.             if (alpha == (byte)0) {
  659.                 param.setTransparentGray(i);
  660.                     
  661.                 ++numTransparent;
  662.                 if (numTransparent > 1) {
  663.                     return null;
  664.                 }
  665.             } else if (alpha != (byte)255) {
  666.                 return null;
  667.             }
  668.         }
  669.         return param;
  670.     }
  671.     public void encode(RenderedImage im) throws IOException {
  672.         this.image = im;
  673.         this.width = image.getWidth();
  674.         this.height = image.getHeight();
  675.         SampleModel sampleModel = image.getSampleModel();
  676.         int[] sampleSize = sampleModel.getSampleSize();
  677.         // Set bitDepth to a sentinel value
  678.         this.bitDepth = -1;
  679.         this.bitShift = 0;
  680.         // Allow user to override the bit depth of gray images
  681.         if (param instanceof PNGEncodeParam.Gray) {
  682.             PNGEncodeParam.Gray paramg = (PNGEncodeParam.Gray)param;
  683.             if (paramg.isBitDepthSet()) {
  684.                 this.bitDepth = paramg.getBitDepth();
  685.             }
  686.             if (paramg.isBitShiftSet()) {
  687.                 this.bitShift = paramg.getBitShift();
  688.             }
  689.         }
  690.         
  691.         // Get bit depth from image if not set in param
  692.         if (this.bitDepth == -1) {
  693.             // Get bit depth from channel 0 of the image
  694.             this.bitDepth = sampleSize[0];
  695.             // Ensure all channels have the same bit depth
  696.             for (int i = 1; i < sampleSize.length; i++) {
  697.                 if (sampleSize[i] != bitDepth) {
  698.                     throw new RuntimeException();
  699.                 }
  700.             }
  701.         
  702.             // Round bit depth up to a power of 2
  703.             if (bitDepth > 2 && bitDepth < 4) {
  704.                 bitDepth = 4;
  705.             } else if (bitDepth > 4 && bitDepth < 8) {
  706.                 bitDepth = 8;
  707.             } else if (bitDepth > 8 && bitDepth < 16) {
  708.                 bitDepth = 16;
  709.             } else if (bitDepth > 16) {
  710.                 throw new RuntimeException();
  711.             }
  712.         }
  713.         this.numBands = sampleModel.getNumBands();
  714.         this.bpp = numBands*((bitDepth == 16) ? 2 : 1);
  715.         
  716.         ColorModel colorModel = image.getColorModel();
  717.         if (colorModel instanceof IndexColorModel) {
  718.             if (bitDepth < 1 || bitDepth > 8) {
  719.                 throw new RuntimeException();
  720.             }
  721.             if (sampleModel.getNumBands() != 1) {
  722.                 throw new RuntimeException();
  723.             }
  724.             IndexColorModel icm = (IndexColorModel)colorModel;
  725.             int size = icm.getMapSize();
  726.             
  727.             redPalette = new byte[size];
  728.             greenPalette = new byte[size];
  729.             bluePalette = new byte[size];
  730.             alphaPalette = new byte[size];
  731.             
  732.             icm.getReds(redPalette);
  733.             icm.getGreens(greenPalette);
  734.             icm.getBlues(bluePalette);
  735.             icm.getAlphas(alphaPalette);
  736.             this.bpp = 1;
  737.             if (param == null) {
  738.                 param = createGrayParam(redPalette,
  739.                                         greenPalette,
  740.                                         bluePalette,
  741.                                         alphaPalette);
  742.             }
  743.             // If param is still null, it can't be expressed as gray
  744.             if (param == null) {
  745.                 param = new PNGEncodeParam.Palette();
  746.             }
  747.             if (param instanceof PNGEncodeParam.Palette) {
  748.                 // If palette not set in param, create one from the ColorModel.
  749.                 PNGEncodeParam.Palette parami = (PNGEncodeParam.Palette)param;
  750.                 if (parami.isPaletteSet()) {
  751.                     int[] palette = parami.getPalette();
  752.                     size = palette.length/3;
  753.                     
  754.                     int index = 0;
  755.                     for (int i = 0; i < size; i++) {
  756.                         redPalette[i] = (byte)palette[index++];
  757.                         greenPalette[i] = (byte)palette[index++];
  758.                         bluePalette[i] = (byte)palette[index++];
  759.                         alphaPalette[i] = (byte)255;
  760.                     }
  761.                 }
  762.                 this.colorType = PNG_COLOR_PALETTE;
  763.             } else if (param instanceof PNGEncodeParam.Gray) {
  764.                 redPalette = greenPalette = bluePalette = alphaPalette = null;
  765.                 this.colorType = PNG_COLOR_GRAY;
  766.             } else {
  767.                 throw new RuntimeException();
  768.             }
  769.         } else if (numBands == 1) {
  770.             if (param == null) {
  771.                 param = new PNGEncodeParam.Gray();
  772.             }
  773.             this.colorType = PNG_COLOR_GRAY;
  774.         } else if (numBands == 2) {
  775.             if (param == null) {
  776.                 param = new PNGEncodeParam.Gray();
  777.             }
  778.             
  779.             if (param.isTransparencySet()) {
  780.                 skipAlpha = true;
  781.                 numBands = 1;
  782.                 if ((sampleSize[0] == 8) && (bitDepth < 8)) {
  783.                     compressGray = true;
  784.                 }
  785.                 bpp = (bitDepth == 16) ? 2 : 1;
  786.                 this.colorType = PNG_COLOR_GRAY;
  787.             } else {
  788.                 if (this.bitDepth < 8) {
  789.                     this.bitDepth = 8;
  790.                 }
  791.                 this.colorType = PNG_COLOR_GRAY_ALPHA;
  792.             }
  793.         } else if (numBands == 3) {
  794.             if (param == null) {
  795.                 param = new PNGEncodeParam.RGB();
  796.             }
  797.             this.colorType = PNG_COLOR_RGB;
  798.         } else if (numBands == 4) {
  799.             if (param == null) {
  800.                 param = new PNGEncodeParam.RGB();
  801.             }
  802.             if (param.isTransparencySet()) {
  803.                 skipAlpha = true;
  804.                 numBands = 3;
  805.                 bpp = (bitDepth == 16) ? 6 : 3;
  806.                 this.colorType = PNG_COLOR_RGB;
  807.             } else {
  808.                 this.colorType = PNG_COLOR_RGB_ALPHA;
  809.             }
  810.         }
  811.         interlace = param.getInterlacing();
  812.         writeMagic();
  813.         writeIHDR();
  814.         writeCHRM();
  815.         writeGAMA();
  816.         writeICCP();
  817.         writeSBIT();
  818.         writeSRGB();
  819.         
  820.         writePLTE();
  821.         writeHIST();
  822.         writeTRNS();
  823.         writeBKGD();
  824.         writePHYS();
  825.         writeSPLT();
  826.         writeTIME();
  827.         writeTEXT();
  828.         writeZTXT();
  829.         writePrivateChunks();
  830.         writeIDAT();
  831.         writeIEND();
  832.         dataOutput.flush();
  833.         dataOutput.close();
  834.     }
  835. //     public static void main(String[] args) {
  836. //         try {
  837. //             SeekableStream stream = new FileSeekableStream(args[0]);
  838. //             String[] names = ImageCodec.getDecoderNames(stream);
  839. //             ImageDecoder dec =
  840. //                 ImageCodec.createImageDecoder(names[0], stream, null);
  841. //             RenderedImage im = dec.decodeAsRenderedImage();
  842. //             OutputStream output = new FileOutputStream(args[1]);
  843. //             PNGEncodeParam param = null;
  844. //             Object o = im.getProperty("encode_param");
  845. //             if ((o != null) && (o instanceof PNGEncodeParam)) {
  846. //                 param = (PNGEncodeParam)o;
  847. //             } else {
  848. //                 param = PNGEncodeParam.getDefaultEncodeParam(im);
  849. //             }
  850. //             if (param instanceof PNGEncodeParam.RGB) {
  851. //                 int[] rgb = { 50, 100, 150 };
  852. //                 ((PNGEncodeParam.RGB)param).setBackgroundRGB(rgb);
  853. //             }
  854. //             param.setChromaticity(0.32270F, 0.319F,
  855. //                                   0.65F, 0.32F,
  856. //                                   0.31F, 0.58F,
  857. //                                   0.16F, 0.04F);
  858. //             param.setGamma(3.5F);
  859. //             int[] sbits = { 8, 8, 8 };
  860. //             param.setSignificantBits(sbits);
  861. //             param.setSRGBIntent(0);
  862. //             String[] text = new String[4];
  863. //             text[0] = "Title";
  864. //             text[1] = "PNG Test Image";
  865. //             text[2] = "Author";
  866. //             text[3] = "Daniel Rice";
  867. //             param.setText(text);
  868. //             String[] ztext = new String[2];
  869. //             ztext[0] = "Description";
  870. //             ztext[1] = "A really incredibly long-winded description of extremely little if any substance whatsoever.";
  871. //             param.setCompressedText(ztext);
  872. //             ImageEncoder enc = new PNGImageEncoder(output, param);
  873. //             enc.encode(im);
  874. //         } catch (Exception e) {
  875. //             e.printStackTrace();
  876. //             System.exit(1);
  877. //         }
  878. //     }
  879. }