0001: /*
0002: * Licensed to the Apache Software Foundation (ASF) under one or more
0003: * contributor license agreements. See the NOTICE file distributed with
0004: * this work for additional information regarding copyright ownership.
0005: * The ASF licenses this file to You under the Apache License, Version 2.0
0006: * (the "License"); you may not use this file except in compliance with
0007: * the License. You may obtain a copy of the License at
0008: *
0009: * http://www.apache.org/licenses/LICENSE-2.0
0010: *
0011: * Unless required by applicable law or agreed to in writing, software
0012: * distributed under the License is distributed on an "AS IS" BASIS,
0013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014: * See the License for the specific language governing permissions and
0015: * limitations under the License.
0016: */
0017:
0018: /* $Id: PNGEncodeParam.java 496556 2007-01-16 00:59:48Z cam $ */
0019:
0020: package org.apache.xmlgraphics.image.codec.png;
0021:
0022: import org.apache.xmlgraphics.image.codec.util.ImageEncodeParam;
0023: import org.apache.xmlgraphics.image.codec.util.PropertyUtil;
0024:
0025: import java.awt.image.ColorModel;
0026: import java.awt.image.IndexColorModel;
0027: import java.awt.image.RenderedImage;
0028: import java.awt.image.SampleModel;
0029: import java.util.Date;
0030: import java.util.List;
0031: import java.util.ArrayList;
0032:
0033: /**
0034: * An instance of <code>ImageEncodeParam</code> for encoding images in
0035: * the PNG format.
0036: *
0037: * <p><b> This class is not a committed part of the JAI API. It may
0038: * be removed or changed in future releases of JAI.</b>
0039: */
0040: public abstract class PNGEncodeParam implements ImageEncodeParam {
0041:
0042: /** Constant for use with the sRGB chunk. */
0043: public static final int INTENT_PERCEPTUAL = 0;
0044:
0045: /** Constant for use with the sRGB chunk. */
0046: public static final int INTENT_RELATIVE = 1;
0047:
0048: /** Constant for use with the sRGB chunk. */
0049: public static final int INTENT_SATURATION = 2;
0050:
0051: /** Constant for use with the sRGB chunk. */
0052: public static final int INTENT_ABSOLUTE = 3;
0053:
0054: /** Constant for use in filtering. */
0055: public static final int PNG_FILTER_NONE = 0;
0056:
0057: /** Constant for use in filtering. */
0058: public static final int PNG_FILTER_SUB = 1;
0059:
0060: /** Constant for use in filtering. */
0061: public static final int PNG_FILTER_UP = 2;
0062:
0063: /** Constant for use in filtering. */
0064: public static final int PNG_FILTER_AVERAGE = 3;
0065:
0066: /** Constant for use in filtering. */
0067: public static final int PNG_FILTER_PAETH = 4;
0068:
0069: /**
0070: * Returns an instance of <code>PNGEncodeParam.Palette</code>,
0071: * <code>PNGEncodeParam.Gray</code>, or
0072: * <code>PNGEncodeParam.RGB</code> appropriate for encoding
0073: * the given image.
0074: *
0075: * <p> If the image has an <code>IndexColorModel</code>, an
0076: * instance of <code>PNGEncodeParam.Palette</code> is returned.
0077: * Otherwise, if the image has 1 or 2 bands an instance of
0078: * <code>PNGEncodeParam.Gray</code> is returned. In all other
0079: * cases an instance of <code>PNGEncodeParam.RGB</code> is
0080: * returned.
0081: *
0082: * <p> Note that this method does not provide any guarantee that
0083: * the given image will be successfully encoded by the PNG
0084: * encoder, as it only performs a very superficial analysis of
0085: * the image structure.
0086: */
0087: public static PNGEncodeParam getDefaultEncodeParam(RenderedImage im) {
0088: ColorModel colorModel = im.getColorModel();
0089: if (colorModel instanceof IndexColorModel) {
0090: return new PNGEncodeParam.Palette();
0091: }
0092:
0093: SampleModel sampleModel = im.getSampleModel();
0094: int numBands = sampleModel.getNumBands();
0095:
0096: if (numBands == 1 || numBands == 2) {
0097: return new PNGEncodeParam.Gray();
0098: } else {
0099: return new PNGEncodeParam.RGB();
0100: }
0101: }
0102:
0103: public static class Palette extends PNGEncodeParam {
0104:
0105: /** Constructs an instance of <code>PNGEncodeParam.Palette</code>. */
0106: public Palette() {
0107: }
0108:
0109: // bKGD chunk
0110:
0111: private boolean backgroundSet = false;
0112:
0113: /**
0114: * Suppresses the 'bKGD' chunk from being output.
0115: */
0116: public void unsetBackground() {
0117: backgroundSet = false;
0118: }
0119:
0120: /**
0121: * Returns true if a 'bKGD' chunk will be output.
0122: */
0123: public boolean isBackgroundSet() {
0124: return backgroundSet;
0125: }
0126:
0127: /**
0128: * Sets the desired bit depth for a palette image. The bit
0129: * depth must be one of 1, 2, 4, or 8, or else an
0130: * <code>IllegalArgumentException</code> will be thrown.
0131: */
0132: public void setBitDepth(int bitDepth) {
0133: if (bitDepth != 1 && bitDepth != 2 && bitDepth != 4
0134: && bitDepth != 8) {
0135: throw new IllegalArgumentException(PropertyUtil
0136: .getString("PNGEncodeParam2"));
0137: }
0138: this .bitDepth = bitDepth;
0139: bitDepthSet = true;
0140: }
0141:
0142: // PLTE chunk
0143:
0144: private int[] palette = null;
0145: private boolean paletteSet = false;
0146:
0147: /**
0148: * Sets the RGB palette of the image to be encoded.
0149: * The <code>rgb</code> parameter contains alternating
0150: * R, G, B values for each color index used in the image.
0151: * The number of elements must be a multiple of 3 between
0152: * 3 and 3*256.
0153: *
0154: * <p> The 'PLTE' chunk will encode this information.
0155: *
0156: * @param rgb An array of <code>int</code>s.
0157: */
0158: public void setPalette(int[] rgb) {
0159: if (rgb.length < 1 * 3 || rgb.length > 256 * 3) {
0160: throw new IllegalArgumentException(PropertyUtil
0161: .getString("PNGEncodeParam0"));
0162: }
0163: if ((rgb.length % 3) != 0) {
0164: throw new IllegalArgumentException(PropertyUtil
0165: .getString("PNGEncodeParam1"));
0166: }
0167:
0168: palette = (int[]) (rgb.clone());
0169: paletteSet = true;
0170: }
0171:
0172: /**
0173: * Returns the current RGB palette.
0174: *
0175: * <p> If the palette has not previously been set, or has been
0176: * unset, an <code>IllegalStateException</code> will be thrown.
0177: *
0178: * @throws IllegalStateException if the palette is not set.
0179: *
0180: * @return An array of <code>int</code>s.
0181: */
0182: public int[] getPalette() {
0183: if (!paletteSet) {
0184: throw new IllegalStateException(PropertyUtil
0185: .getString("PNGEncodeParam3"));
0186: }
0187: return (int[]) (palette.clone());
0188: }
0189:
0190: /**
0191: * Suppresses the 'PLTE' chunk from being output.
0192: */
0193: public void unsetPalette() {
0194: palette = null;
0195: paletteSet = false;
0196: }
0197:
0198: /**
0199: * Returns true if a 'PLTE' chunk will be output.
0200: */
0201: public boolean isPaletteSet() {
0202: return paletteSet;
0203: }
0204:
0205: // bKGD chunk
0206:
0207: private int backgroundPaletteIndex;
0208:
0209: /**
0210: * Sets the palette index of the suggested background color.
0211: *
0212: * <p> The 'bKGD' chunk will encode this information.
0213: */
0214: public void setBackgroundPaletteIndex(int index) {
0215: backgroundPaletteIndex = index;
0216: backgroundSet = true;
0217: }
0218:
0219: /**
0220: * Returns the palette index of the suggested background color.
0221: *
0222: * <p> If the background palette index has not previously been
0223: * set, or has been unset, an
0224: * <code>IllegalStateException</code> will be thrown.
0225: *
0226: * @throws IllegalStateException if the palette index is not set.
0227: */
0228: public int getBackgroundPaletteIndex() {
0229: if (!backgroundSet) {
0230: throw new IllegalStateException(PropertyUtil
0231: .getString("PNGEncodeParam4"));
0232: }
0233: return backgroundPaletteIndex;
0234: }
0235:
0236: // tRNS chunk
0237:
0238: private int[] transparency;
0239:
0240: /**
0241: * Sets the alpha values associated with each palette entry.
0242: * The <code>alpha</code> parameter should have as many entries
0243: * as there are RGB triples in the palette.
0244: *
0245: * <p> The 'tRNS' chunk will encode this information.
0246: */
0247: public void setPaletteTransparency(byte[] alpha) {
0248: transparency = new int[alpha.length];
0249: for (int i = 0; i < alpha.length; i++) {
0250: transparency[i] = alpha[i] & 0xff;
0251: }
0252: transparencySet = true;
0253: }
0254:
0255: /**
0256: * Returns the alpha values associated with each palette entry.
0257: *
0258: * <p> If the palette transparency has not previously been
0259: * set, or has been unset, an
0260: * <code>IllegalStateException</code> will be thrown.
0261: *
0262: * @throws IllegalStateException if the palette transparency is
0263: * not set.
0264: */
0265: public byte[] getPaletteTransparency() {
0266: if (!transparencySet) {
0267: throw new IllegalStateException(PropertyUtil
0268: .getString("PNGEncodeParam5"));
0269: }
0270: byte[] alpha = new byte[transparency.length];
0271: for (int i = 0; i < alpha.length; i++) {
0272: alpha[i] = (byte) transparency[i];
0273: }
0274: return alpha;
0275: }
0276: }
0277:
0278: public static class Gray extends PNGEncodeParam {
0279:
0280: /** Constructs an instance of <code>PNGEncodeParam.Gray</code>. */
0281: public Gray() {
0282: }
0283:
0284: // bKGD chunk
0285:
0286: private boolean backgroundSet = false;
0287:
0288: /**
0289: * Suppresses the 'bKGD' chunk from being output.
0290: */
0291: public void unsetBackground() {
0292: backgroundSet = false;
0293: }
0294:
0295: /**
0296: * Returns true if a 'bKGD' chunk will be output.
0297: */
0298: public boolean isBackgroundSet() {
0299: return backgroundSet;
0300: }
0301:
0302: /**
0303: * Sets the desired bit depth for a grayscale image. The bit
0304: * depth must be one of 1, 2, 4, 8, or 16.
0305: *
0306: * <p> When encoding a source image of a greater bit depth,
0307: * pixel values will be clamped to the smaller range after
0308: * shifting by the value given by <code>getBitShift()</code>.
0309: * When encoding a source image of a smaller bit depth, pixel
0310: * values will be shifted and left-filled with zeroes.
0311: */
0312: public void setBitDepth(int bitDepth) {
0313: if (bitDepth != 1 && bitDepth != 2 && bitDepth != 4
0314: && bitDepth != 8 && bitDepth != 16) {
0315: throw new IllegalArgumentException();
0316: }
0317: this .bitDepth = bitDepth;
0318: bitDepthSet = true;
0319: }
0320:
0321: // bKGD chunk
0322:
0323: private int backgroundPaletteGray;
0324:
0325: /**
0326: * Sets the suggested gray level of the background.
0327: *
0328: * <p> The 'bKGD' chunk will encode this information.
0329: */
0330: public void setBackgroundGray(int gray) {
0331: backgroundPaletteGray = gray;
0332: backgroundSet = true;
0333: }
0334:
0335: /**
0336: * Returns the suggested gray level of the background.
0337: *
0338: * <p> If the background gray level has not previously been
0339: * set, or has been unset, an
0340: * <code>IllegalStateException</code> will be thrown.
0341: *
0342: * @throws IllegalStateException if the background gray level
0343: * is not set.
0344: */
0345: public int getBackgroundGray() {
0346: if (!backgroundSet) {
0347: throw new IllegalStateException(PropertyUtil
0348: .getString("PNGEncodeParam6"));
0349: }
0350: return backgroundPaletteGray;
0351: }
0352:
0353: // tRNS chunk
0354:
0355: private int[] transparency;
0356:
0357: /**
0358: * Sets the gray value to be used to denote transparency.
0359: *
0360: * <p> Setting this attribute will cause the alpha channel
0361: * of the input image to be ignored.
0362: *
0363: * <p> The 'tRNS' chunk will encode this information.
0364: */
0365: public void setTransparentGray(int transparentGray) {
0366: transparency = new int[1];
0367: transparency[0] = transparentGray;
0368: transparencySet = true;
0369: }
0370:
0371: /**
0372: * Returns the gray value to be used to denote transparency.
0373: *
0374: * <p> If the transparent gray value has not previously been
0375: * set, or has been unset, an
0376: * <code>IllegalStateException</code> will be thrown.
0377: *
0378: * @throws IllegalStateException if the transparent gray value
0379: * is not set.
0380: */
0381: public int getTransparentGray() {
0382: if (!transparencySet) {
0383: throw new IllegalStateException(PropertyUtil
0384: .getString("PNGEncodeParam7"));
0385: }
0386: int gray = transparency[0];
0387: return gray;
0388: }
0389:
0390: private int bitShift;
0391: private boolean bitShiftSet = false;
0392:
0393: /**
0394: * Sets the desired bit shift for a grayscale image.
0395: * Pixels in the source image will be shifted right by
0396: * the given amount prior to being clamped to the maximum
0397: * value given by the encoded image's bit depth.
0398: */
0399: public void setBitShift(int bitShift) {
0400: if (bitShift < 0) {
0401: throw new RuntimeException();
0402: }
0403: this .bitShift = bitShift;
0404: bitShiftSet = true;
0405: }
0406:
0407: /**
0408: * Returns the desired bit shift for a grayscale image.
0409: *
0410: * <p> If the bit shift has not previously been set, or has been
0411: * unset, an <code>IllegalStateException</code> will be thrown.
0412: *
0413: * @throws IllegalStateException if the bit shift is not set.
0414: */
0415: public int getBitShift() {
0416: if (!bitShiftSet) {
0417: throw new IllegalStateException(PropertyUtil
0418: .getString("PNGEncodeParam8"));
0419: }
0420: return bitShift;
0421: }
0422:
0423: /**
0424: * Suppresses the setting of the bit shift of a grayscale image.
0425: * Pixels in the source image will not be shifted prior to encoding.
0426: */
0427: public void unsetBitShift() {
0428: bitShiftSet = false;
0429: }
0430:
0431: /**
0432: * Returns true if the bit shift has been set.
0433: */
0434: public boolean isBitShiftSet() {
0435: return bitShiftSet;
0436: }
0437:
0438: /**
0439: * Returns true if the bit depth has been set.
0440: */
0441: public boolean isBitDepthSet() {
0442: return bitDepthSet;
0443: }
0444: }
0445:
0446: public static class RGB extends PNGEncodeParam {
0447:
0448: /** Constructs an instance of <code>PNGEncodeParam.RGB</code>. */
0449: public RGB() {
0450: }
0451:
0452: // bKGD chunk
0453:
0454: private boolean backgroundSet = false;
0455:
0456: /**
0457: * Suppresses the 'bKGD' chunk from being output.
0458: */
0459: public void unsetBackground() {
0460: backgroundSet = false;
0461: }
0462:
0463: /**
0464: * Returns true if a 'bKGD' chunk will be output.
0465: */
0466: public boolean isBackgroundSet() {
0467: return backgroundSet;
0468: }
0469:
0470: /**
0471: * Sets the desired bit depth for an RGB image. The bit
0472: * depth must be 8 or 16.
0473: */
0474: public void setBitDepth(int bitDepth) {
0475: if (bitDepth != 8 && bitDepth != 16) {
0476: throw new RuntimeException();
0477: }
0478: this .bitDepth = bitDepth;
0479: bitDepthSet = true;
0480: }
0481:
0482: // bKGD chunk
0483:
0484: private int[] backgroundRGB;
0485:
0486: /**
0487: * Sets the RGB value of the suggested background color.
0488: * The <code>rgb</code> parameter should have 3 entries.
0489: *
0490: * <p> The 'bKGD' chunk will encode this information.
0491: */
0492: public void setBackgroundRGB(int[] rgb) {
0493: if (rgb.length != 3) {
0494: throw new RuntimeException();
0495: }
0496: backgroundRGB = rgb;
0497: backgroundSet = true;
0498: }
0499:
0500: /**
0501: * Returns the RGB value of the suggested background color.
0502: *
0503: * <p> If the background color has not previously been set, or has been
0504: * unset, an <code>IllegalStateException</code> will be thrown.
0505: *
0506: * @throws IllegalStateException if the background color is not set.
0507: */
0508: public int[] getBackgroundRGB() {
0509: if (!backgroundSet) {
0510: throw new IllegalStateException(PropertyUtil
0511: .getString("PNGEncodeParam9"));
0512: }
0513: return backgroundRGB;
0514: }
0515:
0516: // tRNS chunk
0517:
0518: private int[] transparency;
0519:
0520: /**
0521: * Sets the RGB value to be used to denote transparency.
0522: *
0523: * <p> Setting this attribute will cause the alpha channel
0524: * of the input image to be ignored.
0525: *
0526: * <p> The 'tRNS' chunk will encode this information.
0527: */
0528: public void setTransparentRGB(int[] transparentRGB) {
0529: transparency = (int[]) (transparentRGB.clone());
0530: transparencySet = true;
0531: }
0532:
0533: /**
0534: * Returns the RGB value to be used to denote transparency.
0535: *
0536: * <p> If the transparent color has not previously been set,
0537: * or has been unset, an <code>IllegalStateException</code>
0538: * will be thrown.
0539: *
0540: * @throws IllegalStateException if the transparent color is not set.
0541: */
0542: public int[] getTransparentRGB() {
0543: if (!transparencySet) {
0544: throw new IllegalStateException(PropertyUtil
0545: .getString("PNGEncodeParam10"));
0546: }
0547: return (int[]) (transparency.clone());
0548: }
0549: }
0550:
0551: protected int bitDepth;
0552: protected boolean bitDepthSet = false;
0553:
0554: /**
0555: * Sets the desired bit depth of an image.
0556: */
0557: public abstract void setBitDepth(int bitDepth);
0558:
0559: /**
0560: * Returns the desired bit depth for a grayscale image.
0561: *
0562: * <p> If the bit depth has not previously been set, or has been
0563: * unset, an <code>IllegalStateException</code> will be thrown.
0564: *
0565: * @throws IllegalStateException if the bit depth is not set.
0566: */
0567: public int getBitDepth() {
0568: if (!bitDepthSet) {
0569: throw new IllegalStateException(PropertyUtil
0570: .getString("PNGEncodeParam11"));
0571: }
0572: return bitDepth;
0573: }
0574:
0575: /**
0576: * Suppresses the setting of the bit depth of a grayscale image.
0577: * The depth of the encoded image will be inferred from the source
0578: * image bit depth, rounded up to the next power of 2 between 1
0579: * and 16.
0580: */
0581: public void unsetBitDepth() {
0582: bitDepthSet = false;
0583: }
0584:
0585: private boolean useInterlacing = false;
0586:
0587: /**
0588: * Turns Adam7 interlacing on or off.
0589: */
0590: public void setInterlacing(boolean useInterlacing) {
0591: this .useInterlacing = useInterlacing;
0592: }
0593:
0594: /**
0595: * Returns <code>true</code> if Adam7 interlacing will be used.
0596: */
0597: public boolean getInterlacing() {
0598: return useInterlacing;
0599: }
0600:
0601: // bKGD chunk - delegate to subclasses
0602:
0603: // In JAI 1.0, 'backgroundSet' was private. The JDK 1.2 compiler
0604: // was lenient and incorrectly allowed this variable to be
0605: // accessed from the subclasses. The JDK 1.3 compiler correctly
0606: // flags this as a use of a non-static variable in a static
0607: // context. Changing 'backgroundSet' to protected would have
0608: // solved the problem, but would have introduced a visible API
0609: // change. Thus we are forced to adopt the solution of placing a
0610: // separate private variable in each subclass and providing
0611: // separate implementations of 'unsetBackground' and
0612: // 'isBackgroundSet' in each concrete subclass.
0613:
0614: /**
0615: * Suppresses the 'bKGD' chunk from being output.
0616: * For API compatibility with JAI 1.0, the superclass
0617: * defines this method to throw a <code>RuntimeException</code>;
0618: * accordingly, subclasses must provide their own implementations.
0619: */
0620: public void unsetBackground() {
0621: throw new RuntimeException(PropertyUtil
0622: .getString("PNGEncodeParam23"));
0623: }
0624:
0625: /**
0626: * Returns true if a 'bKGD' chunk will be output.
0627: * For API compatibility with JAI 1.0, the superclass
0628: * defines this method to throw a <code>RuntimeException</code>;
0629: * accordingly, subclasses must provide their own implementations.
0630: */
0631: public boolean isBackgroundSet() {
0632: throw new RuntimeException(PropertyUtil
0633: .getString("PNGEncodeParam24"));
0634: }
0635:
0636: // cHRM chunk
0637:
0638: private float[] chromaticity = null;
0639: private boolean chromaticitySet = false;
0640:
0641: /**
0642: * Sets the white point and primary chromaticities in CIE (x, y)
0643: * space.
0644: *
0645: * <p> The <code>chromaticity</code> parameter should be a
0646: * <code>float</code> array of length 8 containing the white point
0647: * X and Y, red X and Y, green X and Y, and blue X and Y values in
0648: * order.
0649: *
0650: * <p> The 'cHRM' chunk will encode this information.
0651: */
0652: public void setChromaticity(float[] chromaticity) {
0653: if (chromaticity.length != 8) {
0654: throw new IllegalArgumentException();
0655: }
0656: this .chromaticity = (float[]) (chromaticity.clone());
0657: chromaticitySet = true;
0658: }
0659:
0660: /**
0661: * A convenience method that calls the array version.
0662: */
0663: public void setChromaticity(float whitePointX, float whitePointY,
0664: float redX, float redY, float greenX, float greenY,
0665: float blueX, float blueY) {
0666: float[] chroma = new float[8];
0667: chroma[0] = whitePointX;
0668: chroma[1] = whitePointY;
0669: chroma[2] = redX;
0670: chroma[3] = redY;
0671: chroma[4] = greenX;
0672: chroma[5] = greenY;
0673: chroma[6] = blueX;
0674: chroma[7] = blueY;
0675: setChromaticity(chroma);
0676: }
0677:
0678: /**
0679: * Returns the white point and primary chromaticities in
0680: * CIE (x, y) space.
0681: *
0682: * <p> See the documentation for the <code>setChromaticity</code>
0683: * method for the format of the returned data.
0684: *
0685: * <p> If the chromaticity has not previously been set, or has been
0686: * unset, an <code>IllegalStateException</code> will be thrown.
0687: *
0688: * @throws IllegalStateException if the chromaticity is not set.
0689: */
0690: public float[] getChromaticity() {
0691: if (!chromaticitySet) {
0692: throw new IllegalStateException(PropertyUtil
0693: .getString("PNGEncodeParam12"));
0694: }
0695: return (float[]) (chromaticity.clone());
0696: }
0697:
0698: /**
0699: * Suppresses the 'cHRM' chunk from being output.
0700: */
0701: public void unsetChromaticity() {
0702: chromaticity = null;
0703: chromaticitySet = false;
0704: }
0705:
0706: /**
0707: * Returns true if a 'cHRM' chunk will be output.
0708: */
0709: public boolean isChromaticitySet() {
0710: return chromaticitySet;
0711: }
0712:
0713: // gAMA chunk
0714:
0715: private float gamma;
0716: private boolean gammaSet = false;
0717:
0718: /**
0719: * Sets the file gamma value for the image.
0720: *
0721: * <p> The 'gAMA' chunk will encode this information.
0722: */
0723: public void setGamma(float gamma) {
0724: this .gamma = gamma;
0725: gammaSet = true;
0726: }
0727:
0728: /**
0729: * Returns the file gamma value for the image.
0730: *
0731: * <p> If the file gamma has not previously been set, or has been
0732: * unset, an <code>IllegalStateException</code> will be thrown.
0733: *
0734: * @throws IllegalStateException if the gamma is not set.
0735: */
0736: public float getGamma() {
0737: if (!gammaSet) {
0738: throw new IllegalStateException(PropertyUtil
0739: .getString("PNGEncodeParam13"));
0740: }
0741: return gamma;
0742: }
0743:
0744: /**
0745: * Suppresses the 'gAMA' chunk from being output.
0746: */
0747: public void unsetGamma() {
0748: gammaSet = false;
0749: }
0750:
0751: /**
0752: * Returns true if a 'gAMA' chunk will be output.
0753: */
0754: public boolean isGammaSet() {
0755: return gammaSet;
0756: }
0757:
0758: // hIST chunk
0759:
0760: private int[] paletteHistogram = null;
0761: private boolean paletteHistogramSet = false;
0762:
0763: /**
0764: * Sets the palette histogram to be stored with this image.
0765: * The histogram consists of an array of integers, one per
0766: * palette entry.
0767: *
0768: * <p> The 'hIST' chunk will encode this information.
0769: */
0770: public void setPaletteHistogram(int[] paletteHistogram) {
0771: this .paletteHistogram = (int[]) (paletteHistogram.clone());
0772: paletteHistogramSet = true;
0773: }
0774:
0775: /**
0776: * Returns the palette histogram to be stored with this image.
0777: *
0778: * <p> If the histogram has not previously been set, or has been
0779: * unset, an <code>IllegalStateException</code> will be thrown.
0780: *
0781: * @throws IllegalStateException if the histogram is not set.
0782: */
0783: public int[] getPaletteHistogram() {
0784: if (!paletteHistogramSet) {
0785: throw new IllegalStateException(PropertyUtil
0786: .getString("PNGEncodeParam14"));
0787: }
0788: return paletteHistogram;
0789: }
0790:
0791: /**
0792: * Suppresses the 'hIST' chunk from being output.
0793: */
0794: public void unsetPaletteHistogram() {
0795: paletteHistogram = null;
0796: paletteHistogramSet = false;
0797: }
0798:
0799: /**
0800: * Returns true if a 'hIST' chunk will be output.
0801: */
0802: public boolean isPaletteHistogramSet() {
0803: return paletteHistogramSet;
0804: }
0805:
0806: // iCCP chunk
0807:
0808: private byte[] ICCProfileData = null;
0809: private boolean ICCProfileDataSet = false;
0810:
0811: /**
0812: * Sets the ICC profile data to be stored with this image.
0813: * The profile is represented in raw binary form.
0814: *
0815: * <p> The 'iCCP' chunk will encode this information.
0816: */
0817: public void setICCProfileData(byte[] ICCProfileData) {
0818: this .ICCProfileData = (byte[]) (ICCProfileData.clone());
0819: ICCProfileDataSet = true;
0820: }
0821:
0822: /**
0823: * Returns the ICC profile data to be stored with this image.
0824: *
0825: * <p> If the ICC profile has not previously been set, or has been
0826: * unset, an <code>IllegalStateException</code> will be thrown.
0827: *
0828: * @throws IllegalStateException if the ICC profile is not set.
0829: */
0830: public byte[] getICCProfileData() {
0831: if (!ICCProfileDataSet) {
0832: throw new IllegalStateException(PropertyUtil
0833: .getString("PNGEncodeParam15"));
0834: }
0835: return (byte[]) (ICCProfileData.clone());
0836: }
0837:
0838: /**
0839: * Suppresses the 'iCCP' chunk from being output.
0840: */
0841: public void unsetICCProfileData() {
0842: ICCProfileData = null;
0843: ICCProfileDataSet = false;
0844: }
0845:
0846: /**
0847: * Returns true if a 'iCCP' chunk will be output.
0848: */
0849: public boolean isICCProfileDataSet() {
0850: return ICCProfileDataSet;
0851: }
0852:
0853: // pHYS chunk
0854:
0855: private int[] physicalDimension = null;
0856: private boolean physicalDimensionSet = false;
0857:
0858: /**
0859: * Sets the physical dimension information to be stored with this
0860: * image. The physicalDimension parameter should be a 3-entry
0861: * array containing the number of pixels per unit in the X
0862: * direction, the number of pixels per unit in the Y direction,
0863: * and the unit specifier (0 = unknown, 1 = meters).
0864: *
0865: * <p> The 'pHYS' chunk will encode this information.
0866: */
0867: public void setPhysicalDimension(int[] physicalDimension) {
0868: this .physicalDimension = (int[]) (physicalDimension.clone());
0869: physicalDimensionSet = true;
0870: }
0871:
0872: /**
0873: * A convenience method that calls the array version.
0874: */
0875: public void setPhysicalDimension(int xPixelsPerUnit,
0876: int yPixelsPerUnit, int unitSpecifier) {
0877: int[] pd = new int[3];
0878: pd[0] = xPixelsPerUnit;
0879: pd[1] = yPixelsPerUnit;
0880: pd[2] = unitSpecifier;
0881:
0882: setPhysicalDimension(pd);
0883: }
0884:
0885: /**
0886: * Returns the physical dimension information to be stored
0887: * with this image.
0888: *
0889: * <p> If the physical dimension information has not previously
0890: * been set, or has been unset, an
0891: * <code>IllegalStateException</code> will be thrown.
0892: *
0893: * @throws IllegalStateException if the physical dimension information
0894: * is not set.
0895: */
0896: public int[] getPhysicalDimension() {
0897: if (!physicalDimensionSet) {
0898: throw new IllegalStateException(PropertyUtil
0899: .getString("PNGEncodeParam16"));
0900: }
0901: return (int[]) (physicalDimension.clone());
0902: }
0903:
0904: /**
0905: * Suppresses the 'pHYS' chunk from being output.
0906: */
0907: public void unsetPhysicalDimension() {
0908: physicalDimension = null;
0909: physicalDimensionSet = false;
0910: }
0911:
0912: /**
0913: * Returns true if a 'pHYS' chunk will be output.
0914: */
0915: public boolean isPhysicalDimensionSet() {
0916: return physicalDimensionSet;
0917: }
0918:
0919: // sPLT chunk
0920:
0921: private PNGSuggestedPaletteEntry[] suggestedPalette = null;
0922: private boolean suggestedPaletteSet = false;
0923:
0924: /**
0925: * Sets the suggested palette information to be stored with this
0926: * image. The information is passed to this method as an array of
0927: * <code>PNGSuggestedPaletteEntry</code> objects.
0928: *
0929: * <p> The 'sPLT' chunk will encode this information.
0930: */
0931: public void setSuggestedPalette(PNGSuggestedPaletteEntry[] palette) {
0932: suggestedPalette = (PNGSuggestedPaletteEntry[]) (palette
0933: .clone());
0934: suggestedPaletteSet = true;
0935: }
0936:
0937: /**
0938: * Returns the suggested palette information to be stored with this
0939: * image.
0940: *
0941: * <p> If the suggested palette information has not previously
0942: * been set, or has been unset, an
0943: * <code>IllegalStateException</code> will be thrown.
0944: *
0945: * @throws IllegalStateException if the suggested palette
0946: * information is not set.
0947: */
0948: public PNGSuggestedPaletteEntry[] getSuggestedPalette() {
0949: if (!suggestedPaletteSet) {
0950: throw new IllegalStateException(PropertyUtil
0951: .getString("PNGEncodeParam17"));
0952: }
0953: return (PNGSuggestedPaletteEntry[]) (suggestedPalette.clone());
0954: }
0955:
0956: /**
0957: * Suppresses the 'sPLT' chunk from being output.
0958: */
0959: public void unsetSuggestedPalette() {
0960: suggestedPalette = null;
0961: suggestedPaletteSet = false;
0962: }
0963:
0964: /**
0965: * Returns true if a 'sPLT' chunk will be output.
0966: */
0967: public boolean isSuggestedPaletteSet() {
0968: return suggestedPaletteSet;
0969: }
0970:
0971: // sBIT chunk
0972:
0973: private int[] significantBits = null;
0974: private boolean significantBitsSet = false;
0975:
0976: /**
0977: * Sets the number of significant bits for each band of the image.
0978: *
0979: * <p> The number of entries in the <code>significantBits</code>
0980: * array must be equal to the number of output bands in the image:
0981: * 1 for a gray image, 2 for gray+alpha, 3 for index or truecolor,
0982: * and 4 for truecolor+alpha.
0983: *
0984: * <p> The 'sBIT' chunk will encode this information.
0985: */
0986: public void setSignificantBits(int[] significantBits) {
0987: this .significantBits = (int[]) (significantBits.clone());
0988: significantBitsSet = true;
0989: }
0990:
0991: /**
0992: * Returns the number of significant bits for each band of the image.
0993: *
0994: * <p> If the significant bits values have not previously been
0995: * set, or have been unset, an <code>IllegalStateException</code>
0996: * will be thrown.
0997: *
0998: * @throws IllegalStateException if the significant bits values are
0999: * not set.
1000: */
1001: public int[] getSignificantBits() {
1002: if (!significantBitsSet) {
1003: throw new IllegalStateException(PropertyUtil
1004: .getString("PNGEncodeParam18"));
1005: }
1006: return (int[]) significantBits.clone();
1007: }
1008:
1009: /**
1010: * Suppresses the 'sBIT' chunk from being output.
1011: */
1012: public void unsetSignificantBits() {
1013: significantBits = null;
1014: significantBitsSet = false;
1015: }
1016:
1017: /**
1018: * Returns true if an 'sBIT' chunk will be output.
1019: */
1020: public boolean isSignificantBitsSet() {
1021: return significantBitsSet;
1022: }
1023:
1024: // sRGB chunk
1025:
1026: private int SRGBIntent;
1027: private boolean SRGBIntentSet = false;
1028:
1029: /**
1030: * Sets the sRGB rendering intent to be stored with this image.
1031: * The legal values are 0 = Perceptual, 1 = Relative Colorimetric,
1032: * 2 = Saturation, and 3 = Absolute Colorimetric. Refer to the
1033: * PNG specification for information on these values.
1034: *
1035: * <p> The 'sRGB' chunk will encode this information.
1036: */
1037: public void setSRGBIntent(int SRGBIntent) {
1038: this .SRGBIntent = SRGBIntent;
1039: SRGBIntentSet = true;
1040: }
1041:
1042: /**
1043: * Returns the sRGB rendering intent to be stored with this image.
1044: *
1045: * <p> If the sRGB intent has not previously been set, or has been
1046: * unset, an <code>IllegalStateException</code> will be thrown.
1047: *
1048: * @throws IllegalStateException if the sRGB intent is not set.
1049: */
1050: public int getSRGBIntent() {
1051: if (!SRGBIntentSet) {
1052: throw new IllegalStateException(PropertyUtil
1053: .getString("PNGEncodeParam19"));
1054: }
1055: return SRGBIntent;
1056: }
1057:
1058: /**
1059: * Suppresses the 'sRGB' chunk from being output.
1060: */
1061: public void unsetSRGBIntent() {
1062: SRGBIntentSet = false;
1063: }
1064:
1065: /**
1066: * Returns true if an 'sRGB' chunk will be output.
1067: */
1068: public boolean isSRGBIntentSet() {
1069: return SRGBIntentSet;
1070: }
1071:
1072: // tEXt chunk
1073:
1074: private String[] text = null;
1075: private boolean textSet = false;
1076:
1077: /**
1078: * Sets the textual data to be stored in uncompressed form with this
1079: * image. The data is passed to this method as an array of
1080: * <code>String</code>s.
1081: *
1082: * <p> The 'tEXt' chunk will encode this information.
1083: */
1084: public void setText(String[] text) {
1085: this .text = text;
1086: textSet = true;
1087: }
1088:
1089: /**
1090: * Returns the text strings to be stored in uncompressed form with this
1091: * image as an array of <code>String</code>s.
1092: *
1093: * <p> If the text strings have not previously been set, or have been
1094: * unset, an <code>IllegalStateException</code> will be thrown.
1095: *
1096: * @throws IllegalStateException if the text strings are not set.
1097: */
1098: public String[] getText() {
1099: if (!textSet) {
1100: throw new IllegalStateException(PropertyUtil
1101: .getString("PNGEncodeParam20"));
1102: }
1103: return text;
1104: }
1105:
1106: /**
1107: * Suppresses the 'tEXt' chunk from being output.
1108: */
1109: public void unsetText() {
1110: text = null;
1111: textSet = false;
1112: }
1113:
1114: /**
1115: * Returns true if a 'tEXt' chunk will be output.
1116: */
1117: public boolean isTextSet() {
1118: return textSet;
1119: }
1120:
1121: // tIME chunk
1122:
1123: private Date modificationTime;
1124: private boolean modificationTimeSet = false;
1125:
1126: /**
1127: * Sets the modification time, as a <code>Date</code>, to be
1128: * stored with this image. The internal storage format will use
1129: * UTC regardless of how the <code>modificationTime</code>
1130: * parameter was created.
1131: *
1132: * <p> The 'tIME' chunk will encode this information.
1133: */
1134: public void setModificationTime(Date modificationTime) {
1135: this .modificationTime = modificationTime;
1136: modificationTimeSet = true;
1137: }
1138:
1139: /**
1140: * Returns the modification time to be stored with this image.
1141: *
1142: * <p> If the bit depth has not previously been set, or has been
1143: * unset, an <code>IllegalStateException</code> will be thrown.
1144: *
1145: * @throws IllegalStateException if the bit depth is not set.
1146: */
1147: public Date getModificationTime() {
1148: if (!modificationTimeSet) {
1149: throw new IllegalStateException(PropertyUtil
1150: .getString("PNGEncodeParam21"));
1151: }
1152: return modificationTime;
1153: }
1154:
1155: /**
1156: * Suppresses the 'tIME' chunk from being output.
1157: */
1158: public void unsetModificationTime() {
1159: modificationTime = null;
1160: modificationTimeSet = false;
1161: }
1162:
1163: /**
1164: * Returns true if a 'tIME' chunk will be output.
1165: */
1166: public boolean isModificationTimeSet() {
1167: return modificationTimeSet;
1168: }
1169:
1170: // tRNS chunk
1171:
1172: boolean transparencySet = false;
1173:
1174: /**
1175: * Suppresses the 'tRNS' chunk from being output.
1176: */
1177: public void unsetTransparency() {
1178: transparencySet = false;
1179: }
1180:
1181: /**
1182: * Returns true if a 'tRNS' chunk will be output.
1183: */
1184: public boolean isTransparencySet() {
1185: return transparencySet;
1186: }
1187:
1188: // zTXT chunk
1189:
1190: private String[] zText = null;
1191: private boolean zTextSet = false;
1192:
1193: /**
1194: * Sets the text strings to be stored in compressed form with this
1195: * image. The data is passed to this method as an array of
1196: * <code>String</code>s.
1197: *
1198: * <p> The 'zTXt' chunk will encode this information.
1199: */
1200: public void setCompressedText(String[] text) {
1201: this .zText = text;
1202: zTextSet = true;
1203: }
1204:
1205: /**
1206: * Returns the text strings to be stored in compressed form with
1207: * this image as an array of <code>String</code>s.
1208: *
1209: * <p> If the compressed text strings have not previously been
1210: * set, or have been unset, an <code>IllegalStateException</code>
1211: * will be thrown.
1212: *
1213: * @throws IllegalStateException if the compressed text strings are
1214: * not set.
1215: */
1216: public String[] getCompressedText() {
1217: if (!zTextSet) {
1218: throw new IllegalStateException(PropertyUtil
1219: .getString("PNGEncodeParam22"));
1220: }
1221: return zText;
1222: }
1223:
1224: /**
1225: * Suppresses the 'zTXt' chunk from being output.
1226: */
1227: public void unsetCompressedText() {
1228: zText = null;
1229: zTextSet = false;
1230: }
1231:
1232: /**
1233: * Returns true if a 'zTXT' chunk will be output.
1234: */
1235: public boolean isCompressedTextSet() {
1236: return zTextSet;
1237: }
1238:
1239: // Other chunk types
1240:
1241: List chunkType = new ArrayList();
1242: List chunkData = new ArrayList();
1243:
1244: /**
1245: * Adds a private chunk, in binary form, to the list of chunks to
1246: * be stored with this image.
1247: *
1248: * @param type a 4-character String giving the chunk type name.
1249: * @param data an array of <code>byte</code>s containing the
1250: * chunk data.
1251: */
1252: public synchronized void addPrivateChunk(String type, byte[] data) {
1253: chunkType.add(type);
1254: chunkData.add(data.clone());
1255: }
1256:
1257: /**
1258: * Returns the number of private chunks to be written to the
1259: * output file.
1260: */
1261: public synchronized int getNumPrivateChunks() {
1262: return chunkType.size();
1263: }
1264:
1265: /**
1266: * Returns the type of the private chunk at a given index, as a
1267: * 4-character <code>String</code>. The index must be smaller
1268: * than the return value of <code>getNumPrivateChunks</code>.
1269: */
1270: public synchronized String getPrivateChunkType(int index) {
1271: return (String) chunkType.get(index);
1272: }
1273:
1274: /**
1275: * Returns the data associated of the private chunk at a given
1276: * index, as an array of <code>byte</code>s. The index must be
1277: * smaller than the return value of
1278: * <code>getNumPrivateChunks</code>.
1279: */
1280: public synchronized byte[] getPrivateChunkData(int index) {
1281: return (byte[]) chunkData.get(index);
1282: }
1283:
1284: /**
1285: * Remove all private chunks associated with this parameter instance
1286: * whose 'safe-to-copy' bit is not set. This may be advisable when
1287: * transcoding PNG images.
1288: */
1289: public synchronized void removeUnsafeToCopyPrivateChunks() {
1290: List newChunkType = new ArrayList();
1291: List newChunkData = new ArrayList();
1292:
1293: int len = getNumPrivateChunks();
1294: for (int i = 0; i < len; i++) {
1295: String type = getPrivateChunkType(i);
1296: char lastChar = type.charAt(3);
1297: if (lastChar >= 'a' && lastChar <= 'z') {
1298: newChunkType.add(type);
1299: newChunkData.add(getPrivateChunkData(i));
1300: }
1301: }
1302:
1303: chunkType = newChunkType;
1304: chunkData = newChunkData;
1305: }
1306:
1307: /**
1308: * Remove all private chunks associated with this parameter instance.
1309: */
1310: public synchronized void removeAllPrivateChunks() {
1311: chunkType = new ArrayList();
1312: chunkData = new ArrayList();
1313: }
1314:
1315: /**
1316: * An abs() function for use by the Paeth predictor.
1317: */
1318: private static final int abs(int x) {
1319: return (x < 0) ? -x : x;
1320: }
1321:
1322: /**
1323: * The Paeth predictor routine used in PNG encoding. This routine
1324: * is included as a convenience to subclasses that override the
1325: * <code>filterRow</code> method.
1326: */
1327: public static final int paethPredictor(int a, int b, int c) {
1328: int p = a + b - c;
1329: int pa = abs(p - a);
1330: int pb = abs(p - b);
1331: int pc = abs(p - c);
1332:
1333: if ((pa <= pb) && (pa <= pc)) {
1334: return a;
1335: } else if (pb <= pc) {
1336: return b;
1337: } else {
1338: return c;
1339: }
1340: }
1341:
1342: /**
1343: * Performs filtering on a row of an image. This method may be
1344: * overridden in order to provide a custom algorithm for choosing
1345: * the filter type for a given row.
1346: *
1347: * <p> The method is supplied with the current and previous rows
1348: * of the image. For the first row of the image, or of an
1349: * interlacing pass, the previous row array will be filled with
1350: * zeros as required by the PNG specification.
1351: *
1352: * <p> The method is also supplied with five scratch arrays.
1353: * These arrays may be used within the method for any purpose.
1354: * At method exit, the array at the index given by the return
1355: * value of the method should contain the filtered data. The
1356: * return value will also be used as the filter type.
1357: *
1358: * <p> The default implementation of the method performs a trial
1359: * encoding with each of the filter types, and computes the sum of
1360: * absolute values of the differences between the raw bytes of the
1361: * current row and the predicted values. The index of the filter
1362: * producing the smallest result is returned.
1363: *
1364: * <p> As an example, to perform only 'sub' filtering, this method
1365: * could be implemented (non-optimally) as follows:
1366: *
1367: * <pre>
1368: * for (int i = bytesPerPixel; i < bytesPerRow + bytesPerPixel; i++) {
1369: * int curr = currRow[i] & 0xff;
1370: * int left = currRow[i - bytesPerPixel] & 0xff;
1371: * scratchRow[PNG_FILTER_SUB][i] = (byte)(curr - left);
1372: * }
1373: * return PNG_FILTER_SUB;
1374: * </pre>
1375: *
1376: * @param currRow The current row as an array of <code>byte</code>s
1377: * of length at least <code>bytesPerRow + bytesPerPixel</code>.
1378: * The pixel data starts at index <code>bytesPerPixel</code>;
1379: * the initial <code>bytesPerPixel</code> bytes are zero.
1380: * @param prevRow The current row as an array of <code>byte</code>s
1381: * The pixel data starts at index <code>bytesPerPixel</code>;
1382: * the initial <code>bytesPerPixel</code> bytes are zero.
1383: * @param scratchRows An array of 5 <code>byte</code> arrays of
1384: * length at least <code>bytesPerRow +
1385: * bytesPerPixel</code>, useable to hold temporary results.
1386: * The filtered row will be returned as one of the entries
1387: * of this array. The returned filtered data should start
1388: * at index <code>bytesPerPixel</code>; The initial
1389: * <code>bytesPerPixel</code> bytes are not used.
1390: * @param bytesPerRow The number of bytes in the image row.
1391: * This value will always be greater than 0.
1392: * @param bytesPerPixel The number of bytes representing a single
1393: * pixel, rounded up to an integer. This is the 'bpp' parameter
1394: * described in the PNG specification.
1395: *
1396: * @return The filter type to be used. The entry of
1397: * <code>scratchRows[]</code> at this index holds the
1398: * filtered data. */
1399: public int filterRow(byte[] currRow, byte[] prevRow,
1400: byte[][] scratchRows, int bytesPerRow, int bytesPerPixel) {
1401:
1402: int[] badness = { 0, 0, 0, 0, 0 };
1403: int curr, left, up, upleft, diff;
1404: int pa, pb, pc;
1405: for (int i = bytesPerPixel; i < bytesPerRow + bytesPerPixel; i++) {
1406: curr = currRow[i] & 0xff;
1407: left = currRow[i - bytesPerPixel] & 0xff;
1408: up = prevRow[i] & 0xff;
1409: upleft = prevRow[i - bytesPerPixel] & 0xff;
1410:
1411: // no filter
1412: badness[0] += curr;
1413:
1414: // sub filter
1415: diff = curr - left;
1416: scratchRows[1][i] = (byte) diff;
1417: badness[1] += (diff > 0) ? diff : -diff;
1418:
1419: // up filter
1420: diff = curr - up;
1421: scratchRows[2][i] = (byte) diff;
1422: badness[2] += (diff >= 0) ? diff : -diff;
1423:
1424: // average filter
1425: diff = curr - ((left + up) >> 1);
1426: scratchRows[3][i] = (byte) diff;
1427: badness[3] += (diff >= 0) ? diff : -diff;
1428:
1429: // paeth filter
1430:
1431: // Original code much simplier but doesn't take full
1432: // advantage of relationship between pa/b/c and
1433: // information gleaned in abs operations.
1434: /// pa = up -upleft;
1435: /// pb = left-upleft;
1436: /// pc = pa+pb;
1437: /// pa = abs(pa);
1438: /// pb = abs(pb);
1439: /// pc = abs(pc);
1440: /// if ((pa <= pb) && (pa <= pc))
1441: /// diff = curr-left;
1442: /// else if (pb <= pc)
1443: /// diff = curr-up;
1444: /// else
1445: /// diff = curr-upleft;
1446:
1447: pa = up - upleft;
1448: pb = left - upleft;
1449: if (pa < 0) {
1450: if (pb < 0) {
1451: // both pa & pb neg so pc is always greater than or
1452: // equal to pa or pb;
1453: if (pa >= pb) // since pa & pb neg check sense is reversed.
1454: diff = curr - left;
1455: else
1456: diff = curr - up;
1457: } else {
1458: // pa neg pb pos so we must compute pc...
1459: pc = pa + pb;
1460: pa = -pa;
1461: if (pa <= pb) // pc is positive and less than pb
1462: if (pa <= pc)
1463: diff = curr - left;
1464: else
1465: diff = curr - upleft;
1466: else
1467: // pc is negative and less than or equal to pa,
1468: // but since pa is greater than pb this isn't an issue...
1469: if (pb <= -pc)
1470: diff = curr - up;
1471: else
1472: diff = curr - upleft;
1473: }
1474: } else {
1475: if (pb < 0) {
1476: pb = -pb; // make it positive...
1477: if (pa <= pb) {
1478: // pc would be negative and less than or equal to pb
1479: pc = pb - pa;
1480: if (pa <= pc)
1481: diff = curr - left;
1482: else if (pb == pc)
1483: // if pa is zero then pc==pb otherwise
1484: // pc must be less than pb.
1485: diff = curr - up;
1486: else
1487: diff = curr - upleft;
1488: } else {
1489: // pc would be positive and less than pa.
1490: pc = pa - pb;
1491: if (pb <= pc)
1492: diff = curr - up;
1493: else
1494: diff = curr - upleft;
1495: }
1496: } else {
1497: // both pos so pa+pb is always greater than pa/pb
1498: if (pa <= pb)
1499: diff = curr - left;
1500: else
1501: diff = curr - up;
1502: }
1503: }
1504: scratchRows[4][i] = (byte) diff;
1505: badness[4] += (diff >= 0) ? diff : -diff;
1506: }
1507: int filterType = 0;
1508: int minBadness = badness[0];
1509:
1510: for (int i = 1; i < 5; i++) {
1511: if (badness[i] < minBadness) {
1512: minBadness = badness[i];
1513: filterType = i;
1514: }
1515: }
1516:
1517: if (filterType == 0) {
1518: System.arraycopy(currRow, bytesPerPixel, scratchRows[0],
1519: bytesPerPixel, bytesPerRow);
1520: }
1521:
1522: return filterType;
1523: }
1524: }
|