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

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.ColorModel;
  34. import java.awt.image.IndexColorModel;
  35. import java.awt.image.RenderedImage;
  36. import java.awt.image.SampleModel;
  37. import java.util.Date;
  38. import java.util.Vector;
  39. /**
  40.  * An instance of <code>ImageEncodeParam</code> for encoding images in
  41.  * the PNG format.
  42.  *
  43.  * <p><b> This class is not a committed part of the JAI API.  It may
  44.  * be removed or changed in future releases of JAI.</b>
  45.  */
  46. public abstract class PNGEncodeParam implements ImageEncodeParam {
  47.     /** Constant for use with the sRGB chunk. */
  48.     public static final int INTENT_PERCEPTUAL = 0;
  49.     /** Constant for use with the sRGB chunk. */
  50.     public static final int INTENT_RELATIVE = 1;
  51.     /** Constant for use with the sRGB chunk. */
  52.     public static final int INTENT_SATURATION = 2;
  53.     /** Constant for use with the sRGB chunk. */
  54.     public static final int INTENT_ABSOLUTE = 3;
  55.     /** Constant for use in filtering. */
  56.     public static final int PNG_FILTER_NONE = 0;
  57.     /** Constant for use in filtering. */
  58.     public static final int PNG_FILTER_SUB = 1;
  59.     /** Constant for use in filtering. */
  60.     public static final int PNG_FILTER_UP = 2;
  61.     /** Constant for use in filtering. */
  62.     public static final int PNG_FILTER_AVERAGE = 3;
  63.     /** Constant for use in filtering. */
  64.     public static final int PNG_FILTER_PAETH = 4;
  65.     /**
  66.      * Returns an instance of <code>PNGEncodeParam.Palette</code>,
  67.      * <code>PNGEncodeParam.Gray</code>, or
  68.      * <code>PNGEncodeParam.RGB</code> appropriate for encoding
  69.      * the given image.
  70.      *
  71.      * <p> If the image has an <code>IndexColorModel</code>, an
  72.      * instance of <code>PNGEncodeParam.Palette</code> is returned.
  73.      * Otherwise, if the image has 1 or 2 bands an instance of
  74.      * <code>PNGEncodeParam.Gray</code> is returned.  In all other
  75.      * cases an instance of <code>PNGEncodeParam.RGB</code> is
  76.      * returned.
  77.      *
  78.      * <p> Note that this method does not provide any guarantee that
  79.      * the given image will be successfully encoded by the PNG
  80.      * encoder, as it only performs a very superficial analysis of
  81.      * the image structure. 
  82.      */
  83.     public static PNGEncodeParam getDefaultEncodeParam(RenderedImage im) {
  84.         ColorModel colorModel = im.getColorModel();
  85.         if (colorModel instanceof IndexColorModel) {
  86.             return new PNGEncodeParam.Palette();
  87.         }
  88.         SampleModel sampleModel = im.getSampleModel();
  89.         int numBands = sampleModel.getNumBands();
  90.         if (numBands == 1 || numBands == 2) {
  91.             return new PNGEncodeParam.Gray();
  92.         } else {
  93.             return new PNGEncodeParam.RGB();
  94.         }
  95.     }
  96.     public static class Palette extends PNGEncodeParam {
  97.         /** Constructs an instance of <code>PNGEncodeParam.Palette</code>. */
  98.         public Palette() {}
  99.         // bKGD chunk
  100.         
  101.         private boolean backgroundSet = false;
  102.         
  103.         /**
  104.          * Suppresses the 'bKGD' chunk from being output.
  105.          */
  106.         public void unsetBackground() {
  107.             backgroundSet = false;
  108.         }
  109.         
  110.         /**
  111.          * Returns true if a 'bKGD' chunk will be output.
  112.          */
  113.         public boolean isBackgroundSet() {
  114.             return backgroundSet;
  115.         }
  116.         /**
  117.          * Sets the desired bit depth for a palette image.  The bit
  118.          * depth must be one of 1, 2, 4, or 8, or else an
  119.          * <code>IllegalArgumentException</code> will be thrown.
  120.          */
  121.         public void setBitDepth(int bitDepth) {
  122.             if (bitDepth != 1 && bitDepth != 2 && bitDepth != 4 &&
  123.                 bitDepth != 8) {
  124.                 throw new IllegalArgumentException(JaiI18N.getString("PNGEncodeParam2"));
  125.             }
  126.             this.bitDepth = bitDepth;
  127.             bitDepthSet = true;
  128.         }
  129.     
  130.         // PLTE chunk
  131.         private int[] palette = null;
  132.         private boolean paletteSet = false;
  133.         /**
  134.          * Sets the RGB palette of the image to be encoded.
  135.          * The <code>rgb</code> parameter contains alternating
  136.          * R, G, B values for each color index used in the image.
  137.          * The number of elements must be a multiple of 3 between
  138.          * 3 and 3*256.
  139.          *
  140.          * <p> The 'PLTE' chunk will encode this information.
  141.          *
  142.          * @param rgb An array of <code>int</code>s.
  143.          */
  144.         public void setPalette(int[] rgb) {
  145.             if (rgb.length < 1*3 || rgb.length > 256*3) {
  146. throw new 
  147.   IllegalArgumentException(JaiI18N.getString("PNGEncodeParam0"));
  148.             }
  149.             if ((rgb.length % 3) != 0) {
  150.                 throw new 
  151.             IllegalArgumentException(JaiI18N.getString("PNGEncodeParam1"));
  152.             }
  153.             
  154.             palette = (int[])(rgb.clone());
  155.             paletteSet = true;
  156.         }
  157.         
  158.         /**
  159.          * Returns the current RGB palette.
  160.          *
  161.          * <p> If the palette has not previously been set, or has been
  162.          * unset, an <code>IllegalStateException</code> will be thrown.
  163.          *
  164.          * @throws IllegalStateException if the palette is not set.
  165.          *
  166.          * @return An array of <code>int</code>s.
  167.          */
  168.         public int[] getPalette() {
  169.             if (!paletteSet) {
  170.                 throw new IllegalStateException(JaiI18N.getString("PNGEncodeParam3"));
  171.             }
  172.             return (int[])(palette.clone());
  173.         }
  174.         /**
  175.          * Suppresses the 'PLTE' chunk from being output.
  176.          */
  177.         public void unsetPalette() {
  178.             palette = null;
  179.             paletteSet = false;
  180.         }
  181.         
  182.         /**
  183.          * Returns true if a 'PLTE' chunk will be output.
  184.          */
  185.         public boolean isPaletteSet() {
  186.             return paletteSet;
  187.         }
  188.         // bKGD chunk
  189.         
  190.         private int backgroundPaletteIndex;
  191.         
  192.         /**
  193.          * Sets the palette index of the suggested background color.
  194.          *
  195.          * <p> The 'bKGD' chunk will encode this information.
  196.          */
  197.         public void setBackgroundPaletteIndex(int index) {
  198.             backgroundPaletteIndex = index;
  199.             backgroundSet = true;
  200.         }
  201.         
  202.         /**
  203.          * Returns the palette index of the suggested background color.
  204.          *
  205.          * <p> If the background palette index has not previously been
  206.          * set, or has been unset, an
  207.          * <code>IllegalStateException</code> will be thrown.
  208.          *
  209.          * @throws IllegalStateException if the palette index is not set.
  210.          */
  211.         public int getBackgroundPaletteIndex() {
  212.             if (!backgroundSet) {
  213.                 throw new IllegalStateException(JaiI18N.getString("PNGEncodeParam4"));
  214.             }
  215.             return backgroundPaletteIndex;
  216.         }
  217.         // tRNS chunk
  218.         private int[] transparency;
  219.         /**
  220.          * Sets the alpha values associated with each palette entry.
  221.          * The <code>alpha</code> parameter should have as many entries
  222.          * as there are RGB triples in the palette.
  223.          *
  224.          * <p> The 'tRNS' chunk will encode this information.
  225.          */
  226.         public void setPaletteTransparency(byte[] alpha) {
  227.             transparency = new int[alpha.length];
  228.             for (int i = 0; i < alpha.length; i++) {
  229.                 transparency[i] = alpha[i] & 0xff;
  230.             }
  231.             transparencySet = true;
  232.         }
  233.         
  234.         /**
  235.          * Returns the alpha values associated with each palette entry.
  236.          *
  237.          * <p> If the palette transparency has not previously been
  238.          * set, or has been unset, an
  239.          * <code>IllegalStateException</code> will be thrown.
  240.          *
  241.          * @throws IllegalStateException if the palette transparency is
  242.          *        not set.
  243.          */
  244.         public byte[] getPaletteTransparency() {
  245.             if (!transparencySet) {
  246.                 throw new IllegalStateException(JaiI18N.getString("PNGEncodeParam5"));
  247.             }
  248.             byte[] alpha = new byte[transparency.length];
  249.             for (int i = 0; i < alpha.length; i++) {
  250.                 alpha[i] = (byte)transparency[i];
  251.             }
  252.             return alpha;
  253.         }
  254.     }
  255.     public static class Gray extends PNGEncodeParam {
  256.         /** Constructs an instance of <code>PNGEncodeParam.Gray</code>. */
  257.         public Gray() {}
  258.         // bKGD chunk
  259.         
  260.         private boolean backgroundSet = false;
  261.         
  262.         /**
  263.          * Suppresses the 'bKGD' chunk from being output.
  264.          */
  265.         public void unsetBackground() {
  266.             backgroundSet = false;
  267.         }
  268.         
  269.         /**
  270.          * Returns true if a 'bKGD' chunk will be output.
  271.          */
  272.         public boolean isBackgroundSet() {
  273.             return backgroundSet;
  274.         }
  275.         /**
  276.          * Sets the desired bit depth for a grayscale image.  The bit
  277.          * depth must be one of 1, 2, 4, 8, or 16.
  278.          *
  279.          * <p> When encoding a source image of a greater bit depth,
  280.          * pixel values will be clamped to the smaller range after
  281.          * shifting by the value given by <code>getBitShift()</code>.
  282.          * When encoding a source image of a smaller bit depth, pixel
  283.          * values will be shifted and left-filled with zeroes.
  284.          */
  285.         public void setBitDepth(int bitDepth) {
  286.             if (bitDepth != 1 && bitDepth != 2 && bitDepth != 4 &&
  287.                 bitDepth != 8 && bitDepth != 16) {
  288.                 throw new IllegalArgumentException();
  289.             }
  290.             this.bitDepth = bitDepth;
  291.             bitDepthSet = true;
  292.         }
  293.     
  294.         // bKGD chunk
  295.         
  296.         private int backgroundPaletteGray;
  297.         /**
  298.          * Sets the suggested gray level of the background.
  299.          *
  300.          * <p> The 'bKGD' chunk will encode this information.
  301.          */
  302.         public void setBackgroundGray(int gray) {
  303.             backgroundPaletteGray = gray;
  304.             backgroundSet = true;
  305.         }
  306.         
  307.         /**
  308.          * Returns the suggested gray level of the background.
  309.          *
  310.          * <p> If the background gray level has not previously been
  311.          * set, or has been unset, an
  312.          * <code>IllegalStateException</code> will be thrown.
  313.          *
  314.          * @throws IllegalStateException if the background gray level
  315.          *        is not set.
  316.          */
  317.         public int getBackgroundGray() {
  318.             if (!backgroundSet) {
  319.                 throw new IllegalStateException(JaiI18N.getString("PNGEncodeParam6"));
  320.             }
  321.             return backgroundPaletteGray;
  322.         }
  323.         // tRNS chunk
  324.         private int[] transparency;
  325.         /**
  326.          * Sets the gray value to be used to denote transparency.
  327.          *
  328.          * <p> Setting this attribute will cause the alpha channel
  329.          * of the input image to be ignored.
  330.          *
  331.          * <p> The 'tRNS' chunk will encode this information.
  332.          */
  333.         public void setTransparentGray(int transparentGray) {
  334.             transparency = new int[1];
  335.             transparency[0] = transparentGray;
  336.             transparencySet = true;
  337.         }
  338.         
  339.         /**
  340.          * Returns the gray value to be used to denote transparency.
  341.          *
  342.          * <p> If the transparent gray value has not previously been
  343.          * set, or has been unset, an
  344.          * <code>IllegalStateException</code> will be thrown.
  345.          *
  346.          * @throws IllegalStateException if the transparent gray value
  347.          *        is not set.
  348.          */
  349.         public int getTransparentGray() {
  350.             if (!transparencySet) {
  351.                 throw new IllegalStateException(JaiI18N.getString("PNGEncodeParam7"));
  352.             }
  353.             int gray = transparency[0];
  354.             return gray;
  355.         }
  356.         private int bitShift;
  357.         private boolean bitShiftSet = false;
  358.         /**
  359.          * Sets the desired bit shift for a grayscale image.
  360.          * Pixels in the source image will be shifted right by
  361.          * the given amount prior to being clamped to the maximum
  362.          * value given by the encoded image's bit depth.
  363.          */
  364.         public void setBitShift(int bitShift) {
  365.             if (bitShift < 0) {
  366.                 throw new RuntimeException();
  367.             }
  368.             this.bitShift = bitShift;
  369.             bitShiftSet = true;
  370.         }
  371.         /** 
  372.          * Returns the desired bit shift for a grayscale image.
  373.          *
  374.          * <p> If the bit shift has not previously been set, or has been
  375.          * unset, an <code>IllegalStateException</code> will be thrown.
  376.          *
  377.          * @throws IllegalStateException if the bit shift is not set.
  378.          */
  379.         public int getBitShift() {
  380.             if (!bitShiftSet) {
  381.                 throw new IllegalStateException(JaiI18N.getString("PNGEncodeParam8"));
  382.             }
  383.             return bitShift;
  384.         }
  385.         /**
  386.          * Suppresses the setting of the bit shift of a grayscale image.
  387.          * Pixels in the source image will not be shifted prior to encoding.
  388.          */
  389.         public void unsetBitShift() {
  390.             bitShiftSet = false;
  391.         }
  392.         /**
  393.          * Returns true if the bit shift has been set.
  394.          */
  395.         public boolean isBitShiftSet() {
  396.             return bitShiftSet;
  397.         }
  398.         /**
  399.          * Returns true if the bit depth has been set.
  400.          */
  401.         public boolean isBitDepthSet() {
  402.             return bitDepthSet;
  403.         }
  404.     }
  405.     public static class RGB extends PNGEncodeParam {
  406.         /** Constructs an instance of <code>PNGEncodeParam.RGB</code>. */
  407.         public RGB() {}
  408.         // bKGD chunk
  409.         
  410.         private boolean backgroundSet = false;
  411.         
  412.         /**
  413.          * Suppresses the 'bKGD' chunk from being output.
  414.          */
  415.         public void unsetBackground() {
  416.             backgroundSet = false;
  417.         }
  418.         
  419.         /**
  420.          * Returns true if a 'bKGD' chunk will be output.
  421.          */
  422.         public boolean isBackgroundSet() {
  423.             return backgroundSet;
  424.         }
  425.         /**
  426.          * Sets the desired bit depth for an RGB image.  The bit
  427.          * depth must be 8 or 16.
  428.          */
  429.         public void setBitDepth(int bitDepth) {
  430.             if (bitDepth != 8 && bitDepth != 16) {
  431.                 throw new RuntimeException();
  432.             }
  433.             this.bitDepth = bitDepth;
  434.             bitDepthSet = true;
  435.         }
  436.     
  437.         // bKGD chunk
  438.         private int[] backgroundRGB;
  439.         /**
  440.          * Sets the RGB value of the suggested background color.
  441.          * The <code>rgb</code> parameter should have 3 entries. 
  442.          *
  443.          * <p> The 'bKGD' chunk will encode this information.
  444.          */
  445.         public void setBackgroundRGB(int[] rgb) {
  446.             if (rgb.length != 3) {
  447.                 throw new RuntimeException();
  448.             }
  449.             backgroundRGB = rgb;
  450.             backgroundSet = true;
  451.         }
  452.         
  453.         /**
  454.          * Returns the RGB value of the suggested background color.
  455.          *
  456.          * <p> If the background color has not previously been set, or has been
  457.          * unset, an <code>IllegalStateException</code> will be thrown.
  458.          *
  459.          * @throws IllegalStateException if the background color is not set.
  460.          */
  461.         public int[] getBackgroundRGB() {
  462.             if (!backgroundSet) {
  463.                 throw new IllegalStateException(JaiI18N.getString("PNGEncodeParam9"));
  464.             }
  465.             return backgroundRGB;
  466.         }
  467.         // tRNS chunk
  468.         private int[] transparency;
  469.         
  470.         /**
  471.          * Sets the RGB value to be used to denote transparency.
  472.          *
  473.          * <p> Setting this attribute will cause the alpha channel
  474.          * of the input image to be ignored.
  475.          *
  476.          * <p> The 'tRNS' chunk will encode this information.
  477.          */
  478.         public void setTransparentRGB(int[] transparentRGB) {
  479.             transparency = (int[])(transparentRGB.clone());
  480.             transparencySet = true;
  481.         }
  482.         
  483.         /**
  484.          * Returns the RGB value to be used to denote transparency.
  485.          *
  486.          * <p> If the transparent color has not previously been set,
  487.          * or has been unset, an <code>IllegalStateException</code>
  488.          * will be thrown.
  489.          *
  490.          * @throws IllegalStateException if the transparent color is not set.
  491.          */
  492.         public int[] getTransparentRGB() {
  493.             if (!transparencySet) {
  494.                 throw new IllegalStateException(JaiI18N.getString("PNGEncodeParam10"));
  495.             }
  496.             return (int[])(transparency.clone());
  497.         }
  498.     }
  499.     protected int bitDepth;
  500.     protected boolean bitDepthSet = false;
  501.     /**
  502.      * Sets the desired bit depth of an image.
  503.      */
  504.     public abstract void setBitDepth(int bitDepth);
  505.     
  506.     /** 
  507.      * Returns the desired bit depth for a grayscale image.
  508.      *
  509.      * <p> If the bit depth has not previously been set, or has been
  510.      * unset, an <code>IllegalStateException</code> will be thrown.
  511.      *
  512.      * @throws IllegalStateException if the bit depth is not set.
  513.      */
  514.     public int getBitDepth() {
  515.         if (!bitDepthSet) {
  516.             throw new IllegalStateException(JaiI18N.getString("PNGEncodeParam11"));
  517.         }
  518.         return bitDepth;
  519.     }
  520.     /**
  521.      * Suppresses the setting of the bit depth of a grayscale image.
  522.      * The depth of the encoded image will be inferred from the source
  523.      * image bit depth, rounded up to the next power of 2 between 1 
  524.      * and 16.
  525.      */
  526.     public void unsetBitDepth() {
  527.         bitDepthSet = false;
  528.     }
  529.     private boolean useInterlacing = false;
  530.     /**
  531.      * Turns Adam7 interlacing on or off.
  532.      */
  533.     public void setInterlacing(boolean useInterlacing) {
  534.         this.useInterlacing = useInterlacing;
  535.     }
  536.     /**
  537.      * Returns <code>true</code> if Adam7 interlacing will be used.
  538.      */
  539.     public boolean getInterlacing() {
  540.         return useInterlacing;
  541.     }
  542.     
  543.     // bKGD chunk - delegate to subclasses
  544.     // In JAI 1.0, 'backgroundSet' was private.  The JDK 1.2 compiler
  545.     // was lenient and incorrectly allowed this variable to be
  546.     // accessed from the subclasses.  The JDK 1.3 compiler correctly
  547.     // flags this as a use of a non-static variable in a static
  548.     // context.  Changing 'backgroundSet' to protected would have
  549.     // solved the problem, but would have introduced a visible API
  550.     // change.  Thus we are forced to adopt the solution of placing a
  551.     // separate private variable in each subclass and providing
  552.     // separate implementations of 'unsetBackground' and
  553.     // 'isBackgroundSet' in each concrete subclass.
  554.     /**
  555.      * Suppresses the 'bKGD' chunk from being output.
  556.      * For API compatibility with JAI 1.0, the superclass
  557.      * defines this method to throw a <code>RuntimeException</code>;
  558.      * accordingly, subclasses must provide their own implementations.
  559.      */
  560.     public void unsetBackground() {
  561.         throw new RuntimeException(JaiI18N.getString("PNGEncodeParam23"));
  562.     }
  563.     /**
  564.      * Returns true if a 'bKGD' chunk will be output.
  565.      * For API compatibility with JAI 1.0, the superclass
  566.      * defines this method to throw a <code>RuntimeException</code>;
  567.      * accordingly, subclasses must provide their own implementations.
  568.      */
  569.     public boolean isBackgroundSet() {
  570.         throw new RuntimeException(JaiI18N.getString("PNGEncodeParam24"));
  571.     }
  572.     // cHRM chunk
  573.     private float[] chromaticity = null;
  574.     private boolean chromaticitySet = false;
  575.     /**
  576.      * Sets the white point and primary chromaticities in CIE (x, y)
  577.      * space.
  578.      *
  579.      * <p> The <code>chromaticity</code> parameter should be a
  580.      * <code>float</code> array of length 8 containing the white point
  581.      * X and Y, red X and Y, green X and Y, and blue X and Y values in
  582.      * order.
  583.      *
  584.      * <p> The 'cHRM' chunk will encode this information.
  585.      */
  586.     public void setChromaticity(float[] chromaticity) {
  587.         if (chromaticity.length != 8) {
  588.             throw new IllegalArgumentException();
  589.         }
  590.         this.chromaticity = (float[])(chromaticity.clone());
  591.         chromaticitySet = true;
  592.     }
  593.     /**
  594.      * A convenience method that calls the array version.
  595.      */
  596.     public void setChromaticity(float whitePointX, float whitePointY,
  597.                                 float redX, float redY,
  598.                                 float greenX, float greenY,
  599.                                 float blueX, float blueY) {
  600.         float[] chroma = new float[8];
  601.         chroma[0] = whitePointX;
  602.         chroma[1] = whitePointY;
  603.         chroma[2] = redX;
  604.         chroma[3] = redY;
  605.         chroma[4] = greenX;
  606.         chroma[5] = greenY;
  607.         chroma[6] = blueX;
  608.         chroma[7] = blueY;
  609.         setChromaticity(chroma);
  610.     }
  611.     /**
  612.      * Returns the white point and primary chromaticities in
  613.      * CIE (x, y) space.
  614.      *
  615.      * <p> See the documentation for the <code>setChromaticity</code>
  616.      * method for the format of the returned data.
  617.      *
  618.      * <p> If the chromaticity has not previously been set, or has been
  619.      * unset, an <code>IllegalStateException</code> will be thrown.
  620.      *
  621.      * @throws IllegalStateException if the chromaticity is not set.
  622.      */
  623.     public float[] getChromaticity() {
  624.         if (!chromaticitySet) {
  625.             throw new IllegalStateException(JaiI18N.getString("PNGEncodeParam12"));
  626.         }
  627.         return (float[])(chromaticity.clone());
  628.     }
  629.     /**
  630.      * Suppresses the 'cHRM' chunk from being output.
  631.      */
  632.     public void unsetChromaticity() {
  633.         chromaticity = null;
  634.         chromaticitySet = false;
  635.     }
  636.     /**
  637.      * Returns true if a 'cHRM' chunk will be output.
  638.      */
  639.     public boolean isChromaticitySet() {
  640.         return chromaticitySet;
  641.     }
  642.     // gAMA chunk
  643.     private float gamma;
  644.     private boolean gammaSet = false;
  645.     /**
  646.      * Sets the file gamma value for the image.
  647.      *
  648.      * <p> The 'gAMA' chunk will encode this information.
  649.      */
  650.     public void setGamma(float gamma) {
  651.         this.gamma = gamma;
  652.         gammaSet = true;
  653.     }
  654.     /**
  655.      * Returns the file gamma value for the image.
  656.      *
  657.      * <p> If the file gamma has not previously been set, or has been
  658.      * unset, an <code>IllegalStateException</code> will be thrown.
  659.      *
  660.      * @throws IllegalStateException if the gamma is not set.
  661.      */
  662.     public float getGamma() {
  663.         if (!gammaSet) {
  664.             throw new IllegalStateException(JaiI18N.getString("PNGEncodeParam13"));
  665.         }
  666.         return gamma;
  667.     }
  668.     /**
  669.      * Suppresses the 'gAMA' chunk from being output.
  670.      */
  671.     public void unsetGamma() {
  672.         gammaSet = false;
  673.     }
  674.     /**
  675.      * Returns true if a 'gAMA' chunk will be output.
  676.      */
  677.     public boolean isGammaSet() {
  678.         return gammaSet;
  679.     }
  680.     // hIST chunk
  681.     private int[] paletteHistogram = null;
  682.     private boolean paletteHistogramSet = false;
  683.     /**
  684.      * Sets the palette histogram to be stored with this image.
  685.      * The histogram consists of an array of integers, one per
  686.      * palette entry.
  687.      *
  688.      * <p> The 'hIST' chunk will encode this information.
  689.      */
  690.     public void setPaletteHistogram(int[] paletteHistogram) {
  691.         this.paletteHistogram = (int[])(paletteHistogram.clone());
  692.         paletteHistogramSet = true;
  693.     }
  694.     /**
  695.      * Returns the palette histogram to be stored with this image.
  696.      *
  697.      * <p> If the histogram has not previously been set, or has been
  698.      * unset, an <code>IllegalStateException</code> will be thrown.
  699.      *
  700.      * @throws IllegalStateException if the histogram is not set.
  701.      */
  702.     public int[] getPaletteHistogram() {
  703.         if (!paletteHistogramSet) {
  704.             throw new IllegalStateException(JaiI18N.getString("PNGEncodeParam14"));
  705.         }
  706.         return paletteHistogram;
  707.     }
  708.     /**
  709.      * Suppresses the 'hIST' chunk from being output.
  710.      */
  711.     public void unsetPaletteHistogram() {
  712.         paletteHistogram = null;
  713.         paletteHistogramSet = false;
  714.     }
  715.     /**
  716.      * Returns true if a 'hIST' chunk will be output.
  717.      */
  718.     public boolean isPaletteHistogramSet() {
  719.         return paletteHistogramSet;
  720.     }
  721.     // iCCP chunk
  722.     private byte[] ICCProfileData = null;
  723.     private boolean ICCProfileDataSet = false;
  724.     /**
  725.      * Sets the ICC profile data to be stored with this image.
  726.      * The profile is represented in raw binary form.
  727.      *
  728.      * <p> The 'iCCP' chunk will encode this information.
  729.      */
  730.     public void setICCProfileData(byte[] ICCProfileData) {
  731.         this.ICCProfileData = (byte[])(ICCProfileData.clone());
  732.         ICCProfileDataSet = true;
  733.     }
  734.     /**
  735.      * Returns the ICC profile data to be stored with this image.
  736.      *
  737.      * <p> If the ICC profile has not previously been set, or has been
  738.      * unset, an <code>IllegalStateException</code> will be thrown.
  739.      *
  740.      * @throws IllegalStateException if the ICC profile is not set.
  741.      */
  742.     public byte[] getICCProfileData() {
  743.         if (!ICCProfileDataSet) {
  744.             throw new IllegalStateException(JaiI18N.getString("PNGEncodeParam15"));
  745.         }
  746.         return (byte[])(ICCProfileData.clone());
  747.     }
  748.     /**
  749.      * Suppresses the 'iCCP' chunk from being output.
  750.      */
  751.     public void unsetICCProfileData() {
  752.         ICCProfileData = null;
  753.         ICCProfileDataSet = false;
  754.     }
  755.     /**
  756.      * Returns true if a 'iCCP' chunk will be output.
  757.      */
  758.     public boolean isICCProfileDataSet() {
  759.         return ICCProfileDataSet;
  760.     }
  761.     // pHYS chunk
  762.     private int[] physicalDimension = null;
  763.     private boolean physicalDimensionSet = false;
  764.     /**
  765.      * Sets the physical dimension information to be stored with this
  766.      * image.  The physicalDimension parameter should be a 3-entry
  767.      * array containing the number of pixels per unit in the X
  768.      * direction, the number of pixels per unit in the Y direction,
  769.      * and the unit specifier (0 = unknown, 1 = meters).
  770.      *
  771.      * <p> The 'pHYS' chunk will encode this information.
  772.      */
  773.     public void setPhysicalDimension(int[] physicalDimension) {
  774.         this.physicalDimension = (int[])(physicalDimension.clone());
  775.         physicalDimensionSet = true;
  776.     }
  777.     /**
  778.      * A convenience method that calls the array version.
  779.      */
  780.     public void setPhysicalDimension(int xPixelsPerUnit,
  781.                                      int yPixelsPerUnit,
  782.                                      int unitSpecifier) {
  783.         int[] pd = new int[3];
  784.         pd[0] = xPixelsPerUnit;
  785.         pd[1] = yPixelsPerUnit;
  786.         pd[2] = unitSpecifier;
  787.         
  788.         setPhysicalDimension(pd);
  789.     }
  790.     /**
  791.      * Returns the physical dimension information to be stored
  792.      * with this image.
  793.      *
  794.      * <p> If the physical dimension information has not previously
  795.      * been set, or has been unset, an
  796.      * <code>IllegalStateException</code> will be thrown.
  797.      *
  798.      * @throws IllegalStateException if the physical dimension information
  799.      *        is not set.
  800.      */
  801.     public int[] getPhysicalDimension() {
  802.         if (!physicalDimensionSet) {
  803.             throw new IllegalStateException(JaiI18N.getString("PNGEncodeParam16"));
  804.         }
  805.         return (int[])(physicalDimension.clone());        
  806.     }
  807.     /**
  808.      * Suppresses the 'pHYS' chunk from being output.
  809.      */
  810.     public void unsetPhysicalDimension() {
  811.         physicalDimension = null;
  812.         physicalDimensionSet = false;
  813.     }
  814.     /**
  815.      * Returns true if a 'pHYS' chunk will be output.
  816.      */
  817.     public boolean isPhysicalDimensionSet() {
  818.         return physicalDimensionSet;
  819.     }
  820.     // sPLT chunk
  821.     private PNGSuggestedPaletteEntry[] suggestedPalette = null;
  822.     private boolean suggestedPaletteSet = false;
  823.     /**
  824.      * Sets the suggested palette information to be stored with this
  825.      * image.  The information is passed to this method as an array of
  826.      * <code>PNGSuggestedPaletteEntry</code> objects.
  827.      *
  828.      * <p> The 'sPLT' chunk will encode this information.
  829.      */
  830.     public void setSuggestedPalette(PNGSuggestedPaletteEntry[] palette) {
  831.         suggestedPalette = (PNGSuggestedPaletteEntry[])(palette.clone());
  832.         suggestedPaletteSet = true;
  833.     }
  834.     /**
  835.      * Returns the suggested palette information to be stored with this
  836.      * image.
  837.      *
  838.      * <p> If the suggested palette information has not previously
  839.      * been set, or has been unset, an
  840.      * <code>IllegalStateException</code> will be thrown.
  841.      *
  842.      * @throws IllegalStateException if the suggested palette
  843.      *        information is not set.
  844.      */
  845.     public PNGSuggestedPaletteEntry[] getSuggestedPalette() {
  846.         if (!suggestedPaletteSet) {
  847.             throw new IllegalStateException(JaiI18N.getString("PNGEncodeParam17"));
  848.         }
  849.         return (PNGSuggestedPaletteEntry[])(suggestedPalette.clone());
  850.     }
  851.     /**
  852.      * Suppresses the 'sPLT' chunk from being output.
  853.      */
  854.     public void unsetSuggestedPalette() {
  855.         suggestedPalette = null;
  856.         suggestedPaletteSet = false;
  857.     }
  858.     /**
  859.      * Returns true if a 'sPLT' chunk will be output.
  860.      */
  861.     public boolean isSuggestedPaletteSet() {
  862.         return suggestedPaletteSet;
  863.     }
  864.     // sBIT chunk
  865.     private int[] significantBits = null;
  866.     private boolean significantBitsSet = false;
  867.     /**
  868.      * Sets the number of significant bits for each band of the image.
  869.      * 
  870.      * <p> The number of entries in the <code>significantBits</code>
  871.      * array must be equal to the number of output bands in the image:
  872.      * 1 for a gray image, 2 for gray+alpha, 3 for index or truecolor,
  873.      * and 4 for truecolor+alpha.
  874.      *
  875.      * <p> The 'sBIT' chunk will encode this information.
  876.      */
  877.     public void setSignificantBits(int[] significantBits) {
  878.         this.significantBits = (int[])(significantBits.clone());
  879.         significantBitsSet = true;
  880.     }
  881.     /**
  882.      * Returns the number of significant bits for each band of the image.
  883.      *
  884.      * <p> If the significant bits values have not previously been
  885.      * set, or have been unset, an <code>IllegalStateException</code>
  886.      * will be thrown.
  887.      *
  888.      * @throws IllegalStateException if the significant bits values are
  889.      *        not set.
  890.      */
  891.     public int[] getSignificantBits() {
  892.         if (!significantBitsSet) {
  893.             throw new IllegalStateException(JaiI18N.getString("PNGEncodeParam18"));
  894.         }
  895.         return (int[])significantBits.clone();
  896.     }
  897.     /**
  898.      * Suppresses the 'sBIT' chunk from being output.
  899.      */
  900.     public void unsetSignificantBits() {
  901.         significantBits = null;
  902.         significantBitsSet = false;
  903.     }
  904.     /**
  905.      * Returns true if an 'sBIT' chunk will be output.
  906.      */
  907.     public boolean isSignificantBitsSet() {
  908.         return significantBitsSet;
  909.     }
  910.     // sRGB chunk
  911.     private int SRGBIntent;
  912.     private boolean SRGBIntentSet = false;
  913.     /**
  914.      * Sets the sRGB rendering intent to be stored with this image.
  915.      * The legal values are 0 = Perceptual, 1 = Relative Colorimetric,
  916.      * 2 = Saturation, and 3 = Absolute Colorimetric.  Refer to the
  917.      * PNG specification for information on these values.
  918.      *
  919.      * <p> The 'sRGB' chunk will encode this information.
  920.      */
  921.     public void setSRGBIntent(int SRGBIntent) {
  922.         this.SRGBIntent = SRGBIntent;
  923.         SRGBIntentSet = true;
  924.     }
  925.     /**
  926.      * Returns the sRGB rendering intent to be stored with this image.
  927.      *
  928.      * <p> If the sRGB intent has not previously been set, or has been
  929.      * unset, an <code>IllegalStateException</code> will be thrown.
  930.      *
  931.      * @throws IllegalStateException if the sRGB intent is not set.
  932.      */
  933.     public int getSRGBIntent() {
  934.         if (!SRGBIntentSet) {
  935.             throw new IllegalStateException(JaiI18N.getString("PNGEncodeParam19"));
  936.         }
  937.         return SRGBIntent;
  938.     }
  939.     /**
  940.      * Suppresses the 'sRGB' chunk from being output.
  941.      */
  942.     public void unsetSRGBIntent() {
  943.         SRGBIntentSet = false;
  944.     }
  945.     /**
  946.      * Returns true if an 'sRGB' chunk will be output.
  947.      */
  948.     public boolean isSRGBIntentSet() {
  949.         return SRGBIntentSet;
  950.     }
  951.     // tEXt chunk
  952.     private String[] text = null;
  953.     private boolean textSet = false;
  954.     /**
  955.      * Sets the textual data to be stored in uncompressed form with this
  956.      * image.  The data is passed to this method as an array of
  957.      * <code>String</code>s.
  958.      *
  959.      * <p> The 'tEXt' chunk will encode this information.
  960.      */
  961.     public void setText(String[] text) {
  962.         this.text = text;
  963.         textSet = true;
  964.     }
  965.     /**
  966.      * Returns the text strings to be stored in uncompressed form with this
  967.      * image as an array of <code>String</code>s.
  968.      *
  969.      * <p> If the text strings have not previously been set, or have been
  970.      * unset, an <code>IllegalStateException</code> will be thrown.
  971.      *
  972.      * @throws IllegalStateException if the text strings are not set.
  973.      */
  974.     public String[] getText() {
  975.         if (!textSet) {
  976.             throw new IllegalStateException(JaiI18N.getString("PNGEncodeParam20"));
  977.         }
  978.         return text;
  979.     }
  980.     /**
  981.      * Suppresses the 'tEXt' chunk from being output.
  982.      */
  983.     public void unsetText() {
  984.         text = null;
  985.         textSet = false;
  986.     }
  987.     /**
  988.      * Returns true if a 'tEXt' chunk will be output.
  989.      */
  990.     public boolean isTextSet() {
  991.         return textSet;
  992.     }
  993.     // tIME chunk
  994.     private Date modificationTime;
  995.     private boolean modificationTimeSet = false;
  996.     /**
  997.      * Sets the modification time, as a <code>Date</code>, to be
  998.      * stored with this image.  The internal storage format will use
  999.      * UTC regardless of how the <code>modificationTime</code>
  1000.      * parameter was created.
  1001.      *
  1002.      * <p> The 'tIME' chunk will encode this information.
  1003.      */
  1004.     public void setModificationTime(Date modificationTime) {
  1005.         this.modificationTime = modificationTime;
  1006.         modificationTimeSet = true;
  1007.     }
  1008.     /**
  1009.      * Returns the modification time to be stored with this image.
  1010.      *
  1011.      * <p> If the bit depth has not previously been set, or has been
  1012.      * unset, an <code>IllegalStateException</code> will be thrown.
  1013.      *
  1014.      * @throws IllegalStateException if the bit depth is not set.
  1015.      */
  1016.     public Date getModificationTime() {
  1017.         if (!modificationTimeSet) {
  1018.             throw new IllegalStateException(JaiI18N.getString("PNGEncodeParam21"));
  1019.         }
  1020.         return modificationTime;
  1021.     }
  1022.     /**
  1023.      * Suppresses the 'tIME' chunk from being output.
  1024.      */
  1025.     public void unsetModificationTime() {
  1026.         modificationTime = null;
  1027.         modificationTimeSet = false;
  1028.     }
  1029.     /**
  1030.      * Returns true if a 'tIME' chunk will be output.
  1031.      */
  1032.     public boolean isModificationTimeSet() {
  1033.         return modificationTimeSet;
  1034.     }
  1035.     // tRNS chunk
  1036.     boolean transparencySet = false;
  1037.     /**
  1038.      * Suppresses the 'tRNS' chunk from being output.
  1039.      */
  1040.     public void unsetTransparency() {
  1041.         transparencySet = false;
  1042.     }
  1043.     /**
  1044.      * Returns true if a 'tRNS' chunk will be output.
  1045.      */
  1046.     public boolean isTransparencySet() {
  1047.         return transparencySet;
  1048.     }
  1049.     // zTXT chunk
  1050.     private String[] zText = null;
  1051.     private boolean zTextSet = false;
  1052.     /**
  1053.      * Sets the text strings to be stored in compressed form with this
  1054.      * image.  The data is passed to this method as an array of
  1055.      * <code>String</code>s.
  1056.      *
  1057.      * <p> The 'zTXt' chunk will encode this information.
  1058.      */
  1059.     public void setCompressedText(String[] text) {
  1060.         this.zText = text;
  1061.         zTextSet = true;
  1062.     }
  1063.     /**
  1064.      * Returns the text strings to be stored in compressed form with
  1065.      * this image as an array of <code>String</code>s.
  1066.      *
  1067.      * <p> If the compressed text strings have not previously been
  1068.      * set, or have been unset, an <code>IllegalStateException</code>
  1069.      * will be thrown.
  1070.      *
  1071.      * @throws IllegalStateException if the compressed text strings are
  1072.      *        not set.
  1073.      */
  1074.     public String[] getCompressedText() {
  1075.         if (!zTextSet) {
  1076.             throw new IllegalStateException(JaiI18N.getString("PNGEncodeParam22"));
  1077.         }
  1078.         return zText;
  1079.     }
  1080.     /**
  1081.      * Suppresses the 'zTXt' chunk from being output.
  1082.      */
  1083.     public void unsetCompressedText() {
  1084.         zText = null;
  1085.         zTextSet = false;
  1086.     }
  1087.     /**
  1088.      * Returns true if a 'zTXT' chunk will be output.
  1089.      */
  1090.     public boolean isCompressedTextSet() {
  1091.         return zTextSet;
  1092.     }
  1093.     // Other chunk types
  1094.     
  1095.     Vector chunkType = new Vector();
  1096.     Vector chunkData = new Vector();
  1097.     /**
  1098.      * Adds a private chunk, in binary form, to the list of chunks to
  1099.      * be stored with this image.
  1100.      *
  1101.      * @param type a 4-character String giving the chunk type name.
  1102.      * @param data an array of <code>byte</code>s containing the
  1103.      *        chunk data.
  1104.      */
  1105.     public synchronized void addPrivateChunk(String type, byte[] data) {
  1106.         chunkType.add(type);
  1107.         chunkData.add((byte[])data.clone());
  1108.     }
  1109.     /**
  1110.      * Returns the number of private chunks to be written to the
  1111.      * output file.
  1112.      */
  1113.     public synchronized int getNumPrivateChunks() {
  1114.         return chunkType.size();
  1115.     }
  1116.     /**
  1117.      * Returns the type of the private chunk at a given index, as a
  1118.      * 4-character <code>String</code>.  The index must be smaller
  1119.      * than the return value of <code>getNumPrivateChunks</code>.
  1120.      */
  1121.     public synchronized String getPrivateChunkType(int index) {
  1122.         return (String)chunkType.elementAt(index);
  1123.     }
  1124.     /**
  1125.      * Returns the data associated of the private chunk at a given
  1126.      * index, as an array of <code>byte</code>s.  The index must be
  1127.      * smaller than the return value of
  1128.      * <code>getNumPrivateChunks</code>.
  1129.      */
  1130.     public synchronized byte[] getPrivateChunkData(int index) {
  1131.         return (byte[])chunkData.elementAt(index);
  1132.     }
  1133.     /**
  1134.      * Remove all private chunks associated with this parameter instance
  1135.      * whose 'safe-to-copy' bit is not set.  This may be advisable when
  1136.      * transcoding PNG images.
  1137.      */
  1138.     public synchronized void removeUnsafeToCopyPrivateChunks() {
  1139.         Vector newChunkType = new Vector();
  1140.         Vector newChunkData = new Vector();
  1141.         int len = getNumPrivateChunks();
  1142.         for (int i = 0; i < len; i++) {
  1143.             String type = getPrivateChunkType(i);
  1144.             char lastChar = type.charAt(3);
  1145.             if (lastChar >= 'a' && lastChar <= 'z') {
  1146.                 newChunkType.add(type);
  1147.                 newChunkData.add(getPrivateChunkData(i));
  1148.             }
  1149.         }
  1150.         chunkType = newChunkType;
  1151.         chunkData = newChunkData;
  1152.     }
  1153.     /**
  1154.      * Remove all private chunks associated with this parameter instance.
  1155.      */
  1156.     public synchronized void removeAllPrivateChunks() {
  1157.         chunkType = new Vector();
  1158.         chunkData = new Vector();
  1159.     }
  1160.     /**
  1161.      * An abs() function for use by the Paeth predictor.
  1162.      */
  1163.     private static final int abs(int x) {
  1164.         return (x < 0) ? -x : x;
  1165.     }
  1166.     /**
  1167.      * The Paeth predictor routine used in PNG encoding.  This routine
  1168.      * is included as a convenience to subclasses that override the
  1169.      * <code>filterRow</code> method.
  1170.      */
  1171.     public static final int paethPredictor(int a, int b, int c) {
  1172.         int p = a + b - c;
  1173.         int pa = abs(p - a);
  1174.         int pb = abs(p - b);
  1175.         int pc = abs(p - c);
  1176.         if ((pa <= pb) && (pa <= pc)) {
  1177.             return a;
  1178.         } else if (pb <= pc) {
  1179.             return b;
  1180.         } else {
  1181.             return c;
  1182.         }
  1183.     }
  1184.     /**
  1185.      * Performs filtering on a row of an image.  This method may be
  1186.      * overridden in order to provide a custom algorithm for choosing
  1187.      * the filter type for a given row.
  1188.      *
  1189.      * <p> The method is supplied with the current and previous rows
  1190.      * of the image.  For the first row of the image, or of an
  1191.      * interlacing pass, the previous row array will be filled with
  1192.      * zeros as required by the PNG specification.
  1193.      *
  1194.      * <p> The method is also supplied with five scratch arrays.
  1195.      * These arrays may be used within the method for any purpose.
  1196.      * At method exit, the array at the index given by the return
  1197.      * value of the method should contain the filtered data.  The
  1198.      * return value will also be used as the filter type.
  1199.      *
  1200.      * <p> The default implementation of the method performs a trial
  1201.      * encoding with each of the filter types, and computes the sum of
  1202.      * absolute values of the differences between the raw bytes of the
  1203.      * current row and the predicted values.  The index of the filter
  1204.      * producing the smallest result is returned.
  1205.      *
  1206.      * <p> As an example, to perform only 'sub' filtering, this method
  1207.      * could be implemented (non-optimally) as follows:
  1208.      *
  1209.      * <pre>
  1210.      * for (int i = bytesPerPixel; i < bytesPerRow + bytesPerPixel; i++) {
  1211.      *     int curr = currRow[i] & 0xff;
  1212.      *     int left = currRow[i - bytesPerPixel] & 0xff;
  1213.      *     scratchRow[PNG_FILTER_SUB][i] = (byte)(curr - left);
  1214.      * }
  1215.      * return PNG_FILTER_SUB;
  1216.      * </pre>
  1217.      *
  1218.      * @param currRow The current row as an array of <code>byte</code>s
  1219.      *        of length at least <code>bytesPerRow + bytesPerPixel</code>.
  1220.      *        The pixel data starts at index <code>bytesPerPixel</code>;
  1221.      *        the initial <code>bytesPerPixel</code> bytes are zero.
  1222.      * @param prevRow The current row as an array of <code>byte</code>s
  1223.      *        The pixel data starts at index <code>bytesPerPixel</code>;
  1224.      *        the initial <code>bytesPerPixel</code> bytes are zero.
  1225.      * @param scratchRows An array of 5 <code>byte</code> arrays of
  1226.      *        length at least <code>bytesPerRow +
  1227.      *        bytesPerPixel</code>, useable to hold temporary results.
  1228.      *        The filtered row will be returned as one of the entries
  1229.      *        of this array.  The returned filtered data should start
  1230.      *        at index <code>bytesPerPixel</code>; The initial
  1231.      *        <code>bytesPerPixel</code> bytes are not used.
  1232.      * @param bytesPerRow The number of bytes in the image row.
  1233.      *        This value will always be greater than 0.
  1234.      * @param bytesPerPixel The number of bytes representing a single
  1235.      *        pixel, rounded up to an integer.  This is the 'bpp' parameter
  1236.      *        described in the PNG specification.
  1237.      *
  1238.      * @return The filter type to be used.  The entry of
  1239.      *         <code>scratchRows[]</code> at this index holds the
  1240.      *         filtered data.  */
  1241.     public int filterRow(byte[] currRow,
  1242.                          byte[] prevRow,
  1243.                          byte[][] scratchRows,
  1244.                          int bytesPerRow,
  1245.                          int bytesPerPixel) {
  1246.         int[] filterBadness = new int[5];
  1247.         for (int i = 0; i < 5; i++) {
  1248.             filterBadness[i] = Integer.MAX_VALUE;
  1249.         }
  1250.         
  1251.         {
  1252.             int badness = 0;
  1253.             
  1254.             for (int i = bytesPerPixel; i < bytesPerRow + bytesPerPixel; i++) {
  1255.                 int curr = currRow[i] & 0xff;
  1256.                 badness += curr;
  1257.             }
  1258.             
  1259.             filterBadness[0] = badness;
  1260.         }
  1261.         
  1262.         {
  1263.             byte[] subFilteredRow = scratchRows[1];
  1264.             int badness = 0;
  1265.             
  1266.             for (int i = bytesPerPixel; i < bytesPerRow + bytesPerPixel; i++) {
  1267.                 int curr = currRow[i] & 0xff;
  1268.                 int left = currRow[i - bytesPerPixel] & 0xff;
  1269.                 int difference = curr - left;
  1270.                 subFilteredRow[i] = (byte)difference;
  1271.                 
  1272.                 badness += abs(difference);
  1273.             }
  1274.             
  1275.             filterBadness[1] = badness;
  1276.         }
  1277.         
  1278.         {
  1279.             byte[] upFilteredRow = scratchRows[2];
  1280.             int badness = 0;
  1281.             
  1282.             for (int i = bytesPerPixel; i < bytesPerRow + bytesPerPixel; i++) {
  1283.                 int curr = currRow[i] & 0xff;
  1284.                 int up = prevRow[i] & 0xff;
  1285.                 int difference = curr - up;
  1286.                 upFilteredRow[i] = (byte)difference;
  1287.                 
  1288.                 badness += abs(difference);
  1289.             }
  1290.             
  1291.             filterBadness[2] = badness;
  1292.         }
  1293.         
  1294.         {
  1295.             byte[] averageFilteredRow = scratchRows[3];
  1296.             int badness = 0;
  1297.             for (int i = bytesPerPixel; i < bytesPerRow + bytesPerPixel; i++) {
  1298.                 int curr = currRow[i] & 0xff;
  1299.                 int left = currRow[i - bytesPerPixel] & 0xff;
  1300.                 int up = prevRow[i] & 0xff;
  1301.                 int difference = curr - (left + up)/2;;
  1302.                 averageFilteredRow[i] = (byte)difference;
  1303.                 
  1304.                 badness += abs(difference);
  1305.             }
  1306.             
  1307.             filterBadness[3] = badness;
  1308.         }
  1309.         
  1310.         {
  1311.             byte[] paethFilteredRow = scratchRows[4];
  1312.             int badness = 0;
  1313.             
  1314.             for (int i = bytesPerPixel; i < bytesPerRow + bytesPerPixel; i++) {
  1315.                 int curr = currRow[i] & 0xff;
  1316.                 int left = currRow[i - bytesPerPixel] & 0xff;
  1317.                 int up = prevRow[i] & 0xff;
  1318.                 int upleft = prevRow[i - bytesPerPixel] & 0xff;
  1319.                 int predictor = paethPredictor(left, up, upleft);
  1320.                 int difference = curr - predictor;
  1321.                 paethFilteredRow[i] = (byte)difference;
  1322.                 
  1323.                 badness += abs(difference);
  1324.             }
  1325.             
  1326.             filterBadness[4] = badness;
  1327.         }
  1328.         
  1329.         int filterType = 0;
  1330.         int minBadness = filterBadness[0];
  1331.         
  1332.         for (int i = 1; i < 5; i++) {
  1333.             if (filterBadness[i] < minBadness) {
  1334.                 minBadness = filterBadness[i];
  1335.                 filterType = i;
  1336.             }
  1337.         }
  1338.         if (filterType == 0) {
  1339.             System.arraycopy(currRow, bytesPerPixel,
  1340.                              scratchRows[0], bytesPerPixel,
  1341.                              bytesPerRow);
  1342.         }
  1343.         
  1344.         return filterType;
  1345.     }
  1346. }