0001: /*
0002: * $Id: PNGHelper.java,v 1.3 2002/06/27 21:49:17 awason Exp $
0003: *
0004: * ===========================================================================
0005: *
0006: * The JGenerator Software License, Version 1.0
0007: *
0008: * Copyright (c) 2000 Dmitry Skavish (skavish@usa.net). All rights reserved.
0009: *
0010: * Redistribution and use in source and binary forms, with or without
0011: * modification, are permitted provided that the following conditions are met:
0012: *
0013: * 1. Redistributions of source code must retain the above copyright
0014: * notice, this list of conditions and the following disclaimer.
0015: *
0016: * 2. Redistributions in binary form must reproduce the above copyright
0017: * notice, this list of conditions and the following disclaimer in
0018: * the documentation and/or other materials provided with the
0019: * distribution.
0020: *
0021: * 3. The end-user documentation included with the redistribution, if
0022: * any, must include the following acknowlegement:
0023: * "This product includes software developed by Dmitry Skavish
0024: * (skavish@usa.net, http://www.flashgap.com/)."
0025: * Alternately, this acknowlegement may appear in the software itself,
0026: * if and wherever such third-party acknowlegements normally appear.
0027: *
0028: * 4. The name "The JGenerator" must not be used to endorse or promote
0029: * products derived from this software without prior written permission.
0030: * For written permission, please contact skavish@usa.net.
0031: *
0032: * 5. Products derived from this software may not be called "The JGenerator"
0033: * nor may "The JGenerator" appear in their names without prior written
0034: * permission of Dmitry Skavish.
0035: *
0036: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
0037: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
0038: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
0039: * DISCLAIMED. IN NO EVENT SHALL DMITRY SKAVISH OR THE OTHER
0040: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
0041: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
0042: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
0043: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
0044: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
0045: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
0046: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
0047: * SUCH DAMAGE.
0048: *
0049: */
0050:
0051: package org.openlaszlo.iv.flash.util;
0052:
0053: import java.io.EOFException;
0054: import java.io.IOException;
0055: import java.io.InputStream;
0056: import java.io.DataInputStream;
0057: import java.io.BufferedInputStream;
0058: import java.io.ByteArrayInputStream;
0059: import java.util.zip.InflaterInputStream;
0060: import java.util.zip.Deflater;
0061: import java.util.zip.CRC32;
0062: import java.util.Hashtable;
0063: import org.openlaszlo.iv.flash.parser.DataMarker;
0064:
0065: /**
0066: * Helper class to decode PNG images.
0067: * Does read but not support gAMA chunk (yet :-)
0068: * @author Patrick Talbot
0069: */
0070: public class PNGHelper {
0071: /** header chunk */
0072: private static final int CHUNK_IHDR = 0x49484452;
0073: /** palette chunk */
0074: private static final int CHUNK_PLTE = 0x504c5445;
0075: /** data chunk */
0076: private static final int CHUNK_IDAT = 0x49444154;
0077: /** end of file chunk */
0078: private static final int CHUNK_IEND = 0x49454e44;
0079: /** transparency chunk */
0080: private static final int CHUNK_tRNS = 0x74524e53;
0081:
0082: /** background (ignored) chunk */
0083: private static final int CHUNK_bKGD = 0x624b4744;
0084: /** chromaticities (ignored) chunk */
0085: private static final int CHUNK_cHRM = 0x6348524d;
0086: /** fractal image parameters (ignored) chunk */
0087: private static final int CHUNK_fRAc = 0x66524163;
0088: /** gamma (ignored) chunk */
0089: private static final int CHUNK_gAMA = 0x67414d41;
0090: /** GIF graphic control (ignored) chunk */
0091: private static final int CHUNK_gIFg = 0x67494667;
0092: /** GIF plain text (ignored) chunk */
0093: private static final int CHUNK_gIFt = 0x67494674;
0094: /** GIF application (ignored) chunk */
0095: private static final int CHUNK_gIFx = 0x67494678;
0096: /** histogram (ignored) chunk */
0097: private static final int CHUNK_hIST = 0x68495354;
0098: /** embedded ICC profile (ignored) chunk */
0099: private static final int CHUNK_iCCP = 0x69434350;
0100: /** unicode UTF-8 text (ignored) chunk */
0101: private static final int CHUNK_iTXt = 0x69545874;
0102: /** image offset (ignored) chunk */
0103: private static final int CHUNK_oFFs = 0x6f464673;
0104: /** calibration (ignored) chunk */
0105: private static final int CHUNK_pCAL = 0x7043414c;
0106: /** physical pixel dimension (ignored) chunk */
0107: private static final int CHUNK_pHYs = 0x70485973;
0108: /** significant bits (ignored) chunk */
0109: private static final int CHUNK_sBIT = 0x73424954;
0110: /** physical scale (ignored) chunk */
0111: private static final int CHUNK_sCAL = 0x7343414c;
0112: /** suggested palette (ignored) chunk */
0113: private static final int CHUNK_sPLT = 0x73504c54;
0114: /** standard RGB colour space (ignored) chunk */
0115: private static final int CHUNK_sRGB = 0x73524742;
0116: /** normal text (ignored) chunk */
0117: private static final int CHUNK_tEXt = 0x74455874;
0118: /** time stamp (ignored) chunk */
0119: private static final int CHUNK_tIME = 0x74494d45;
0120: /** compressed text (ignored) chunk */
0121: private static final int CHUNK_zTXt = 0x7a545874;
0122:
0123: /** PNG Signature */
0124: private static final int[] PNG_SIGN = { 0x89, 0x50, 0x4e, 0x47,
0125: 0x0d, 0x0a, 0x1a, 0x0a };
0126:
0127: /** a flag to test if the header chunk has been encountered */
0128: private boolean flagIHDR = false;
0129: /** a flag to test if data chunks have been encountered */
0130: private boolean flagIDAT = false;
0131: /** a flag to test if the palette chunk has been encountered */
0132: private boolean flagPLTE = false;
0133:
0134: /** flag used to indicate that further computing is need to manage transparency */
0135: private boolean tRNSPassRequired = false;
0136: /** transparency color for colorType 0 and 2 */
0137: private RGB tRNSKeyColor = null;
0138: /** used to determine if transparency chunk exists */
0139: private boolean flagtRNS = false;
0140:
0141: /** number of bytes used per pixel */
0142: private int colorType_components = -1;
0143:
0144: /** compression method should always be 0 = deflate/inflate, */
0145: private int compressionMethod = -1;
0146: /** filter method can be
0147: * 0 = none
0148: * 1 = sub
0149: * 2 = up
0150: * 3 = average
0151: * 4 = paeth
0152: */
0153: private int filterMethod = -1;
0154: /** interlace method can be
0155: * 0 = not interlace
0156: * 1 = Adam7 interlacing
0157: */
0158: private int interlaceMethod = -1;
0159:
0160: /** width of image*/
0161: private int width = -1;
0162: /** height of image */
0163: private int height = -1;
0164: /** pixels bit depth can be 1,2,4,8,16 */
0165: private int bitDepth = -1;
0166:
0167: /** gamma value of image, set to default value */
0168: private long gamma = 45455;
0169:
0170: /** color type can be
0171: * 0 = gray
0172: * 2 = RGB
0173: * 3 = paletted
0174: * 4 = gray + alpha
0175: * 6 = RGB + alpha
0176: */
0177: private int colorType = -1;
0178:
0179: /** flag set if palette is used */
0180: private boolean colorType_hasPalette = false;
0181: /** flag set if alpha channel exists for colorType = 4 or 6 */
0182: private boolean colorType_hasAlpha = false;
0183: /** flag set if not gray */
0184: private boolean colorType_hasColor = false;
0185:
0186: /** RGB Palette */
0187: private RGB[] palette;
0188:
0189: /** size of the palette */
0190: private int colorsUsed = 0;
0191:
0192: /** main IDAT buffer */
0193: private byte[] buffer;
0194:
0195: /** main filters per row buffer */
0196: //private int[] filters;
0197: /** Stream to read data from */
0198: private CRCInputStream source;
0199:
0200: /**
0201: * A private input stream class used to read multi-byte integer values
0202: * while maintaining CRC code for read bytes.
0203: * Provides utility methods to read multi-byte integer values
0204: * in both little- and bit-endian orders.
0205: */
0206: //private
0207: public class CRCInputStream extends DataInputStream {
0208: /** crc checker */
0209: private CRC32 crc = new CRC32();
0210:
0211: /**
0212: * Constructor
0213: * @param inputStream java.io.InputStream
0214: */
0215: public CRCInputStream(InputStream inputStream) {
0216: super (inputStream);
0217: }
0218:
0219: /**
0220: * Return the crc value
0221: * @return long
0222: */
0223: public long getCRC() {
0224: return crc.getValue();
0225: }
0226:
0227: /**
0228: * Reset the crc checker
0229: */
0230: public void reset() {
0231: crc.reset();
0232: }
0233:
0234: /**
0235: * Read one int and ensure the crc is updated
0236: * @return int
0237: */
0238: public int read() throws IOException {
0239: int x = super .read();
0240: crc.update(x);
0241: return x;
0242: }
0243:
0244: /**
0245: * Read two ints
0246: * @return int
0247: */
0248: public int read2() throws IOException, EOFException {
0249: int a = read();
0250: int b = read();
0251: return a | b << 8;
0252: }
0253:
0254: /**
0255: * Read two ints in reverse order
0256: * @return int
0257: */
0258: public int read2n() throws IOException, EOFException {
0259: int b = read();
0260: int a = read();
0261: return a | b << 8;
0262: }
0263:
0264: /**
0265: * Read a long in reverse order
0266: * @return long
0267: */
0268: public long read4n() throws IOException, EOFException {
0269: long b = (long) read2n();
0270: long a = (long) read2n();
0271: return a | b << 16;
0272: }
0273: }
0274:
0275: /**
0276: * Private wrapper class for colors in RGBA float format
0277: * Provides utilities to set Color from different input format
0278: * And utilities to retrive them accordingly
0279: */
0280: //private
0281: public class RGB {
0282: /** represents 15 bits format */
0283: private static final int FORMAT_RGB5 = 0x8050;
0284: /** represents 24 bits format */
0285: private static final int FORMAT_RGB8 = 0x8051;
0286: /** represents 16 bits format */
0287: private static final int FORMAT_RGB5_A1 = 0x8057;
0288: /** represents 32 bits format */
0289: private static final int FORMAT_RGBA8 = 0x8058;
0290:
0291: /** used to define color without alpha information */
0292: private static final int NO_ALPHA = -1;
0293:
0294: /** default alpha value */
0295: private static final float DEFAULT_ALPHA = 1.0f;
0296: /** used to define color with default format */
0297: private static final float DEFAULT_FORMAT = 255.0f;
0298:
0299: /** red value */
0300: private float red;
0301: /** green value */
0302: private float green;
0303: /** blue value */
0304: private float blue;
0305: /** alpha value */
0306: private float alpha;
0307:
0308: /**
0309: * float gray constructor
0310: * @param gray float
0311: */
0312: public RGB(float gray) {
0313: set(gray, gray, gray, DEFAULT_ALPHA);
0314: }
0315:
0316: /**
0317: * int gray constructor
0318: * @param gray int
0319: */
0320: public RGB(int gray) {
0321: set(gray, gray, gray, NO_ALPHA, NO_ALPHA);
0322: }
0323:
0324: /**
0325: * int gray + alpha constructor
0326: * @param gray int
0327: * @param a int
0328: */
0329: public RGB(int gray, int a) {
0330: set(gray, gray, gray, a, DEFAULT_FORMAT);
0331: }
0332:
0333: /**
0334: * float gray + alpha constructor
0335: * @param gray float
0336: * @param a float
0337: */
0338: public RGB(float gray, float a) {
0339: set(gray, gray, gray, a);
0340: }
0341:
0342: /**
0343: * float r,g,b constructor
0344: * @param r float
0345: * @param g float
0346: * @param b float
0347: */
0348: public RGB(float r, float g, float b) {
0349: set(r, g, b, DEFAULT_ALPHA);
0350: }
0351:
0352: /**
0353: * int r,g,b constructor
0354: * @param r int
0355: * @param g int
0356: * @param b int
0357: */
0358: public RGB(int r, int g, int b) {
0359: set(r, g, b, NO_ALPHA, NO_ALPHA);
0360: }
0361:
0362: /**
0363: * int r,g,b + alpha constructor
0364: * @param r int
0365: * @param g int
0366: * @param b int
0367: * @param a int
0368: */
0369: public RGB(int r, int g, int b, int a) {
0370: set((float) r / DEFAULT_FORMAT, (float) g / DEFAULT_FORMAT,
0371: (float) b / DEFAULT_FORMAT, (float) a
0372: / DEFAULT_FORMAT);
0373: }
0374:
0375: /**
0376: * float r,g,b + alpha constructor
0377: * @param r float
0378: * @param g float
0379: * @param b float
0380: * @param a float
0381: */
0382: public RGB(float r, float g, float b, float a) {
0383: set(r, g, b, a);
0384: }
0385:
0386: /**
0387: * general float r,g,b + alpha setter
0388: * @param r float
0389: * @param g float
0390: * @param b float
0391: * @param a float
0392: */
0393: public void set(float r, float g, float b, float a) {
0394: red = r > 1.0f ? 1.0f : (r < 0.0f ? 0.0f : r);
0395: green = g > 1.0f ? 1.0f : (g < 0.0f ? 0.0f : g);
0396: blue = b > 1.0f ? 1.0f : (b < 0.0f ? 0.0f : b);
0397: alpha = a > 1.0f ? 1.0f : (a < 0.0f ? 0.0f : a);
0398: }
0399:
0400: /**
0401: * general int r,g,b + alpha setter
0402: * @param r int
0403: * @param g int
0404: * @param b int
0405: * @param a int
0406: * @param format float
0407: */
0408: public void set(int r, int g, int b, int a, float format) {
0409: set((float) r / DEFAULT_FORMAT, (float) g / DEFAULT_FORMAT,
0410: (float) b / DEFAULT_FORMAT,
0411: format == NO_ALPHA ? DEFAULT_ALPHA : (float) a
0412: / format);
0413: }
0414:
0415: /**
0416: * Return an int array of color packed in the requested format
0417: * @param format int
0418: * @return int[]
0419: */
0420: public int[] getPacked(int format) {
0421: int r;
0422: int g;
0423: int b;
0424: switch (format) {
0425: case FORMAT_RGB5:
0426: r = (int) (red * 32.0f);
0427: g = (int) (green * 32.0f);
0428: b = (int) (blue * 32.0f);
0429: return new int[] { r + (g & 0x07) << 5,
0430: (g & 0x18) >> 3 + (b & 0x1f) << 2 };
0431: case FORMAT_RGB8:
0432: return new int[] { (int) (red * 255.0f),
0433: (int) (green * 255.0f), (int) (blue * 255.0f) };
0434: case FORMAT_RGB5_A1:
0435: r = (int) (red * 32.0f);
0436: g = (int) (green * 32.0f);
0437: b = (int) (blue * 32.0f);
0438: return new int[] {
0439: r + (g & 0x07) << 5,
0440: (g & 0x18) >> 3 + (b & 0x1f) << 2 + (alpha > 0.5f ? 128
0441: : 0) };
0442: case FORMAT_RGBA8:
0443: return new int[] { (int) (red * 255.0f),
0444: (int) (green * 255.0f), (int) (blue * 255.0f),
0445: (int) (alpha * 255.0f) };
0446: default:
0447: throw new IllegalArgumentException(
0448: "Unknown color format " + format);
0449: }
0450: }
0451:
0452: /*
0453: * get the red value as a float
0454: * @return float
0455: */
0456: public float getRed() {
0457: return red;
0458: }
0459:
0460: /*
0461: * get the green value as a float
0462: * @return float
0463: */
0464: public float getGreen() {
0465: return green;
0466: }
0467:
0468: /*
0469: * get the blue value as a float
0470: * @return float
0471: */
0472: public float getBlue() {
0473: return blue;
0474: }
0475:
0476: /*
0477: * get the alpha value as a float
0478: * @return float
0479: */
0480: public float getAlpha() {
0481: return alpha;
0482: }
0483:
0484: /**
0485: * Set the alpha value as a float
0486: * @param alpha float
0487: */
0488: public void setAlpha(float alpha) {
0489: this .alpha = alpha;
0490: }
0491:
0492: /*
0493: * Compare to RGB colors (with or without alpha)
0494: * @param rgb RGB the color to compare to
0495: * @param compareAlpha boolean (use or not alpha in the comparison)
0496: * @return boolean
0497: */
0498: public boolean compareTo(RGB rgb, boolean compareAlpha) {
0499: if (rgb.getRed() == red && rgb.getGreen() == green
0500: && rgb.getBlue() == blue)
0501: if (!compareAlpha
0502: || (compareAlpha && rgb.getAlpha() == alpha))
0503: return true;
0504: return false;
0505: }
0506: }
0507:
0508: /*
0509: * Default constructor does nothing
0510: * You need to use the setInputStream method to set the input
0511: * before attempting to get the zlib buffer and info
0512: */
0513: public PNGHelper() {
0514: }
0515:
0516: /*
0517: * Constructor. Allocates a Buffered Input Stream to optimises reading
0518: * @param inputBuffer byte[]
0519: */
0520: public PNGHelper(byte[] inputBuffer) {
0521: setInputBuffer(inputBuffer);
0522: }
0523:
0524: /**
0525: * Set the main inputStream and construct the CRCInputStream to read to
0526: *
0527: * @param inputBuffer buffer to read from
0528: */
0529: public void setInputBuffer(byte[] inputBuffer) {
0530: source = new CRCInputStream(new ByteArrayInputStream(
0531: inputBuffer));
0532: }
0533:
0534: /**
0535: * Set the main inputStream and construct the CRCInputStream to read to
0536: *
0537: * @param fob buffer to read from
0538: */
0539: public void setInputBuffer(FlashBuffer fob) {
0540: //source = new CRCInputStream(new ByteArrayInputStream(fob.getBuf(),0,fob.getSize()));
0541: source = new CRCInputStream(fob.getInputStream());
0542: }
0543:
0544: /**
0545: * Return the width of the image
0546: * @return width of the image
0547: */
0548: public int getWidth() {
0549: return width;
0550: }
0551:
0552: /**
0553: * Return the height of the image
0554: *
0555: * @return height of the image
0556: */
0557: public int getHeight() {
0558: return height;
0559: }
0560:
0561: /*
0562: * Return the palette size (= -1 if none)
0563: * @return int
0564: */
0565: public int getColorTableSize() {
0566: return (colorsUsed - 1);
0567: }
0568:
0569: /*
0570: * Return transparency
0571: * if colorType = 4 or 6, the image uses Alpha Channel
0572: * Otherwise if a tRNS chunk has been seen
0573: * @return boolean
0574: */
0575: public boolean hasTransparency() {
0576: return (colorType > 3 || flagtRNS);
0577: }
0578:
0579: /*
0580: * Return a color format compatible with Flash expectancy
0581: * 3 = 8 bits; 4 = 16 bits; 5 = 32 bits
0582: * Based on the colorType_components value
0583: * And the bitDepth of image
0584: * @return int
0585: */
0586: public int getFormat() {
0587: int format;
0588: switch (colorType_components) {
0589: case 1:
0590: format = 3; // 8 bits
0591: if (colorType == 0)
0592: format = 5;
0593: if (bitDepth == 16)
0594: format = 5;
0595: break;
0596: //case 2:
0597: //format = 4; // 16 bits : forget about it
0598: //break;
0599: default:
0600: format = 5; // 32 bits
0601: break;
0602: }
0603: return format;
0604: }
0605:
0606: /*
0607: * Return the zLib compressed Image data.
0608: * We should be able to send back the IDAT buffer... unfortunately,
0609: * its format is not recognised as such by Flash Player !
0610: * So basically we need to compute our own zLib compression
0611: * on data compatible with Flash
0612: * This can be time consuming !
0613: * @return DataMarker the zlibDatas
0614: * @exception java.io.IOException
0615: * @exception java.io.EOFException
0616: */
0617: public DataMarker getZlibData() throws IOException, EOFException,
0618: IVException {
0619: RGB[][] rgb = read();
0620: boolean transparency = hasTransparency();
0621: int format = getFormat();
0622: int mult = 1;
0623: switch (format) {
0624: case 4:
0625: mult = 2;
0626: break;
0627: case 5:
0628: mult = 4;
0629: }
0630: byte[] tempData;
0631: int plus = 3;
0632: int[] pixel;
0633: int idx = 0;
0634: int falsewidth = width;
0635: int added = 0;
0636: int maxpixels = width * height;
0637: // calculate padding (if needed)
0638: if ((mult == 1) && (width % 4 > 0)) {
0639: while (falsewidth % 4 > 0) {
0640: falsewidth++;
0641: added++;
0642: }
0643: maxpixels = falsewidth * height;
0644: }
0645:
0646: if (colorType_hasPalette) {
0647: if (transparency) // Use RGBA palette
0648: plus++;
0649:
0650: tempData = new byte[(maxpixels) + (colorsUsed * plus)]; // Index + Palette RGB(A)
0651: for (int i = 0; i < colorsUsed; i++) {
0652: if (transparency)
0653: pixel = palette[i].getPacked(RGB.FORMAT_RGBA8);
0654: else
0655: pixel = palette[i].getPacked(RGB.FORMAT_RGB8);
0656:
0657: if (transparency && pixel[3] == 0x00) // the color itself should be 0
0658: {
0659: tempData[idx++] = 0;
0660: tempData[idx++] = 0;
0661: tempData[idx++] = 0;
0662: } else {
0663: tempData[idx++] = (byte) ((pixel[0]));
0664: tempData[idx++] = (byte) ((pixel[1]));
0665: tempData[idx++] = (byte) ((pixel[2]));
0666: }
0667: if (transparency)
0668: tempData[idx++] = (byte) ((pixel[3]));
0669: }
0670: } else {
0671: if (transparency)
0672: mult = 4;
0673: tempData = new byte[(maxpixels * mult)]; // RGB (+ Alpha)
0674: }
0675:
0676: for (int y = 0; y < height; y++) {
0677: for (int x = 0; x < width; x++) {
0678: if (colorType_hasPalette) {
0679: if (transparency)
0680: pixel = rgb[y][x].getPacked(RGB.FORMAT_RGBA8);
0681: else
0682: pixel = rgb[y][x].getPacked(RGB.FORMAT_RGB8);
0683: int b = 0;
0684: for (int i = 0; i < palette.length; i++) {
0685: if (palette[i].compareTo(rgb[y][x], false)) {
0686: b = i;
0687: break;
0688: }
0689: }
0690: tempData[idx++] = (byte) b;
0691: } else {
0692: if (mult == 4)
0693: pixel = rgb[y][x].getPacked(RGB.FORMAT_RGBA8);
0694: else if (mult == 2)
0695: pixel = rgb[y][x].getPacked(RGB.FORMAT_RGB5_A1);
0696: else
0697: pixel = rgb[y][x].getPacked(RGB.FORMAT_RGB8);
0698:
0699: if (pixel != null) {
0700: if (mult == 4)
0701: tempData[idx++] = (byte) ((pixel[3]));
0702: tempData[idx++] = (byte) ((pixel[0]));
0703: if (mult > 1)
0704: tempData[idx++] = (byte) ((pixel[1]));
0705: if (mult == 4)
0706: tempData[idx++] = (byte) ((pixel[2]));
0707: }
0708: }
0709: }
0710: if (added > 0) // 32 bits padding for 8 bits image
0711: {
0712: for (int i = 0; i < added; i++) {
0713: tempData[idx++] = 0x00;
0714: }
0715: }
0716: }
0717: Deflater deflater = new Deflater(9); // MAXIMUM Compression
0718: deflater.setInput(tempData);
0719: byte[] data = new byte[(maxpixels * mult) + (colorsUsed * plus)];
0720: deflater.finish();
0721: int defsize = deflater.deflate(data);
0722: byte[] buff = new byte[defsize];
0723: System.arraycopy(data, 0, buff, 0, defsize);
0724: DataMarker zlibDatas = new DataMarker(buff, 0, defsize);
0725: return zlibDatas;
0726: }
0727:
0728: /***********************************************************************************
0729: ** PRIVATE IMPLEMENTATION **
0730: ***********************************************************************************/
0731:
0732: /*
0733: * Reads the actual datas, return a multiDimentionnal RGB array : the pixels
0734: * @return RGB[][]
0735: * @exception java.io.IOException
0736: * @exception java.io.EOFException
0737: */
0738: private RGB[][] read() throws IOException, EOFException,
0739: IVException {
0740: if (source == null)
0741: throw new IOException("Null input stream");
0742: source.mark(Integer.MAX_VALUE);
0743: buffer = new byte[0];
0744: try {
0745: for (int i = 0; i < 8; i++)
0746: if (source.read() != PNG_SIGN[i])
0747: throw new IOException("Not a valid PNG file");
0748:
0749: int length;
0750: int id;
0751: while (true) {
0752: length = (int) source.read4n();
0753: source.reset();
0754: id = (int) source.read4n();
0755: switch (id) {
0756: // Critical chunks
0757: case CHUNK_IHDR:
0758: readChunk_IHDR(length);
0759: break;
0760: case CHUNK_PLTE:
0761: readChunk_PLTE(length);
0762: break;
0763: case CHUNK_tRNS:
0764: readChunk_tRNS(length);
0765: break;
0766: case CHUNK_IDAT:
0767: readChunk_IDAT(length);
0768: break;
0769: case CHUNK_IEND:
0770: if (!flagIHDR || !flagIDAT)
0771: throw new IOException(
0772: "No header or data chunk found");
0773: return decode(buffer);
0774:
0775: // known, ignored chunks
0776: case CHUNK_gAMA:
0777: readChunk_gAMA(length);
0778: break; // read only for now
0779: case CHUNK_bKGD:
0780: case CHUNK_cHRM: // we might need this one later for advanced color handling
0781: case CHUNK_fRAc:
0782: case CHUNK_gIFg:
0783: case CHUNK_gIFt:
0784: case CHUNK_gIFx:
0785: case CHUNK_hIST:
0786: case CHUNK_iCCP: // we might need this one later for advanced color handling
0787: case CHUNK_iTXt:
0788: case CHUNK_oFFs:
0789: case CHUNK_pCAL:
0790: case CHUNK_pHYs:
0791: case CHUNK_sBIT:
0792: case CHUNK_sCAL:
0793: case CHUNK_sPLT: // we might need this one later for advanced color handling
0794: case CHUNK_sRGB: // we might need this one later for advanced color handling
0795: case CHUNK_tEXt:
0796: case CHUNK_tIME:
0797: case CHUNK_zTXt:
0798: source.skipBytes(length + 4); // skip it and its CRC code
0799: break;
0800: default:
0801: // unknown chunk...
0802: if ((id & 0x20000000) == 0)
0803: // if critical we throw...
0804: throw new IOException("Unknown critical chunk "
0805: + (char) ((id >> 24) & 0xff)
0806: + (char) ((id >> 16) & 0xff)
0807: + (char) ((id >> 8) & 0xff)
0808: + (char) (id & 0xff));
0809: // otherwise, skip that chunk and its CRC code
0810: source.skipBytes(length + 4);
0811: }
0812: }
0813: } catch (EOFException eofe) {
0814: throw new EOFException("Reached unexpected EOF");
0815: } catch (IOException ioe) {
0816: throw new IOException("Error while reading file");
0817: }
0818: }
0819:
0820: /**
0821: * Deal with the gamma chunk
0822: * @param length int the length of the chunk to read
0823: * @exception java.io.IOException
0824: */
0825: private void readChunk_gAMA(int length) throws IOException {
0826: if (flagIDAT || flagPLTE)
0827: throw new IOException(
0828: "gAMA chunk must precede IDAT and PLTE chunks");
0829:
0830: if (length != 4)
0831: throw new IOException("Invalid length " + length
0832: + " for gAMA chunk");
0833:
0834: gamma = source.read4n(); // the float value will be : (val / 100000)
0835:
0836: long okCRC = source.getCRC();
0837: if (source.read4n() != okCRC)
0838: throw new IOException("Invalid CRC for gAMA chunk "
0839: + length);
0840: }
0841:
0842: /**
0843: * Deal with the transparency chunk
0844: * @param length int the length of the chunk to read
0845: * @exception java.io.IOException
0846: */
0847: private void readChunk_tRNS(int length) throws IOException {
0848: if (!flagIHDR || flagIDAT)
0849: throw new IOException("Misplaced transparency chunk");
0850:
0851: switch (colorType) {
0852: case 0: // grayscale
0853: int tmp1 = source.read();
0854: int tmp2 = source.read();
0855:
0856: if (bitDepth < 16) {
0857: int max = 1;
0858: switch (bitDepth) {
0859: case 1:
0860: max = 256;
0861: break;
0862: case 2:
0863: max = 256 / 4;
0864: break;
0865: case 4:
0866: max = 256 / 16;
0867: break;
0868: }
0869: if (bitDepth == 1) // (0x00 or 0xff)
0870: tRNSKeyColor = new RGB(tmp2 * max);
0871: else {
0872: tmp1 = ((tmp2 + 1) * max) - 1;
0873: tmp1 = (tmp1 < 0) ? 0 : tmp1;
0874: tRNSKeyColor = new RGB(tmp1);
0875: }
0876: } else
0877: tRNSKeyColor = new RGB(
0878: (float) (tmp1 << 8 | tmp2) / 65535.0f);
0879:
0880: tRNSKeyColor.setAlpha(0.0f);
0881: tRNSPassRequired = true;
0882: break;
0883: case 2: // RGB
0884: int tmp;
0885: int cr1 = source.read();
0886: int cr2 = source.read();
0887: int cg1 = source.read();
0888: int cg2 = source.read();
0889: int cb1 = source.read();
0890: int cb2 = source.read();
0891: if (bitDepth < 16) {
0892: tRNSKeyColor = new RGB(cr2, cg2, cb2);
0893: } else
0894: tRNSKeyColor = new RGB(
0895: (float) (cr1 << 8 | cr2) / 65535.0f,
0896: (float) (cg1 << 8 | cg2) / 65535.0f,
0897: (float) (cb1 << 8 | cb2) / 65535.0f);
0898: tRNSKeyColor.setAlpha(0.0f);
0899: tRNSPassRequired = true;
0900: break;
0901: case 3: // paletted
0902: // update palette entries with alpha information
0903: for (int i = 0; i < length; i++)
0904: palette[i].setAlpha((float) source.read() / 255.0f);
0905: break;
0906: default: // 4 and 6 colorType should not have tranparency chunk, they have Alpha channel instead
0907: throw new IOException("Unexpected transparency chunk");
0908: }
0909: flagtRNS = true;
0910:
0911: long okCRC = source.getCRC();
0912: if (source.read4n() != okCRC)
0913: throw new IOException("Invalid CRC for tRNS chunk "
0914: + length);
0915: }
0916:
0917: /**
0918: * Deal with the palette chunk
0919: * @param length int the length of the chunk to read
0920: * @exception java.io.IOException
0921: */
0922: private void readChunk_PLTE(int length) throws IOException {
0923: if (!flagIHDR || flagIDAT)
0924: throw new IOException("Misplaced palette chunk");
0925:
0926: if (length % 3 != 0)
0927: throw new IOException("Invalid palette size: " + length
0928: + " bytes");
0929: if (colorType != 3) {
0930: // throw new IOException("Palette must not appear in grayscale or RGB images");
0931: // eat the entries
0932: int tmp;
0933: for (int i = 0; i < length; i++)
0934: tmp = source.read();
0935: } else {
0936: flagPLTE = true;
0937: int entries = length / 3;
0938: colorsUsed = entries;
0939: palette = new RGB[entries];
0940: for (int i = 0; i < entries; i++)
0941: palette[i] = new RGB(source.read(), source.read(),
0942: source.read());
0943: }
0944: long okCRC = source.getCRC();
0945: if (source.read4n() != okCRC)
0946: throw new IOException("Invalid CRC for PLTE chunk");
0947: }
0948:
0949: /**
0950: * Deal with the header chunk
0951: * @param length int the length of the chunk to read
0952: * @exception java.io.IOException
0953: */
0954: private void readChunk_IHDR(int length) throws IOException {
0955: if (flagIHDR)
0956: throw new IOException("Duplicate header chunks");
0957: flagIHDR = true;
0958:
0959: width = (int) source.read4n();
0960: height = (int) source.read4n();
0961: bitDepth = source.read();
0962: colorType = source.read();
0963: compressionMethod = source.read();
0964: filterMethod = source.read();
0965: interlaceMethod = source.read();
0966:
0967: if ((width == 0) || (height == 0))
0968: throw new IOException("Invalid image size: " + width + "x"
0969: + height);
0970: switch (colorType) {
0971: case 0: // grayscale: 1, 2, 4, 8, 16
0972: if ((bitDepth != 1) && (bitDepth != 2) && (bitDepth != 4)
0973: && (bitDepth != 8) && (bitDepth != 16))
0974: throw new IOException("Invalid bit depth: " + bitDepth
0975: + " for color type: grayscale");
0976: colorType_components = 1;
0977: //colorType_hasPalette = false;
0978: //colorType_hasColor = false;
0979: //colorType_hasAlpha = false;
0980: break;
0981: case 3: // paletted: 1, 2, 4, 8
0982: if ((bitDepth != 1) && (bitDepth != 2) && (bitDepth != 4)
0983: && (bitDepth != 8))
0984: throw new IOException("Invalid bit depth: " + bitDepth
0985: + " for color type: paletted");
0986: colorType_components = 1;
0987: colorType_hasPalette = true;
0988: colorType_hasColor = true;
0989: //colorType_hasAlpha = false;
0990: break;
0991: case 2: // RGB: 8, 16
0992: if ((bitDepth != 8) && (bitDepth != 16))
0993: throw new IOException("Invalid bit depth: " + bitDepth
0994: + " for color type: RGB");
0995: colorType_components = 3;
0996: //colorType_hasPalette = false;
0997: colorType_hasColor = true;
0998: //colorType_hasAlpha = false;
0999: break;
1000: case 4: // gray + alpha: 8, 16
1001: if ((bitDepth != 8) && (bitDepth != 16))
1002: throw new IOException("Invalid bit depth: " + bitDepth
1003: + " for color type: gray + alpha");
1004: colorType_components = 2;
1005: //colorType_hasPalette = false;
1006: //colorType_hasColor = false;
1007: colorType_hasAlpha = true;
1008: break;
1009: case 6: // RGB + alpha: 8, 16
1010: if ((bitDepth != 8) && (bitDepth != 16))
1011: throw new IOException("Invalid bit depth: " + bitDepth
1012: + " for color type: RGB + alpha");
1013: colorType_components = 4;
1014: //colorType_hasPalette = false;
1015: colorType_hasColor = true;
1016: colorType_hasAlpha = true;
1017: break;
1018: default:
1019: throw new IOException("Invalid color type: " + colorType);
1020: }
1021: if (compressionMethod != 0)
1022: throw new IOException("Unsupported compression method: "
1023: + compressionMethod);
1024: if (filterMethod != 0)
1025: throw new IOException("Unsupported filter method: "
1026: + compressionMethod);
1027: if ((interlaceMethod != 0) && (interlaceMethod != 1))
1028: throw new IOException("Unsupported interlace method: "
1029: + interlaceMethod);
1030:
1031: long okCRC = source.getCRC();
1032: if (source.read4n() != okCRC)
1033: throw new IOException("Invalid CRC for IHDR chunk");
1034: }
1035:
1036: /**
1037: * Deal with data chunks
1038: * @param length int the length of the chunk to read
1039: * @exception java.io.IOException
1040: */
1041: private void readChunk_IDAT(int length) throws IOException {
1042: if (!flagIHDR)
1043: throw new IOException("Data chunks before header");
1044: if ((colorType == 3) && (!flagPLTE))
1045: throw new IOException("Missing palette");
1046: flagIDAT = true;
1047:
1048: int oldLength = buffer.length;
1049: int newLength = oldLength + length;
1050: byte[] zlibNew = new byte[newLength];
1051: System.arraycopy(buffer, 0, zlibNew, 0, oldLength);
1052:
1053: for (int i = oldLength; i < newLength; i++)
1054: zlibNew[i] = (byte) source.read();
1055:
1056: buffer = zlibNew;
1057:
1058: long okCRC = source.getCRC();
1059: if (source.read4n() != okCRC)
1060: throw new IOException("Invalid CRC for IDAT chunk");
1061: }
1062:
1063: /*
1064: * Decodes the zLib buffer according to compression/filtering
1065: * and return an array of pixel computed based on the colorType/bitDepth
1066: * need to take interlaced method (Adam7) into account
1067: * @param zlibStream byte[]
1068: * @return RGB[][]
1069: * @exception java.io.IOException
1070: */
1071: private RGB[][] decode(byte[] zlibStream) throws IOException {
1072: InflaterInputStream inflater = new InflaterInputStream(
1073: new java.io.ByteArrayInputStream(zlibStream));
1074:
1075: int bytesPerScanline = (int) ((float) width * (float) bitDepth
1076: * (float) colorType_components / 8.0f);
1077:
1078: // add 1 byte of padding for paletted colors of less than 8 bit depth
1079: if (colorType == 3) {
1080: // width/bitdepth
1081: switch (bitDepth) {
1082: case 1:
1083: if ((width % 8) > 0)
1084: bytesPerScanline++;
1085: break;
1086: case 2:
1087: if ((width % 4) > 0)
1088: bytesPerScanline++;
1089: break;
1090: case 4:
1091: if ((width % 2) > 0)
1092: bytesPerScanline++;
1093: break;
1094: }
1095: }
1096:
1097: RGB[][] rgb = new RGB[height][width];
1098:
1099: // decompressing // deinterlacing (if needed) and process color (if needed)
1100: if (interlaceMethod == 1 && bitDepth < 8) {
1101: // special case with padding involved, colors will already be decoded
1102: rgb = deCompressSpecial(rgb, inflater);
1103: } else {
1104: int bytesPerPixel = bitDepth * colorType_components / 8;
1105: bytesPerPixel = (bytesPerPixel == 0) ? 1 : bytesPerPixel;
1106:
1107: int[][] scanlines = new int[height][bytesPerScanline];
1108: scanlines = deCompress(scanlines, inflater, bytesPerPixel);
1109:
1110: // decoding byte values to pixel values
1111: int b, bb; // temp
1112: int cr, cg, cb, ca;
1113: switch (colorType) {
1114:
1115: case 0: // grayscale: 1, 2, 4, 8, 16
1116: switch (bitDepth) {
1117: case 1:
1118: for (int y = 0; y < height; y++) {
1119: int scanX = 0;
1120: for (int x = 0; x < width;) {
1121: b = scanlines[y][scanX++];
1122: for (int z = 7; ((x < width) && (z > -1)); z--) {
1123: bb = ((b >> z) & 0x01) * 256;
1124: RGB color = new RGB(bb, bb, bb);
1125: if (tRNSPassRequired) // Apply transparency
1126: {
1127: if (color.compareTo(tRNSKeyColor,
1128: false)) // on the right color
1129: {
1130: color = new RGB(0);
1131: color.setAlpha(0.0f);
1132: }
1133: }
1134: rgb[y][x++] = color;
1135: }
1136: }
1137: }
1138: break;
1139: case 2:
1140: for (int y = 0; y < height; y++) {
1141: int scanX = 0;
1142: for (int x = 0; x < width;) {
1143: b = scanlines[y][scanX++];
1144: for (int z = 6; ((x < width) && (z > -1)); z -= 2) {
1145: bb = ((((b >> z) & 0x03) + 1) * 64) - 1;
1146: bb = (bb < 0) ? 0 : bb;
1147: RGB color = new RGB(bb, bb, bb);
1148: if (tRNSPassRequired) // Apply transparency
1149: {
1150: if (color.compareTo(tRNSKeyColor,
1151: false)) // on the right color
1152: {
1153: color = new RGB(0);
1154: color.setAlpha(0.0f);
1155: }
1156: }
1157: rgb[y][x++] = color;
1158: }
1159: }
1160: }
1161: break;
1162: case 4:
1163: for (int y = 0; y < height; y++) {
1164: int scanX = 0;
1165: for (int x = 0; x < width;) {
1166: b = scanlines[y][scanX++];
1167: bb = ((((b >> 4) & 0x0f) + 1) * 16) - 1;
1168: bb = (bb < 0) ? 0 : bb;
1169: RGB color = new RGB(bb, bb, bb);
1170: if (tRNSPassRequired) // Apply transparency
1171: {
1172: if (color
1173: .compareTo(tRNSKeyColor, false)) // on the right color
1174: {
1175: color = new RGB(0);
1176: color.setAlpha(0.0f);
1177: }
1178: }
1179: rgb[y][x++] = color;
1180: if (x < width) {
1181: bb = (((b & 0x0f) + 1) * 16) - 1;
1182: bb = (bb < 0) ? 0 : bb;
1183: color = new RGB(bb, bb, bb);
1184: if (tRNSPassRequired) // Apply transparency
1185: {
1186: if (color.compareTo(tRNSKeyColor,
1187: false)) // on the right color
1188: {
1189: color = new RGB(0);
1190: color.setAlpha(0.0f);
1191: }
1192: }
1193: rgb[y][x++] = color;
1194: }
1195: }
1196: }
1197: break;
1198: case 8:
1199: for (int y = 0; y < height; y++) {
1200: for (int x = 0; x < width; x++) {
1201: RGB color = new RGB(scanlines[y][x]);
1202: if (tRNSPassRequired) // Apply transparency
1203: {
1204: if (color
1205: .compareTo(tRNSKeyColor, false)) // on the right color
1206: {
1207: color = new RGB(0);
1208: color.setAlpha(0.0f);
1209: }
1210: }
1211: rgb[y][x] = color;
1212: }
1213: }
1214: break;
1215: case 16:
1216: for (int y = 0; y < height; y++) {
1217: int scanX = 0;
1218: for (int x = 0; x < width; x++) {
1219: RGB color = new RGB(
1220: (float) (scanlines[y][scanX++] << 8 | scanlines[y][scanX++]) / 65535.0f);
1221: if (tRNSPassRequired) // Apply transparency
1222: {
1223: if (color
1224: .compareTo(tRNSKeyColor, false)) // on the right color
1225: {
1226: color = new RGB(0);
1227: color.setAlpha(0.0f);
1228: }
1229: }
1230: rgb[y][x] = color;
1231: }
1232: }
1233: break;
1234: }
1235: break;
1236:
1237: case 2: // RGB: 8, 16
1238: switch (bitDepth) {
1239: case 8:
1240: for (int y = 0; y < height; y++) {
1241: int scanX = 0;
1242: for (int x = 0; x < width; x++) {
1243: RGB color = new RGB(scanlines[y][scanX++],
1244: scanlines[y][scanX++],
1245: scanlines[y][scanX++]);
1246: if (tRNSPassRequired) // Apply transparency
1247: {
1248: if (color
1249: .compareTo(tRNSKeyColor, false)) // on the right color
1250: {
1251: color = new RGB(0);
1252: color.setAlpha(0.0f);
1253: }
1254: }
1255: rgb[y][x] = color;
1256: }
1257: }
1258: break;
1259: case 16:
1260: // this one is really weird, further tests will be required
1261: for (int y = 0; y < height; y++) {
1262: int scanX = 0;
1263: for (int x = 0; x < width; x++) {
1264: float aa = (float) (scanlines[y][scanX++] << 8 | scanlines[y][scanX++]) / 65535.0f;
1265: float ab = (float) (scanlines[y][scanX++] << 8 | scanlines[y][scanX++]) / 65535.0f;
1266: float ac = (float) (scanlines[y][scanX++] << 8 | scanlines[y][scanX++]) / 65535.0f;
1267: RGB color = new RGB(aa, ab, ac);
1268:
1269: if (tRNSPassRequired) // Apply transparency
1270: {
1271: if (color
1272: .compareTo(tRNSKeyColor, false)) // on the right color
1273: {
1274: color = new RGB(0);
1275: color.setAlpha(0.0f);
1276: }
1277: }
1278: rgb[x][y] = color;
1279: }
1280: }
1281: break;
1282: }
1283: break;
1284: case 3: // paletted: 1, 2, 4, 8
1285: switch (bitDepth) {
1286: case 1:
1287: for (int y = 0; y < height; y++) {
1288: int scanX = 0;
1289: for (int x = 0; x < width;) {
1290: if (scanX < bytesPerScanline) {
1291: b = scanlines[y][scanX++];
1292: for (int z = 7; ((x < width) && (z > -1)); z--) {
1293: bb = (b >> z) & 0x01;
1294: rgb[y][x++] = palette[bb];
1295: }
1296: } else
1297: break;
1298: }
1299: }
1300: break;
1301: case 2:
1302: for (int y = 0; y < height; y++) {
1303: int scanX = 0;
1304: for (int x = 0; x < width;) {
1305: if (scanX < bytesPerScanline) {
1306: b = scanlines[y][scanX++];
1307: for (int z = 6; ((x < width) && (z > -1)); z -= 2) {
1308: bb = (b >> z) & 0x03;
1309: rgb[y][x++] = palette[bb];
1310: }
1311: } else
1312: break;
1313: }
1314: }
1315: break;
1316: case 4:
1317: for (int y = 0; y < height; y++) {
1318: int scanX = 0;
1319: for (int x = 0; x < width;) {
1320: if (scanX <= bytesPerScanline) {
1321: b = scanlines[y][scanX++];
1322: bb = (b >> 4) & 0x0f;
1323: rgb[y][x++] = palette[bb];
1324: if (x < width) {
1325: bb = b & 0x0f;
1326: rgb[y][x++] = palette[bb];
1327: }
1328: } else
1329: break;
1330: }
1331: }
1332: break;
1333: case 8:
1334: for (int y = 0; y < height; y++) {
1335: for (int x = 0; x < width; x++) {
1336: b = scanlines[y][x];
1337: // to be on the safe side
1338: b = (b < palette.length) ? b : 0;
1339: rgb[y][x] = palette[b];
1340: }
1341: }
1342: break;
1343: }
1344: break;
1345: case 4: // gray + alpha: 8, 16
1346: switch (bitDepth) {
1347: case 8:
1348: for (int y = 0; y < height; y++) {
1349: int scanX = 0;
1350: for (int x = 0; x < width; x++) {
1351: // cr used as 'gray'
1352: cr = scanlines[y][scanX++];
1353: ca = scanlines[y][scanX++];
1354:
1355: // apply alpha
1356: float fa = (float) ca / 255.0f;
1357: cr = (int) (cr * fa);
1358:
1359: rgb[y][x] = new RGB(cr, ca);
1360: }
1361: }
1362: break;
1363: case 16:
1364: for (int y = 0; y < height; y++) {
1365: int scanX = 0;
1366: for (int x = 0; x < width; x++) {
1367: // aa used as 'gray'
1368: float aa = (float) (scanlines[y][scanX++] << 8 | scanlines[y][scanX++]) / 65535.0f;
1369: float ab = (float) (scanlines[y][scanX++] << 8 | scanlines[y][scanX++]) / 65535.0f;
1370:
1371: // apply alpha
1372: aa *= ab;
1373:
1374: rgb[y][x] = new RGB(aa, ab);
1375: }
1376: }
1377: break;
1378: }
1379: break;
1380: case 6: // RGB + alpha: 8, 16
1381: switch (bitDepth) {
1382: case 8:
1383: int idx = 0;
1384: for (int y = 0; y < height; y++) {
1385: int scanX = 0;
1386: for (int x = 0; x < width; x++) {
1387: cr = scanlines[y][scanX++];
1388: cg = scanlines[y][scanX++];
1389: cb = scanlines[y][scanX++];
1390: ca = scanlines[y][scanX++];
1391:
1392: // apply alpha
1393: if (ca == 0) {
1394: cr = cg = cb = 0;
1395: } else {
1396: float fa = (float) ca / 255.0f;
1397: cr = (int) ((cr) * fa);
1398: cg = (int) ((cg) * fa);
1399: cb = (int) ((cb) * fa);
1400: }
1401:
1402: rgb[y][x] = new RGB(cr, cg, cb, ca);
1403: }
1404: }
1405: break;
1406: case 16:
1407: for (int y = 0; y < height; y++) {
1408: int scanX = 0;
1409: for (int x = 0; x < width; x++) {
1410: float aa = (float) (scanlines[y][scanX++] << 8 | scanlines[y][scanX++]) / 65535.0f;
1411: float ab = (float) (scanlines[y][scanX++] << 8 | scanlines[y][scanX++]) / 65535.0f;
1412: float ac = (float) (scanlines[y][scanX++] << 8 | scanlines[y][scanX++]) / 65535.0f;
1413: float ad = (float) (scanlines[y][scanX++] << 8 | scanlines[y][scanX++]) / 65535.0f;
1414:
1415: // apply alpha
1416: aa *= ad;
1417: ab *= ad;
1418: ac *= ad;
1419:
1420: rgb[y][x] = new RGB(aa, ab, ac, ad);
1421: }
1422: }
1423: break;
1424: }
1425: }
1426: }
1427: return rgb;
1428: }
1429:
1430: /**
1431: * get the previous row value of the pass
1432: * @param row int the current row
1433: * @param pass int the current pass
1434: * @return int
1435: */
1436: private int getPreviousRow(int row, int pass) {
1437: if (interlaceMethod == 1) {
1438: int[] starting_Row = { 0, 0, 4, 0, 2, 0, 1 };
1439: int[] row_Increment = { 8, 8, 8, 4, 4, 2, 2 };
1440: int currentPassMin = starting_Row[pass];
1441: if (row == currentPassMin)
1442: return 0;
1443: else
1444: return ((row > (row - row_Increment[pass])) ? (row - row_Increment[pass])
1445: : 0);
1446: } else {
1447: return ((row > 0) ? (row - 1) : 0);
1448: }
1449: }
1450:
1451: /**
1452: * get the previous column value of the pass
1453: * @param col int the current column
1454: * @param pass int the current pass
1455: * @return int
1456: */
1457: private int getPreviousCol(int col, int pass) {
1458: if (interlaceMethod == 1) {
1459: int[] starting_Col = { 0, 4, 0, 2, 0, 1, 0 };
1460: int[] col_Increment = { 8, 8, 4, 4, 2, 2, 1 };
1461: int currentPassMin = starting_Col[pass];
1462: if (col == currentPassMin)
1463: return 0;
1464: else
1465: return ((col > (col - col_Increment[pass])) ? (col - col_Increment[pass])
1466: : 0);
1467: } else {
1468: return ((col > 0) ? (col - 1) : 0);
1469: }
1470: }
1471:
1472: /**
1473: * Decompress, deinterlace, unfilter and set the pixels colors
1474: * Used for special interlaced images of less than 8 bit depth (padding involved)
1475: * @param rgb RGB[][] the RGB colors of pixels
1476: * @param inflater InputStream the inflater InputStream to read the zlib data from
1477: * @return RGB[][] the RGB colors of pixels set
1478: * @Exception java.io.IOException
1479: */
1480: private RGB[][] deCompressSpecial(RGB[][] rgb, InputStream inflater)
1481: throws IOException {
1482: int[] starting_Row = { 0, 0, 4, 0, 2, 0, 1 };
1483: int[] starting_Col = { 0, 4, 0, 2, 0, 1, 0 };
1484: int[] row_Increment = { 8, 8, 8, 4, 4, 2, 2 };
1485: int[] col_Increment = { 8, 8, 4, 4, 2, 2, 1 };
1486: int[] block_Height = { 8, 8, 4, 4, 2, 2, 1 };
1487: int[] block_Width = { 8, 4, 4, 2, 2, 1, 1 };
1488:
1489: int row, col, filter;
1490:
1491: int[][] pixels = new int[height][width];
1492:
1493: int a, b, c, pa, pb, pc, p; // for paeth filter
1494:
1495: for (int pass = 0; pass < 7; pass++) {
1496: row = starting_Row[pass];
1497: while (row < height) {
1498: int minHeight = (block_Height[pass] < (height - row)) ? block_Height[pass]
1499: : (height - row);
1500: b = inflater.read();
1501: if (b > -1) {
1502: filter = (b >> 4) & 0x03;
1503:
1504: col = starting_Col[pass];
1505: while (col < width) {
1506: int minWidth = (block_Width[pass] < (width - col)) ? block_Width[pass]
1507: : (width - col);
1508: b = inflater.read();
1509: if (b > -1) {
1510: switch (bitDepth) {
1511: case 1:
1512: for (int i = 7; i > -1; i--) {
1513: if (col < width) {
1514: a = (b >> i) & 0x01;
1515: for (int x = 0; x < minHeight; x++) {
1516: int rowx = row + x;
1517: if (rowx < height) {
1518: for (int y = 0; y < minWidth; y++) {
1519: int coly = col + y;
1520: if (coly < width)
1521: pixels[rowx][coly] = a;
1522: else
1523: break;
1524: }
1525: } else
1526: break;
1527: }
1528: } else
1529: break;
1530: col += col_Increment[pass];
1531: }
1532: break;
1533: case 2:
1534: for (int i = 6; i > -1; i -= 2) {
1535: if (col < width) {
1536: a = (b >> i) & 0x03;
1537: for (int x = 0; x < minHeight; x++) {
1538: int rowx = row + x;
1539: if (rowx < height) {
1540: for (int y = 0; y < minWidth; y++) {
1541: int coly = col + y;
1542: if (coly < width)
1543: pixels[rowx][coly] = a;
1544: else
1545: break;
1546: }
1547: } else
1548: break;
1549: }
1550: } else
1551: break;
1552: col += col_Increment[pass];
1553: }
1554: break;
1555: case 4:
1556: for (int i = 4; i > -1; i -= 4) {
1557: if (col < width) {
1558: a = (b >> i) & 0x0f;
1559: for (int x = 0; x < minHeight; x++) {
1560: int rowx = row + x;
1561: if (rowx < height) {
1562: for (int y = 0; y < minWidth; y++) {
1563: int coly = col + y;
1564: if (coly < width)
1565: pixels[rowx][coly] = a;
1566: else
1567: break;
1568: }
1569: } else
1570: break;
1571: }
1572: } else
1573: break;
1574: col += col_Increment[pass];
1575: }
1576: break;
1577: }
1578: } else
1579: break;
1580: }
1581:
1582: // Cast to byte when filtering to use modulo 256 arithmetic
1583: int nbboucle = 8 / bitDepth;
1584: switch (filter) {
1585: case 0:
1586: // no filtering
1587: break;
1588: case 1: // sub
1589: col = starting_Col[pass] + col_Increment[pass];
1590: while (col < width) {
1591: for (int i = 0; i < nbboucle; i++) {
1592: if (col < width) {
1593: pixels[row][col] = (byte) (pixels[row][col] + pixels[row][getPreviousCol(
1594: col, pass)]) & 0xff;
1595: }
1596: col += col_Increment[pass];
1597: }
1598: }
1599: break;
1600: case 2: // up
1601: if (row == 0)
1602: break;
1603: col = starting_Col[pass];
1604: while (col < width) {
1605: for (int i = 0; i < nbboucle; i++) {
1606: if (col < width) {
1607: pixels[row][col] = (byte) (pixels[row][col] + pixels[getPreviousRow(
1608: row, pass)][col]) & 0xff;
1609: }
1610: col += col_Increment[pass];
1611: }
1612: }
1613: break;
1614: case 3: // average
1615: col = starting_Col[pass];
1616: pixels[row][col] = (byte) (pixels[row][col] + (((row == 0) ? 0
1617: : pixels[getPreviousRow(row, pass)][col]) / 2)) & 0xff;
1618: col += col_Increment[pass];
1619: while (col < width) {
1620: pixels[row][col] = (byte) (pixels[row][col] + (pixels[row][getPreviousCol(
1621: col, pass)] + ((row == 0) ? 0
1622: : pixels[getPreviousRow(row, pass)][col])) / 2) & 0xff;
1623:
1624: col += col_Increment[pass];
1625: }
1626: break;
1627: case 4: // paeth
1628: if (row == 0) {
1629: col = starting_Col[pass];
1630: pixels[row][col] = 0xff & pixels[row][col];
1631: col += col_Increment[pass];
1632:
1633: while (col < width) {
1634: p = pixels[row][getPreviousCol(col,
1635: pass)];
1636: pixels[row][col] = 0xff & (byte) (p + pixels[row][col]);
1637: col += col_Increment[pass];
1638: }
1639: } else {
1640: col = starting_Col[pass];
1641: pixels[row][col] = 0xff & (byte) (pixels[getPreviousRow(
1642: row, pass)][col] + pixels[row][col]);
1643: col += col_Increment[pass];
1644:
1645: while (col < width) {
1646: a = pixels[row][getPreviousCol(col,
1647: pass)]; // left
1648: b = pixels[getPreviousRow(row, pass)][col]; // above
1649: c = pixels[getPreviousRow(row, pass)][getPreviousCol(
1650: col, pass)]; // upper left
1651:
1652: // Paeth tests
1653: p = (a + b) - c; // initial estimate
1654: pa = p > a ? p - a : a - p; // nearest distance to a
1655: pb = p > b ? p - b : b - p; // nearest distance to b
1656: pc = p > c ? p - c : c - p; // nearest distance to c
1657:
1658: if ((pa <= pb) && (pa <= pc))
1659: p = a;
1660: else if (pb <= pc)
1661: p = b;
1662: else
1663: p = c;
1664:
1665: pixels[row][col] = 0xff & (byte) (p + pixels[row][col]);
1666:
1667: col += col_Increment[pass];
1668: }
1669: }
1670: break;
1671: }
1672: }
1673: row += row_Increment[pass];
1674: }
1675: }
1676:
1677: p = 256 / (bitDepth * bitDepth); // gray multiplier
1678: row = 0;
1679: while (row < height) {
1680: col = 0;
1681: while (col < width) {
1682: if (colorType == 0) // gray
1683: {
1684: RGB color;
1685: if (bitDepth == 1)
1686: color = new RGB(pixels[row][col] * p);
1687: else
1688: color = new RGB(
1689: ((pixels[row][col] + 1) * p) - 1);
1690: if (tRNSPassRequired) // Apply transparency
1691: {
1692: if (color.compareTo(tRNSKeyColor, false)) // on the right color
1693: {
1694: color = new RGB(0);
1695: color.setAlpha(0.0f);
1696: }
1697: }
1698: rgb[row][col] = color;
1699: } else
1700: // paletted
1701: // transparency is already integrated in the palette
1702: rgb[row][col] = palette[pixels[row][col]];
1703: col++;
1704: }
1705: row++;
1706: }
1707:
1708: return rgb;
1709: }
1710:
1711: /**
1712: * Decompress, deinterlace, and unfilter the scanlines
1713: * Used for special interlaced images of less than 8 bit depth (padding involved)
1714: * @param scanlines int[][] the scanlines to decode into
1715: * @param inflater InputStream the inflater InputStream to read the zlib data from
1716: * @param bytesPerPixel int the number of bytes per pixel of scanlines
1717: * @return int[][] the scanlines decoded
1718: * @Exception java.io.IOException
1719: */
1720: private int[][] deCompress(int[][] scanlines, InputStream inflater,
1721: int bytesPerPixel) throws IOException {
1722: int[] starting_Row = { 0, 0, 4, 0, 2, 0, 1 };
1723: int[] starting_Col = { 0, 4, 0, 2, 0, 1, 0 };
1724: int[] row_Increment = { 8, 8, 8, 4, 4, 2, 2 };
1725: int[] col_Increment = { 8, 8, 4, 4, 2, 2, 1 };
1726:
1727: int a, b, c, pa, pb, pc, p; // for paeth filter
1728: int maxPass = 7;
1729: if (interlaceMethod == 0)
1730: maxPass = 1;
1731:
1732: int row, col;
1733: for (int pass = 0; pass < maxPass; pass++) {
1734: if (interlaceMethod == 1)
1735: row = starting_Row[pass];
1736: else
1737: row = 0;
1738:
1739: while (row < height) {
1740: int filter = inflater.read();
1741: if (interlaceMethod == 1)
1742: col = starting_Col[pass];
1743: else
1744: col = 0;
1745: while (col < width) {
1746: for (int i = 0; i < bytesPerPixel; i++) {
1747: int bitcol = (col * bytesPerPixel) + i;
1748: if (bitcol < scanlines[row].length)
1749: scanlines[row][bitcol] = inflater.read();
1750: }
1751: if (interlaceMethod == 1)
1752: col += col_Increment[pass];
1753: else
1754: col++;
1755: }
1756:
1757: // Cast to byte when filtering to use modulo 256 arithmetic
1758: switch (filter) {
1759: case 0:
1760: // no filtering
1761: break;
1762: case 1: // sub
1763: if (interlaceMethod == 1)
1764: col = starting_Col[pass] + col_Increment[pass];
1765: else
1766: col = 1;
1767: while (col < width) {
1768: for (int i = 0; i < bytesPerPixel; i++) {
1769: int bitcol = (col * bytesPerPixel) + i;
1770: if (bitcol < scanlines[row].length) {
1771: scanlines[row][bitcol] = (byte) (scanlines[row][bitcol] + scanlines[row][(getPreviousCol(
1772: col, pass) * bytesPerPixel)
1773: + i]) & 0xff;
1774: }
1775: }
1776: if (interlaceMethod == 1)
1777: col += col_Increment[pass];
1778: else
1779: col++;
1780: }
1781: break;
1782: case 2: // up
1783: if (row == 0)
1784: break;
1785: if (interlaceMethod == 1)
1786: col = starting_Col[pass];
1787: else
1788: col = 0;
1789: while (col < width) {
1790: for (int i = 0; i < bytesPerPixel; i++) {
1791: int bitcol = (col * bytesPerPixel) + i;
1792: if (bitcol < scanlines[row].length) {
1793: scanlines[row][bitcol] = (byte) (scanlines[row][bitcol] + scanlines[getPreviousRow(
1794: row, pass)][bitcol]) & 0xff;
1795: }
1796: }
1797: if (interlaceMethod == 1)
1798: col += col_Increment[pass];
1799: else
1800: col++;
1801: }
1802: break;
1803: case 3: // average
1804: if (interlaceMethod == 1)
1805: col = starting_Col[pass];
1806: else
1807: col = 0;
1808: if (row > 0) {
1809: for (int i = 0; i < bytesPerPixel; i++) {
1810: int bitcol = (col * bytesPerPixel) + i;
1811: if (bitcol < scanlines[row].length) {
1812: scanlines[row][bitcol] = (byte) (scanlines[row][bitcol] + (scanlines[getPreviousRow(
1813: row, pass)][bitcol] / 2)) & 0xff;
1814: }
1815: }
1816: }
1817: if (interlaceMethod == 1)
1818: col += col_Increment[pass];
1819: else
1820: col++;
1821: while (col < width) {
1822: for (int i = 0; i < bytesPerPixel; i++) {
1823: int bitcol = (col * bytesPerPixel) + i;
1824: if (bitcol < scanlines[row].length) {
1825: scanlines[row][bitcol] = (byte) (scanlines[row][bitcol] + (scanlines[row][(getPreviousCol(
1826: col, pass) * bytesPerPixel)
1827: + i] + ((row == 0) ? 0
1828: : scanlines[getPreviousRow(row,
1829: pass)][bitcol])) / 2) & 0xff;
1830: }
1831: }
1832: if (interlaceMethod == 1)
1833: col += col_Increment[pass];
1834: else
1835: col++;
1836: }
1837: break;
1838: case 4: // paeth
1839: if (row == 0) {
1840: if (interlaceMethod == 1)
1841: col = starting_Col[pass];
1842: else
1843: col = 0;
1844: for (int i = 0; i < bytesPerPixel; i++) {
1845: int bitcol = (col * bytesPerPixel) + i;
1846: if (bitcol < scanlines[row].length)
1847: scanlines[row][bitcol] = 0xff & scanlines[row][bitcol];
1848: }
1849: if (interlaceMethod == 1)
1850: col += col_Increment[pass];
1851: else
1852: col++;
1853:
1854: while (col < width) {
1855: for (int i = 0; i < bytesPerPixel; i++) {
1856: int bitcol = (col * bytesPerPixel) + i;
1857: if (bitcol < scanlines[row].length) {
1858: p = scanlines[row][(getPreviousCol(
1859: col, pass) * bytesPerPixel)
1860: + i];
1861: scanlines[row][bitcol] = 0xff & (byte) (p + scanlines[row][bitcol]);
1862: }
1863: }
1864: if (interlaceMethod == 1)
1865: col += col_Increment[pass];
1866: else
1867: col++;
1868: }
1869: } else {
1870: if (interlaceMethod == 1)
1871: col = starting_Col[pass];
1872: else
1873: col = 0;
1874: for (int i = 0; i < bytesPerPixel; i++) {
1875: int bitcol = (col * bytesPerPixel) + i;
1876: if (bitcol < scanlines[row].length)
1877: scanlines[row][bitcol] = 0xff & (byte) (scanlines[getPreviousRow(
1878: row, pass)][bitcol] + scanlines[row][bitcol]);
1879: }
1880: if (interlaceMethod == 1)
1881: col += col_Increment[pass];
1882: else
1883: col++;
1884:
1885: while (col < width) {
1886: for (int i = 0; i < bytesPerPixel; i++) {
1887: int bitcol = (col * bytesPerPixel) + i;
1888: if (bitcol < scanlines[row].length) {
1889: a = scanlines[row][(getPreviousCol(
1890: col, pass) * bytesPerPixel)
1891: + i]; // left
1892: b = scanlines[getPreviousRow(row,
1893: pass)][bitcol]; // above
1894: c = scanlines[getPreviousRow(row,
1895: pass)][(getPreviousCol(col,
1896: pass) * bytesPerPixel)
1897: + i]; // upper left
1898:
1899: // Paeth tests
1900: p = (a + b) - c; // initial estimate
1901: pa = p > a ? p - a : a - p; // nearest distance to a
1902: pb = p > b ? p - b : b - p; // nearest distance to b
1903: pc = p > c ? p - c : c - p; // nearest distance to c
1904:
1905: if ((pa <= pb) && (pa <= pc))
1906: p = a;
1907: else if (pb <= pc)
1908: p = b;
1909: else
1910: p = c;
1911:
1912: scanlines[row][bitcol] = 0xff & (byte) (p + scanlines[row][bitcol]);
1913: }
1914: }
1915: if (interlaceMethod == 1)
1916: col += col_Increment[pass];
1917: else
1918: col++;
1919: }
1920: }
1921: break;
1922: }
1923: if (interlaceMethod == 1)
1924: row += row_Increment[pass];
1925: else
1926: row++;
1927: }
1928: }
1929: return scanlines;
1930: }
1931: }
|