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