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 int
s.
*/
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 int
s.
*/
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
* String
s.
*
*
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 String
s.
*
*
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
* String
s.
*
*
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 String
s.
*
*
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 byte
s 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 byte
s. 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
byte
s
* 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 byte
s
* 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;
}
}