package com.fr.third.JAI; /* * Copyright (c) 2001 Sun Microsystems, Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * -Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * -Redistribution in binary form must reproduct the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither the name of Sun Microsystems, Inc. or the names of contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS * LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, * INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF * OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE * POSSIBILITY OF SUCH DAMAGES. * * You acknowledge that Software is not designed,licensed or intended for use in * the design, construction, operation or maintenance of any nuclear facility. */ import java.awt.image.ColorModel; import java.awt.image.IndexColorModel; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.util.Date; import java.util.Vector; /** * An instance of ImageEncodeParam for encoding images in * the PNG format. * *

This class is not a committed part of the JAI API. It may * be removed or changed in future releases of JAI. */ public abstract class PNGEncodeParam implements ImageEncodeParam { /** Constant for use with the sRGB chunk. */ public static final int INTENT_PERCEPTUAL = 0; /** Constant for use with the sRGB chunk. */ public static final int INTENT_RELATIVE = 1; /** Constant for use with the sRGB chunk. */ public static final int INTENT_SATURATION = 2; /** Constant for use with the sRGB chunk. */ public static final int INTENT_ABSOLUTE = 3; /** Constant for use in filtering. */ public static final int PNG_FILTER_NONE = 0; /** Constant for use in filtering. */ public static final int PNG_FILTER_SUB = 1; /** Constant for use in filtering. */ public static final int PNG_FILTER_UP = 2; /** Constant for use in filtering. */ public static final int PNG_FILTER_AVERAGE = 3; /** Constant for use in filtering. */ public static final int PNG_FILTER_PAETH = 4; /** * Returns an instance of PNGEncodeParam.Palette, * PNGEncodeParam.Gray, or * PNGEncodeParam.RGB appropriate for encoding * the given image. * *

If the image has an IndexColorModel, an * instance of PNGEncodeParam.Palette is returned. * Otherwise, if the image has 1 or 2 bands an instance of * PNGEncodeParam.Gray is returned. In all other * cases an instance of PNGEncodeParam.RGB is * returned. * *

Note that this method does not provide any guarantee that * the given image will be successfully encoded by the PNG * encoder, as it only performs a very superficial analysis of * the image structure. */ public static PNGEncodeParam getDefaultEncodeParam(RenderedImage im) { ColorModel colorModel = im.getColorModel(); if (colorModel instanceof IndexColorModel) { return new PNGEncodeParam.Palette(); } SampleModel sampleModel = im.getSampleModel(); int numBands = sampleModel.getNumBands(); if (numBands == 1 || numBands == 2) { return new PNGEncodeParam.Gray(); } else { return new PNGEncodeParam.RGB(); } } public static class Palette extends PNGEncodeParam { /** Constructs an instance of PNGEncodeParam.Palette. */ public Palette() {} // bKGD chunk private boolean backgroundSet = false; /** * Suppresses the 'bKGD' chunk from being output. */ public void unsetBackground() { backgroundSet = false; } /** * Returns true if a 'bKGD' chunk will be output. */ public boolean isBackgroundSet() { return backgroundSet; } /** * Sets the desired bit depth for a palette image. The bit * depth must be one of 1, 2, 4, or 8, or else an * IllegalArgumentException will be thrown. */ public void setBitDepth(int bitDepth) { if (bitDepth != 1 && bitDepth != 2 && bitDepth != 4 && bitDepth != 8) { throw new IllegalArgumentException(JaiI18N.getString("PNGEncodeParam2")); } this.bitDepth = bitDepth; bitDepthSet = true; } // PLTE chunk private int[] palette = null; private boolean paletteSet = false; /** * Sets the RGB palette of the image to be encoded. * The rgb parameter contains alternating * R, G, B values for each color index used in the image. * The number of elements must be a multiple of 3 between * 3 and 3*256. * *

The 'PLTE' chunk will encode this information. * * @param rgb An array of ints. */ public void setPalette(int[] rgb) { if (rgb.length < 1*3 || rgb.length > 256*3) { throw new IllegalArgumentException(JaiI18N.getString("PNGEncodeParam0")); } if ((rgb.length % 3) != 0) { throw new IllegalArgumentException(JaiI18N.getString("PNGEncodeParam1")); } palette = (int[])(rgb.clone()); paletteSet = true; } /** * Returns the current RGB palette. * *

If the palette has not previously been set, or has been * unset, an IllegalStateException will be thrown. * * @throws IllegalStateException if the palette is not set. * * @return An array of ints. */ public int[] getPalette() { if (!paletteSet) { throw new IllegalStateException(JaiI18N.getString("PNGEncodeParam3")); } return (int[])(palette.clone()); } /** * Suppresses the 'PLTE' chunk from being output. */ public void unsetPalette() { palette = null; paletteSet = false; } /** * Returns true if a 'PLTE' chunk will be output. */ public boolean isPaletteSet() { return paletteSet; } // bKGD chunk private int backgroundPaletteIndex; /** * Sets the palette index of the suggested background color. * *

The 'bKGD' chunk will encode this information. */ public void setBackgroundPaletteIndex(int index) { backgroundPaletteIndex = index; backgroundSet = true; } /** * Returns the palette index of the suggested background color. * *

If the background palette index has not previously been * set, or has been unset, an * IllegalStateException will be thrown. * * @throws IllegalStateException if the palette index is not set. */ public int getBackgroundPaletteIndex() { if (!backgroundSet) { throw new IllegalStateException(JaiI18N.getString("PNGEncodeParam4")); } return backgroundPaletteIndex; } // tRNS chunk private int[] transparency; /** * Sets the alpha values associated with each palette entry. * The alpha parameter should have as many entries * as there are RGB triples in the palette. * *

The 'tRNS' chunk will encode this information. */ public void setPaletteTransparency(byte[] alpha) { transparency = new int[alpha.length]; for (int i = 0; i < alpha.length; i++) { transparency[i] = alpha[i] & 0xff; } transparencySet = true; } /** * Returns the alpha values associated with each palette entry. * *

If the palette transparency has not previously been * set, or has been unset, an * IllegalStateException will be thrown. * * @throws IllegalStateException if the palette transparency is * not set. */ public byte[] getPaletteTransparency() { if (!transparencySet) { throw new IllegalStateException(JaiI18N.getString("PNGEncodeParam5")); } byte[] alpha = new byte[transparency.length]; for (int i = 0; i < alpha.length; i++) { alpha[i] = (byte)transparency[i]; } return alpha; } } public static class Gray extends PNGEncodeParam { /** Constructs an instance of PNGEncodeParam.Gray. */ public Gray() {} // bKGD chunk private boolean backgroundSet = false; /** * Suppresses the 'bKGD' chunk from being output. */ public void unsetBackground() { backgroundSet = false; } /** * Returns true if a 'bKGD' chunk will be output. */ public boolean isBackgroundSet() { return backgroundSet; } /** * Sets the desired bit depth for a grayscale image. The bit * depth must be one of 1, 2, 4, 8, or 16. * *

When encoding a source image of a greater bit depth, * pixel values will be clamped to the smaller range after * shifting by the value given by getBitShift(). * When encoding a source image of a smaller bit depth, pixel * values will be shifted and left-filled with zeroes. */ public void setBitDepth(int bitDepth) { if (bitDepth != 1 && bitDepth != 2 && bitDepth != 4 && bitDepth != 8 && bitDepth != 16) { throw new IllegalArgumentException(); } this.bitDepth = bitDepth; bitDepthSet = true; } // bKGD chunk private int backgroundPaletteGray; /** * Sets the suggested gray level of the background. * *

The 'bKGD' chunk will encode this information. */ public void setBackgroundGray(int gray) { backgroundPaletteGray = gray; backgroundSet = true; } /** * Returns the suggested gray level of the background. * *

If the background gray level has not previously been * set, or has been unset, an * IllegalStateException will be thrown. * * @throws IllegalStateException if the background gray level * is not set. */ public int getBackgroundGray() { if (!backgroundSet) { throw new IllegalStateException(JaiI18N.getString("PNGEncodeParam6")); } return backgroundPaletteGray; } // tRNS chunk private int[] transparency; /** * Sets the gray value to be used to denote transparency. * *

Setting this attribute will cause the alpha channel * of the input image to be ignored. * *

The 'tRNS' chunk will encode this information. */ public void setTransparentGray(int transparentGray) { transparency = new int[1]; transparency[0] = transparentGray; transparencySet = true; } /** * Returns the gray value to be used to denote transparency. * *

If the transparent gray value has not previously been * set, or has been unset, an * IllegalStateException will be thrown. * * @throws IllegalStateException if the transparent gray value * is not set. */ public int getTransparentGray() { if (!transparencySet) { throw new IllegalStateException(JaiI18N.getString("PNGEncodeParam7")); } int gray = transparency[0]; return gray; } private int bitShift; private boolean bitShiftSet = false; /** * Sets the desired bit shift for a grayscale image. * Pixels in the source image will be shifted right by * the given amount prior to being clamped to the maximum * value given by the encoded image's bit depth. */ public void setBitShift(int bitShift) { if (bitShift < 0) { throw new RuntimeException(); } this.bitShift = bitShift; bitShiftSet = true; } /** * Returns the desired bit shift for a grayscale image. * *

If the bit shift has not previously been set, or has been * unset, an IllegalStateException will be thrown. * * @throws IllegalStateException if the bit shift is not set. */ public int getBitShift() { if (!bitShiftSet) { throw new IllegalStateException(JaiI18N.getString("PNGEncodeParam8")); } return bitShift; } /** * Suppresses the setting of the bit shift of a grayscale image. * Pixels in the source image will not be shifted prior to encoding. */ public void unsetBitShift() { bitShiftSet = false; } /** * Returns true if the bit shift has been set. */ public boolean isBitShiftSet() { return bitShiftSet; } /** * Returns true if the bit depth has been set. */ public boolean isBitDepthSet() { return bitDepthSet; } } public static class RGB extends PNGEncodeParam { /** Constructs an instance of PNGEncodeParam.RGB. */ public RGB() {} // bKGD chunk private boolean backgroundSet = false; /** * Suppresses the 'bKGD' chunk from being output. */ public void unsetBackground() { backgroundSet = false; } /** * Returns true if a 'bKGD' chunk will be output. */ public boolean isBackgroundSet() { return backgroundSet; } /** * Sets the desired bit depth for an RGB image. The bit * depth must be 8 or 16. */ public void setBitDepth(int bitDepth) { if (bitDepth != 8 && bitDepth != 16) { throw new RuntimeException(); } this.bitDepth = bitDepth; bitDepthSet = true; } // bKGD chunk private int[] backgroundRGB; /** * Sets the RGB value of the suggested background color. * The rgb parameter should have 3 entries. * *

The 'bKGD' chunk will encode this information. */ public void setBackgroundRGB(int[] rgb) { if (rgb.length != 3) { throw new RuntimeException(); } backgroundRGB = rgb; backgroundSet = true; } /** * Returns the RGB value of the suggested background color. * *

If the background color has not previously been set, or has been * unset, an IllegalStateException will be thrown. * * @throws IllegalStateException if the background color is not set. */ public int[] getBackgroundRGB() { if (!backgroundSet) { throw new IllegalStateException(JaiI18N.getString("PNGEncodeParam9")); } return backgroundRGB; } // tRNS chunk private int[] transparency; /** * Sets the RGB value to be used to denote transparency. * *

Setting this attribute will cause the alpha channel * of the input image to be ignored. * *

The 'tRNS' chunk will encode this information. */ public void setTransparentRGB(int[] transparentRGB) { transparency = (int[])(transparentRGB.clone()); transparencySet = true; } /** * Returns the RGB value to be used to denote transparency. * *

If the transparent color has not previously been set, * or has been unset, an IllegalStateException * will be thrown. * * @throws IllegalStateException if the transparent color is not set. */ public int[] getTransparentRGB() { if (!transparencySet) { throw new IllegalStateException(JaiI18N.getString("PNGEncodeParam10")); } return (int[])(transparency.clone()); } } protected int bitDepth; protected boolean bitDepthSet = false; /** * Sets the desired bit depth of an image. */ public abstract void setBitDepth(int bitDepth); /** * Returns the desired bit depth for a grayscale image. * *

If the bit depth has not previously been set, or has been * unset, an IllegalStateException will be thrown. * * @throws IllegalStateException if the bit depth is not set. */ public int getBitDepth() { if (!bitDepthSet) { throw new IllegalStateException(JaiI18N.getString("PNGEncodeParam11")); } return bitDepth; } /** * Suppresses the setting of the bit depth of a grayscale image. * The depth of the encoded image will be inferred from the source * image bit depth, rounded up to the next power of 2 between 1 * and 16. */ public void unsetBitDepth() { bitDepthSet = false; } private boolean useInterlacing = false; /** * Turns Adam7 interlacing on or off. */ public void setInterlacing(boolean useInterlacing) { this.useInterlacing = useInterlacing; } /** * Returns true if Adam7 interlacing will be used. */ public boolean getInterlacing() { return useInterlacing; } // bKGD chunk - delegate to subclasses // In JAI 1.0, 'backgroundSet' was private. The JDK 1.2 compiler // was lenient and incorrectly allowed this variable to be // accessed from the subclasses. The JDK 1.3 compiler correctly // flags this as a use of a non-static variable in a static // context. Changing 'backgroundSet' to protected would have // solved the problem, but would have introduced a visible API // change. Thus we are forced to adopt the solution of placing a // separate private variable in each subclass and providing // separate implementations of 'unsetBackground' and // 'isBackgroundSet' in each concrete subclass. /** * Suppresses the 'bKGD' chunk from being output. * For API compatibility with JAI 1.0, the superclass * defines this method to throw a RuntimeException; * accordingly, subclasses must provide their own implementations. */ public void unsetBackground() { throw new RuntimeException(JaiI18N.getString("PNGEncodeParam23")); } /** * Returns true if a 'bKGD' chunk will be output. * For API compatibility with JAI 1.0, the superclass * defines this method to throw a RuntimeException; * accordingly, subclasses must provide their own implementations. */ public boolean isBackgroundSet() { throw new RuntimeException(JaiI18N.getString("PNGEncodeParam24")); } // cHRM chunk private float[] chromaticity = null; private boolean chromaticitySet = false; /** * Sets the white point and primary chromaticities in CIE (x, y) * space. * *

The chromaticity parameter should be a * float array of length 8 containing the white point * X and Y, red X and Y, green X and Y, and blue X and Y values in * order. * *

The 'cHRM' chunk will encode this information. */ public void setChromaticity(float[] chromaticity) { if (chromaticity.length != 8) { throw new IllegalArgumentException(); } this.chromaticity = (float[])(chromaticity.clone()); chromaticitySet = true; } /** * A convenience method that calls the array version. */ public void setChromaticity(float whitePointX, float whitePointY, float redX, float redY, float greenX, float greenY, float blueX, float blueY) { float[] chroma = new float[8]; chroma[0] = whitePointX; chroma[1] = whitePointY; chroma[2] = redX; chroma[3] = redY; chroma[4] = greenX; chroma[5] = greenY; chroma[6] = blueX; chroma[7] = blueY; setChromaticity(chroma); } /** * Returns the white point and primary chromaticities in * CIE (x, y) space. * *

See the documentation for the setChromaticity * method for the format of the returned data. * *

If the chromaticity has not previously been set, or has been * unset, an IllegalStateException will be thrown. * * @throws IllegalStateException if the chromaticity is not set. */ public float[] getChromaticity() { if (!chromaticitySet) { throw new IllegalStateException(JaiI18N.getString("PNGEncodeParam12")); } return (float[])(chromaticity.clone()); } /** * Suppresses the 'cHRM' chunk from being output. */ public void unsetChromaticity() { chromaticity = null; chromaticitySet = false; } /** * Returns true if a 'cHRM' chunk will be output. */ public boolean isChromaticitySet() { return chromaticitySet; } // gAMA chunk private float gamma; private boolean gammaSet = false; /** * Sets the file gamma value for the image. * *

The 'gAMA' chunk will encode this information. */ public void setGamma(float gamma) { this.gamma = gamma; gammaSet = true; } /** * Returns the file gamma value for the image. * *

If the file gamma has not previously been set, or has been * unset, an IllegalStateException will be thrown. * * @throws IllegalStateException if the gamma is not set. */ public float getGamma() { if (!gammaSet) { throw new IllegalStateException(JaiI18N.getString("PNGEncodeParam13")); } return gamma; } /** * Suppresses the 'gAMA' chunk from being output. */ public void unsetGamma() { gammaSet = false; } /** * Returns true if a 'gAMA' chunk will be output. */ public boolean isGammaSet() { return gammaSet; } // hIST chunk private int[] paletteHistogram = null; private boolean paletteHistogramSet = false; /** * Sets the palette histogram to be stored with this image. * The histogram consists of an array of integers, one per * palette entry. * *

The 'hIST' chunk will encode this information. */ public void setPaletteHistogram(int[] paletteHistogram) { this.paletteHistogram = (int[])(paletteHistogram.clone()); paletteHistogramSet = true; } /** * Returns the palette histogram to be stored with this image. * *

If the histogram has not previously been set, or has been * unset, an IllegalStateException will be thrown. * * @throws IllegalStateException if the histogram is not set. */ public int[] getPaletteHistogram() { if (!paletteHistogramSet) { throw new IllegalStateException(JaiI18N.getString("PNGEncodeParam14")); } return paletteHistogram; } /** * Suppresses the 'hIST' chunk from being output. */ public void unsetPaletteHistogram() { paletteHistogram = null; paletteHistogramSet = false; } /** * Returns true if a 'hIST' chunk will be output. */ public boolean isPaletteHistogramSet() { return paletteHistogramSet; } // iCCP chunk private byte[] ICCProfileData = null; private boolean ICCProfileDataSet = false; /** * Sets the ICC profile data to be stored with this image. * The profile is represented in raw binary form. * *

The 'iCCP' chunk will encode this information. */ public void setICCProfileData(byte[] ICCProfileData) { this.ICCProfileData = (byte[])(ICCProfileData.clone()); ICCProfileDataSet = true; } /** * Returns the ICC profile data to be stored with this image. * *

If the ICC profile has not previously been set, or has been * unset, an IllegalStateException will be thrown. * * @throws IllegalStateException if the ICC profile is not set. */ public byte[] getICCProfileData() { if (!ICCProfileDataSet) { throw new IllegalStateException(JaiI18N.getString("PNGEncodeParam15")); } return (byte[])(ICCProfileData.clone()); } /** * Suppresses the 'iCCP' chunk from being output. */ public void unsetICCProfileData() { ICCProfileData = null; ICCProfileDataSet = false; } /** * Returns true if a 'iCCP' chunk will be output. */ public boolean isICCProfileDataSet() { return ICCProfileDataSet; } // pHYS chunk private int[] physicalDimension = null; private boolean physicalDimensionSet = false; /** * Sets the physical dimension information to be stored with this * image. The physicalDimension parameter should be a 3-entry * array containing the number of pixels per unit in the X * direction, the number of pixels per unit in the Y direction, * and the unit specifier (0 = unknown, 1 = meters). * *

The 'pHYS' chunk will encode this information. */ public void setPhysicalDimension(int[] physicalDimension) { this.physicalDimension = (int[])(physicalDimension.clone()); physicalDimensionSet = true; } /** * A convenience method that calls the array version. */ public void setPhysicalDimension(int xPixelsPerUnit, int yPixelsPerUnit, int unitSpecifier) { int[] pd = new int[3]; pd[0] = xPixelsPerUnit; pd[1] = yPixelsPerUnit; pd[2] = unitSpecifier; setPhysicalDimension(pd); } /** * Returns the physical dimension information to be stored * with this image. * *

If the physical dimension information has not previously * been set, or has been unset, an * IllegalStateException will be thrown. * * @throws IllegalStateException if the physical dimension information * is not set. */ public int[] getPhysicalDimension() { if (!physicalDimensionSet) { throw new IllegalStateException(JaiI18N.getString("PNGEncodeParam16")); } return (int[])(physicalDimension.clone()); } /** * Suppresses the 'pHYS' chunk from being output. */ public void unsetPhysicalDimension() { physicalDimension = null; physicalDimensionSet = false; } /** * Returns true if a 'pHYS' chunk will be output. */ public boolean isPhysicalDimensionSet() { return physicalDimensionSet; } // sPLT chunk private PNGSuggestedPaletteEntry[] suggestedPalette = null; private boolean suggestedPaletteSet = false; /** * Sets the suggested palette information to be stored with this * image. The information is passed to this method as an array of * PNGSuggestedPaletteEntry objects. * *

The 'sPLT' chunk will encode this information. */ public void setSuggestedPalette(PNGSuggestedPaletteEntry[] palette) { suggestedPalette = (PNGSuggestedPaletteEntry[])(palette.clone()); suggestedPaletteSet = true; } /** * Returns the suggested palette information to be stored with this * image. * *

If the suggested palette information has not previously * been set, or has been unset, an * IllegalStateException will be thrown. * * @throws IllegalStateException if the suggested palette * information is not set. */ public PNGSuggestedPaletteEntry[] getSuggestedPalette() { if (!suggestedPaletteSet) { throw new IllegalStateException(JaiI18N.getString("PNGEncodeParam17")); } return (PNGSuggestedPaletteEntry[])(suggestedPalette.clone()); } /** * Suppresses the 'sPLT' chunk from being output. */ public void unsetSuggestedPalette() { suggestedPalette = null; suggestedPaletteSet = false; } /** * Returns true if a 'sPLT' chunk will be output. */ public boolean isSuggestedPaletteSet() { return suggestedPaletteSet; } // sBIT chunk private int[] significantBits = null; private boolean significantBitsSet = false; /** * Sets the number of significant bits for each band of the image. * *

The number of entries in the significantBits * array must be equal to the number of output bands in the image: * 1 for a gray image, 2 for gray+alpha, 3 for index or truecolor, * and 4 for truecolor+alpha. * *

The 'sBIT' chunk will encode this information. */ public void setSignificantBits(int[] significantBits) { this.significantBits = (int[])(significantBits.clone()); significantBitsSet = true; } /** * Returns the number of significant bits for each band of the image. * *

If the significant bits values have not previously been * set, or have been unset, an IllegalStateException * will be thrown. * * @throws IllegalStateException if the significant bits values are * not set. */ public int[] getSignificantBits() { if (!significantBitsSet) { throw new IllegalStateException(JaiI18N.getString("PNGEncodeParam18")); } return (int[])significantBits.clone(); } /** * Suppresses the 'sBIT' chunk from being output. */ public void unsetSignificantBits() { significantBits = null; significantBitsSet = false; } /** * Returns true if an 'sBIT' chunk will be output. */ public boolean isSignificantBitsSet() { return significantBitsSet; } // sRGB chunk private int SRGBIntent; private boolean SRGBIntentSet = false; /** * Sets the sRGB rendering intent to be stored with this image. * The legal values are 0 = Perceptual, 1 = Relative Colorimetric, * 2 = Saturation, and 3 = Absolute Colorimetric. Refer to the * PNG specification for information on these values. * *

The 'sRGB' chunk will encode this information. */ public void setSRGBIntent(int SRGBIntent) { this.SRGBIntent = SRGBIntent; SRGBIntentSet = true; } /** * Returns the sRGB rendering intent to be stored with this image. * *

If the sRGB intent has not previously been set, or has been * unset, an IllegalStateException will be thrown. * * @throws IllegalStateException if the sRGB intent is not set. */ public int getSRGBIntent() { if (!SRGBIntentSet) { throw new IllegalStateException(JaiI18N.getString("PNGEncodeParam19")); } return SRGBIntent; } /** * Suppresses the 'sRGB' chunk from being output. */ public void unsetSRGBIntent() { SRGBIntentSet = false; } /** * Returns true if an 'sRGB' chunk will be output. */ public boolean isSRGBIntentSet() { return SRGBIntentSet; } // tEXt chunk private String[] text = null; private boolean textSet = false; /** * Sets the textual data to be stored in uncompressed form with this * image. The data is passed to this method as an array of * Strings. * *

The 'tEXt' chunk will encode this information. */ public void setText(String[] text) { this.text = text; textSet = true; } /** * Returns the text strings to be stored in uncompressed form with this * image as an array of Strings. * *

If the text strings have not previously been set, or have been * unset, an IllegalStateException will be thrown. * * @throws IllegalStateException if the text strings are not set. */ public String[] getText() { if (!textSet) { throw new IllegalStateException(JaiI18N.getString("PNGEncodeParam20")); } return text; } /** * Suppresses the 'tEXt' chunk from being output. */ public void unsetText() { text = null; textSet = false; } /** * Returns true if a 'tEXt' chunk will be output. */ public boolean isTextSet() { return textSet; } // tIME chunk private Date modificationTime; private boolean modificationTimeSet = false; /** * Sets the modification time, as a Date, to be * stored with this image. The internal storage format will use * UTC regardless of how the modificationTime * parameter was created. * *

The 'tIME' chunk will encode this information. */ public void setModificationTime(Date modificationTime) { this.modificationTime = modificationTime; modificationTimeSet = true; } /** * Returns the modification time to be stored with this image. * *

If the bit depth has not previously been set, or has been * unset, an IllegalStateException will be thrown. * * @throws IllegalStateException if the bit depth is not set. */ public Date getModificationTime() { if (!modificationTimeSet) { throw new IllegalStateException(JaiI18N.getString("PNGEncodeParam21")); } return modificationTime; } /** * Suppresses the 'tIME' chunk from being output. */ public void unsetModificationTime() { modificationTime = null; modificationTimeSet = false; } /** * Returns true if a 'tIME' chunk will be output. */ public boolean isModificationTimeSet() { return modificationTimeSet; } // tRNS chunk boolean transparencySet = false; /** * Suppresses the 'tRNS' chunk from being output. */ public void unsetTransparency() { transparencySet = false; } /** * Returns true if a 'tRNS' chunk will be output. */ public boolean isTransparencySet() { return transparencySet; } // zTXT chunk private String[] zText = null; private boolean zTextSet = false; /** * Sets the text strings to be stored in compressed form with this * image. The data is passed to this method as an array of * Strings. * *

The 'zTXt' chunk will encode this information. */ public void setCompressedText(String[] text) { this.zText = text; zTextSet = true; } /** * Returns the text strings to be stored in compressed form with * this image as an array of Strings. * *

If the compressed text strings have not previously been * set, or have been unset, an IllegalStateException * will be thrown. * * @throws IllegalStateException if the compressed text strings are * not set. */ public String[] getCompressedText() { if (!zTextSet) { throw new IllegalStateException(JaiI18N.getString("PNGEncodeParam22")); } return zText; } /** * Suppresses the 'zTXt' chunk from being output. */ public void unsetCompressedText() { zText = null; zTextSet = false; } /** * Returns true if a 'zTXT' chunk will be output. */ public boolean isCompressedTextSet() { return zTextSet; } // Other chunk types Vector chunkType = new Vector(); Vector chunkData = new Vector(); /** * Adds a private chunk, in binary form, to the list of chunks to * be stored with this image. * * @param type a 4-character String giving the chunk type name. * @param data an array of bytes containing the * chunk data. */ public synchronized void addPrivateChunk(String type, byte[] data) { chunkType.add(type); chunkData.add((byte[])data.clone()); } /** * Returns the number of private chunks to be written to the * output file. */ public synchronized int getNumPrivateChunks() { return chunkType.size(); } /** * Returns the type of the private chunk at a given index, as a * 4-character String. The index must be smaller * than the return value of getNumPrivateChunks. */ public synchronized String getPrivateChunkType(int index) { return (String)chunkType.elementAt(index); } /** * Returns the data associated of the private chunk at a given * index, as an array of bytes. The index must be * smaller than the return value of * getNumPrivateChunks. */ public synchronized byte[] getPrivateChunkData(int index) { return (byte[])chunkData.elementAt(index); } /** * Remove all private chunks associated with this parameter instance * whose 'safe-to-copy' bit is not set. This may be advisable when * transcoding PNG images. */ public synchronized void removeUnsafeToCopyPrivateChunks() { Vector newChunkType = new Vector(); Vector newChunkData = new Vector(); int len = getNumPrivateChunks(); for (int i = 0; i < len; i++) { String type = getPrivateChunkType(i); char lastChar = type.charAt(3); if (lastChar >= 'a' && lastChar <= 'z') { newChunkType.add(type); newChunkData.add(getPrivateChunkData(i)); } } chunkType = newChunkType; chunkData = newChunkData; } /** * Remove all private chunks associated with this parameter instance. */ public synchronized void removeAllPrivateChunks() { chunkType = new Vector(); chunkData = new Vector(); } /** * An abs() function for use by the Paeth predictor. */ private static final int abs(int x) { return (x < 0) ? -x : x; } /** * The Paeth predictor routine used in PNG encoding. This routine * is included as a convenience to subclasses that override the * filterRow method. */ public static final int paethPredictor(int a, int b, int c) { int p = a + b - c; int pa = abs(p - a); int pb = abs(p - b); int pc = abs(p - c); if ((pa <= pb) && (pa <= pc)) { return a; } else if (pb <= pc) { return b; } else { return c; } } /** * Performs filtering on a row of an image. This method may be * overridden in order to provide a custom algorithm for choosing * the filter type for a given row. * *

The method is supplied with the current and previous rows * of the image. For the first row of the image, or of an * interlacing pass, the previous row array will be filled with * zeros as required by the PNG specification. * *

The method is also supplied with five scratch arrays. * These arrays may be used within the method for any purpose. * At method exit, the array at the index given by the return * value of the method should contain the filtered data. The * return value will also be used as the filter type. * *

The default implementation of the method performs a trial * encoding with each of the filter types, and computes the sum of * absolute values of the differences between the raw bytes of the * current row and the predicted values. The index of the filter * producing the smallest result is returned. * *

As an example, to perform only 'sub' filtering, this method * could be implemented (non-optimally) as follows: * *

     * for (int i = bytesPerPixel; i < bytesPerRow + bytesPerPixel; i++) {
     *     int curr = currRow[i] & 0xff;
     *     int left = currRow[i - bytesPerPixel] & 0xff;
     *     scratchRow[PNG_FILTER_SUB][i] = (byte)(curr - left);
     * }
     * return PNG_FILTER_SUB;
     * 
* * @param currRow The current row as an array of bytes * of length at least bytesPerRow + bytesPerPixel. * The pixel data starts at index bytesPerPixel; * the initial bytesPerPixel bytes are zero. * @param prevRow The current row as an array of bytes * The pixel data starts at index bytesPerPixel; * the initial bytesPerPixel bytes are zero. * @param scratchRows An array of 5 byte arrays of * length at least bytesPerRow + * bytesPerPixel, useable to hold temporary results. * The filtered row will be returned as one of the entries * of this array. The returned filtered data should start * at index bytesPerPixel; The initial * bytesPerPixel bytes are not used. * @param bytesPerRow The number of bytes in the image row. * This value will always be greater than 0. * @param bytesPerPixel The number of bytes representing a single * pixel, rounded up to an integer. This is the 'bpp' parameter * described in the PNG specification. * * @return The filter type to be used. The entry of * scratchRows[] at this index holds the * filtered data. */ public int filterRow(byte[] currRow, byte[] prevRow, byte[][] scratchRows, int bytesPerRow, int bytesPerPixel) { int[] filterBadness = new int[5]; for (int i = 0; i < 5; i++) { filterBadness[i] = Integer.MAX_VALUE; } { int badness = 0; for (int i = bytesPerPixel; i < bytesPerRow + bytesPerPixel; i++) { int curr = currRow[i] & 0xff; badness += curr; } filterBadness[0] = badness; } { byte[] subFilteredRow = scratchRows[1]; int badness = 0; for (int i = bytesPerPixel; i < bytesPerRow + bytesPerPixel; i++) { int curr = currRow[i] & 0xff; int left = currRow[i - bytesPerPixel] & 0xff; int difference = curr - left; subFilteredRow[i] = (byte)difference; badness += abs(difference); } filterBadness[1] = badness; } { byte[] upFilteredRow = scratchRows[2]; int badness = 0; for (int i = bytesPerPixel; i < bytesPerRow + bytesPerPixel; i++) { int curr = currRow[i] & 0xff; int up = prevRow[i] & 0xff; int difference = curr - up; upFilteredRow[i] = (byte)difference; badness += abs(difference); } filterBadness[2] = badness; } { byte[] averageFilteredRow = scratchRows[3]; int badness = 0; for (int i = bytesPerPixel; i < bytesPerRow + bytesPerPixel; i++) { int curr = currRow[i] & 0xff; int left = currRow[i - bytesPerPixel] & 0xff; int up = prevRow[i] & 0xff; int difference = curr - (left + up)/2;; averageFilteredRow[i] = (byte)difference; badness += abs(difference); } filterBadness[3] = badness; } { byte[] paethFilteredRow = scratchRows[4]; int badness = 0; for (int i = bytesPerPixel; i < bytesPerRow + bytesPerPixel; i++) { int curr = currRow[i] & 0xff; int left = currRow[i - bytesPerPixel] & 0xff; int up = prevRow[i] & 0xff; int upleft = prevRow[i - bytesPerPixel] & 0xff; int predictor = paethPredictor(left, up, upleft); int difference = curr - predictor; paethFilteredRow[i] = (byte)difference; badness += abs(difference); } filterBadness[4] = badness; } int filterType = 0; int minBadness = filterBadness[0]; for (int i = 1; i < 5; i++) { if (filterBadness[i] < minBadness) { minBadness = filterBadness[i]; filterType = i; } } if (filterType == 0) { System.arraycopy(currRow, bytesPerPixel, scratchRows[0], bytesPerPixel, bytesPerRow); } return filterType; } }