All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.xmlgraphics.image.codec.png.PNGEncodeParam Maven / Gradle / Ivy

There is a newer version: 1.2.2.1-jre17
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* $Id: PNGEncodeParam.java 1732018 2016-02-24 04:51:06Z gadams $ */

package org.apache.xmlgraphics.image.codec.png;

import java.awt.image.ColorModel;
import java.awt.image.IndexColorModel;
import java.awt.image.RenderedImage;
import java.awt.image.SampleModel;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.apache.xmlgraphics.image.codec.util.ImageEncodeParam;
import org.apache.xmlgraphics.image.codec.util.PropertyUtil;

// CSOFF: MemberName
// CSOFF: MultipleVariableDeclarations
// CSOFF: NeedBraces
// CSOFF: OperatorWrap
// CSOFF: ParameterName
// CSOFF: WhitespaceAround

/**
 * 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 { private static final long serialVersionUID = -7851509538552141263L; /** 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 { private static final long serialVersionUID = -5181545170427733891L; /** Constructs an instance of PNGEncodeParam.Palette. */ public Palette() { } // bKGD chunk private boolean backgroundSet; /** * 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(PropertyUtil.getString("PNGEncodeParam2")); } this.bitDepth = bitDepth; bitDepthSet = true; } // PLTE chunk private int[] palette; private boolean paletteSet; /** * 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(PropertyUtil.getString("PNGEncodeParam0")); } if ((rgb.length % 3) != 0) { throw new IllegalArgumentException(PropertyUtil.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(PropertyUtil.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(PropertyUtil.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(PropertyUtil.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 { private static final long serialVersionUID = -2055439792025795274L; /** Constructs an instance of PNGEncodeParam.Gray. */ public Gray() { } // bKGD chunk private boolean backgroundSet; /** * 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(PropertyUtil.getString("PNGEncodeParam2")); } 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(PropertyUtil.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(PropertyUtil.getString("PNGEncodeParam7")); } int gray = transparency[0]; return gray; } private int bitShift; private boolean bitShiftSet; /** * 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 IllegalArgumentException(PropertyUtil.getString("PNGEncodeParam25")); } 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(PropertyUtil.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 { private static final long serialVersionUID = -8918762026006670891L; /** Constructs an instance of PNGEncodeParam.RGB. */ public RGB() { } // bKGD chunk private boolean backgroundSet; /** * 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 IllegalArgumentException(PropertyUtil.getString("PNGEncodeParam26")); } 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 IllegalArgumentException(PropertyUtil.getString("PNGEncodeParam27")); } 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(PropertyUtil.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(PropertyUtil.getString("PNGEncodeParam10")); } return (int[])(transparency.clone()); } } protected int bitDepth; protected boolean bitDepthSet; /** * 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(PropertyUtil.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; /** * 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(PropertyUtil.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(PropertyUtil.getString("PNGEncodeParam24")); } // cHRM chunk private float[] chromaticity; private boolean chromaticitySet; /** * 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(PropertyUtil.getString("PNGEncodeParam28")); } 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(PropertyUtil.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; /** * 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(PropertyUtil.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; private boolean paletteHistogramSet; /** * 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(PropertyUtil.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; private boolean iccProfileDataSet; /** * 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(PropertyUtil.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; private boolean physicalDimensionSet; /** * 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(PropertyUtil.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; private boolean suggestedPaletteSet; /** * 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(PropertyUtil.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; private boolean significantBitsSet; /** * 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(PropertyUtil.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; /** * 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(PropertyUtil.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; private boolean textSet; /** * 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(PropertyUtil.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; /** * 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(PropertyUtil.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; /** * 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; private boolean zTextSet; /** * 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(PropertyUtil.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 List chunkType = new ArrayList(); List chunkData = new ArrayList(); /** * 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(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.get(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.get(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() { List newChunkType = new ArrayList(); List newChunkData = new ArrayList(); 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 ArrayList(); chunkData = new ArrayList(); } /** * An abs() function for use by the Paeth predictor. */ private static 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 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 [] badness = {0, 0, 0, 0, 0}; int curr; int left; int up; int upleft; int diff; int pa; int pb; int pc; for (int i = bytesPerPixel; i < bytesPerRow + bytesPerPixel; i++) { curr = currRow[i] & 0xff; left = currRow[i - bytesPerPixel] & 0xff; up = prevRow[i] & 0xff; upleft = prevRow[i - bytesPerPixel] & 0xff; // no filter badness[0] += curr; // sub filter diff = curr - left; scratchRows[1][i] = (byte)diff; badness [1] += (diff > 0) ? diff : -diff; // up filter diff = curr - up; scratchRows[2][i] = (byte)diff; badness [2] += (diff >= 0) ? diff : -diff; // average filter diff = curr - ((left + up) >> 1); scratchRows[3][i] = (byte)diff; badness [3] += (diff >= 0) ? diff : -diff; // paeth filter // Original code much simplier but doesn't take full // advantage of relationship between pa/b/c and // information gleaned in abs operations. /// pa = up -upleft; /// pb = left-upleft; /// pc = pa+pb; /// pa = abs(pa); /// pb = abs(pb); /// pc = abs(pc); /// if ((pa <= pb) && (pa <= pc)) /// diff = curr-left; /// else if (pb <= pc) /// diff = curr-up; /// else /// diff = curr-upleft; pa = up - upleft; pb = left - upleft; if (pa < 0) { if (pb < 0) { // both pa & pb neg so pc is always greater than or // equal to pa or pb; if (pa >= pb) { // since pa & pb neg check sense is reversed. diff = curr - left; } else { diff = curr - up; } } else { // pa neg pb pos so we must compute pc... pc = pa + pb; pa = -pa; if (pa <= pb) { // pc is positive and less than pb if (pa <= pc) { diff = curr - left; } else { diff = curr - upleft; } } else { // pc is negative and less than or equal to pa, // but since pa is greater than pb this isn't an issue... if (pb <= -pc) { diff = curr - up; } else { diff = curr - upleft; } } } } else { if (pb < 0) { pb = -pb; // make it positive... if (pa <= pb) { // pc would be negative and less than or equal to pb pc = pb - pa; if (pa <= pc) { diff = curr - left; } else if (pb == pc) { // if pa is zero then pc==pb otherwise // pc must be less than pb. diff = curr - up; } else { diff = curr - upleft; } } else { // pc would be positive and less than pa. pc = pa - pb; if (pb <= pc) { diff = curr - up; } else { diff = curr - upleft; } } } else { // both pos so pa+pb is always greater than pa/pb if (pa <= pb) { diff = curr - left; } else { diff = curr - up; } } } scratchRows[4][i] = (byte)diff; badness [4] += (diff >= 0) ? diff : -diff; } int filterType = 0; int minBadness = badness[0]; for (int i = 1; i < 5; i++) { if (badness[i] < minBadness) { minBadness = badness[i]; filterType = i; } } if (filterType == 0) { System.arraycopy(currRow, bytesPerPixel, scratchRows[0], bytesPerPixel, bytesPerRow); } return filterType; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy