From 6025b6016517c6d898d8957d1d7e03ba71431912 Mon Sep 17 00:00:00 2001 From: tknall Date: Fri, 1 Dec 2006 12:20:24 +0000 Subject: Initial import of release 2.2. git-svn-id: https://joinup.ec.europa.eu/svn/pdf-as/trunk@4 7b5415b0-85f9-ee4d-85bd-d5d0c3b42d1c --- .../java/com/lowagie/text/pdf/codec/PngImage.java | 987 +++++++++++++++++++++ 1 file changed, 987 insertions(+) create mode 100644 src/main/java/com/lowagie/text/pdf/codec/PngImage.java (limited to 'src/main/java/com/lowagie/text/pdf/codec/PngImage.java') diff --git a/src/main/java/com/lowagie/text/pdf/codec/PngImage.java b/src/main/java/com/lowagie/text/pdf/codec/PngImage.java new file mode 100644 index 0000000..b82e17b --- /dev/null +++ b/src/main/java/com/lowagie/text/pdf/codec/PngImage.java @@ -0,0 +1,987 @@ +/* + * Copyright 2003 by Paulo Soares. + * + * The contents of this file are subject to the Mozilla Public License Version 1.1 + * (the "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the License. + * + * The Original Code is 'iText, a free JAVA-PDF library'. + * + * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by + * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie. + * All Rights Reserved. + * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer + * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved. + * + * Contributor(s): all the names of the contributors are added in the source code + * where applicable. + * + * Alternatively, the contents of this file may be used under the terms of the + * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the + * provisions of LGPL are applicable instead of those above. If you wish to + * allow use of your version of this file only under the terms of the LGPL + * License and not to allow others to use your version of this file under + * the MPL, indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by the LGPL. + * If you do not delete the provisions above, a recipient may use your version + * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE. + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the MPL as stated above or under the terms of the GNU + * Library General Public License as published by the Free Software Foundation; + * either version 2 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Library general Public License for more + * details. + * + * If you didn't download this code from the following link, you should check if + * you aren't using an obsolete version: + * http://www.lowagie.com/iText/ + * + * + * The original JAI codecs have the following license + * + * Copyright (c) 2001 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * -Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * -Redistribution in binary form must reproduct the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY + * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR + * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS + * LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, + * INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER + * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF + * OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that Software is not designed,licensed or intended for use in + * the design, construction, operation or maintenance of any nuclear facility. + */ + +package com.lowagie.text.pdf.codec; + +import java.awt.color.ICC_Profile; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.zip.Inflater; +import java.util.zip.InflaterInputStream; + +import com.lowagie.text.ExceptionConverter; +import com.lowagie.text.Image; +import com.lowagie.text.ImgRaw; +import com.lowagie.text.pdf.ByteBuffer; +import com.lowagie.text.pdf.PdfArray; +import com.lowagie.text.pdf.PdfDictionary; +import com.lowagie.text.pdf.PdfLiteral; +import com.lowagie.text.pdf.PdfName; +import com.lowagie.text.pdf.PdfNumber; +import com.lowagie.text.pdf.PdfObject; +import com.lowagie.text.pdf.PdfReader; +import com.lowagie.text.pdf.PdfString; + +/** Reads a PNG image. All types of PNG can be read. + *

+ * It is based in part in the JAI codec. + * + * @author Paulo Soares (psoares@consiste.pt) + */ +public class PngImage { +/** Some PNG specific values. */ + public static final int[] PNGID = {137, 80, 78, 71, 13, 10, 26, 10}; + +/** A PNG marker. */ + public static final String IHDR = "IHDR"; + +/** A PNG marker. */ + public static final String PLTE = "PLTE"; + +/** A PNG marker. */ + public static final String IDAT = "IDAT"; + +/** A PNG marker. */ + public static final String IEND = "IEND"; + +/** A PNG marker. */ + public static final String tRNS = "tRNS"; + +/** A PNG marker. */ + public static final String pHYs = "pHYs"; + +/** A PNG marker. */ + public static final String gAMA = "gAMA"; + +/** A PNG marker. */ + public static final String cHRM = "cHRM"; + +/** A PNG marker. */ + public static final String sRGB = "sRGB"; + +/** A PNG marker. */ + public static final String iCCP = "iCCP"; + + private static final int TRANSFERSIZE = 4096; + private static final int PNG_FILTER_NONE = 0; + private static final int PNG_FILTER_SUB = 1; + private static final int PNG_FILTER_UP = 2; + private static final int PNG_FILTER_AVERAGE = 3; + private static final int PNG_FILTER_PAETH = 4; + private static final PdfName intents[] = {PdfName.PERCEPTUAL, + PdfName.RELATIVECALORIMETRIC,PdfName.SATURATION,PdfName.ABSOLUTECALORIMETRIC}; + + InputStream is; + DataInputStream dataStream; + int width; + int height; + int bitDepth; + int colorType; + int compressionMethod; + int filterMethod; + int interlaceMethod; + PdfDictionary additional = new PdfDictionary(); + byte image[]; + byte smask[]; + byte trans[]; + NewByteArrayOutputStream idat = new NewByteArrayOutputStream(); + int dpiX; + int dpiY; + float XYRatio; + boolean genBWMask; + boolean palShades; + int transRedGray = -1; + int transGreen = -1; + int transBlue = -1; + int inputBands; + int bytesPerPixel; // number of bytes per input pixel + byte colorTable[]; + float gamma = 1f; + boolean hasCHRM = false; + float xW, yW, xR, yR, xG, yG, xB, yB; + PdfName intent; + ICC_Profile icc_profile; + + + + /** Creates a new instance of PngImage */ + PngImage(InputStream is) { + this.is = is; + } + + /** Reads a PNG from an url. + * @param url the url + * @throws IOException on error + * @return the image + */ + public static Image getImage(URL url) throws IOException { + InputStream is = null; + try { + is = url.openStream(); + Image img = getImage(is); + img.setUrl(url); + return img; + } + finally { + if (is != null) { + is.close(); + } + } + } + + /** Reads a PNG from a stream. + * @param is the stream + * @throws IOException on error + * @return the image + */ + public static Image getImage(InputStream is) throws IOException { + PngImage png = new PngImage(is); + return png.getImage(); + } + + /** Reads a PNG from a file. + * @param file the file + * @throws IOException on error + * @return the image + */ + public static Image getImage(String file) throws IOException { + return getImage(Image.toURL(file)); + } + + /** Reads a PNG from a byte array. + * @param data the byte array + * @throws IOException on error + * @return the image + */ + public static Image getImage(byte data[]) throws IOException { + InputStream is = null; + try { + is = new ByteArrayInputStream(data); + Image img = getImage(is); + img.setOriginalData(data); + return img; + } + finally { + if (is != null) { + is.close(); + } + } + } + + boolean checkMarker(String s) { + if (s.length() != 4) + return false; + for (int k = 0; k < 4; ++k) { + char c = s.charAt(k); + if ((c < 'a' || c > 'z') && (c < 'A' || c > 'Z')) + return false; + } + return true; + } + + void readPng() throws IOException { + for (int i = 0; i < PNGID.length; i++) { + if (PNGID[i] != is.read()) { + throw new IOException("File is not a valid PNG."); + } + } + byte buffer[] = new byte[TRANSFERSIZE]; + while (true) { + int len = getInt(is); + String marker = getString(is); + if (len < 0 || !checkMarker(marker)) + throw new IOException("Corrupted PNG file."); + if (IDAT.equals(marker)) { + int size; + while (len != 0) { + size = is.read(buffer, 0, Math.min(len, TRANSFERSIZE)); + if (size < 0) + return; + idat.write(buffer, 0, size); + len -= size; + } + } + else if (tRNS.equals(marker)) { + switch (colorType) { + case 0: + if (len >= 2) { + len -= 2; + int gray = getWord(is); + if (bitDepth == 16) + transRedGray = gray; + else + additional.put(PdfName.MASK, new PdfLiteral("["+gray+" "+gray+"]")); + } + break; + case 2: + if (len >= 6) { + len -= 6; + int red = getWord(is); + int green = getWord(is); + int blue = getWord(is); + if (bitDepth == 16) { + transRedGray = red; + transGreen = green; + transBlue = blue; + } + else + additional.put(PdfName.MASK, new PdfLiteral("["+red+" "+red+" "+green+" "+green+" "+blue+" "+blue+"]")); + } + break; + case 3: + if (len > 0) { + trans = new byte[len]; + for (int k = 0; k < len; ++k) + trans[k] = (byte)is.read(); + len = 0; + } + break; + } + Image.skip(is, len); + } + else if (IHDR.equals(marker)) { + width = getInt(is); + height = getInt(is); + + bitDepth = is.read(); + colorType = is.read(); + compressionMethod = is.read(); + filterMethod = is.read(); + interlaceMethod = is.read(); + } + else if (PLTE.equals(marker)) { + if (colorType == 3) { + PdfArray colorspace = new PdfArray(); + colorspace.add(PdfName.INDEXED); + colorspace.add(getColorspace()); + colorspace.add(new PdfNumber(len / 3 - 1)); + ByteBuffer colortable = new ByteBuffer(); + while ((len--) > 0) { + colortable.append_i(is.read()); + } + colorspace.add(new PdfString(colorTable = colortable.toByteArray())); + additional.put(PdfName.COLORSPACE, colorspace); + } + else { + Image.skip(is, len); + } + } + else if (pHYs.equals(marker)) { + int dx = getInt(is); + int dy = getInt(is); + int unit = is.read(); + if (unit == 1) { + dpiX = (int)((float)dx * 0.0254f); + dpiY = (int)((float)dy * 0.0254f); + } + else { + if (dy != 0) + XYRatio = (float)dx / (float)dy; + } + } + else if (cHRM.equals(marker)) { + xW = (float)getInt(is) / 100000f; + yW = (float)getInt(is) / 100000f; + xR = (float)getInt(is) / 100000f; + yR = (float)getInt(is) / 100000f; + xG = (float)getInt(is) / 100000f; + yG = (float)getInt(is) / 100000f; + xB = (float)getInt(is) / 100000f; + yB = (float)getInt(is) / 100000f; + hasCHRM = !(Math.abs(xW)<0.0001f||Math.abs(yW)<0.0001f||Math.abs(xR)<0.0001f||Math.abs(yR)<0.0001f||Math.abs(xG)<0.0001f||Math.abs(yG)<0.0001f||Math.abs(xB)<0.0001f||Math.abs(yB)<0.0001f); + } + else if (sRGB.equals(marker)) { + int ri = is.read(); + intent = intents[ri]; + gamma = 2.2f; + xW = 0.3127f; + yW = 0.329f; + xR = 0.64f; + yR = 0.33f; + xG = 0.3f; + yG = 0.6f; + xB = 0.15f; + yB = 0.06f; + hasCHRM = true; + } + else if (gAMA.equals(marker)) { + int gm = getInt(is); + if (gm != 0) { + gamma = 100000f / (float)gm; + if (!hasCHRM) { + xW = 0.3127f; + yW = 0.329f; + xR = 0.64f; + yR = 0.33f; + xG = 0.3f; + yG = 0.6f; + xB = 0.15f; + yB = 0.06f; + hasCHRM = true; + } + } + } + else if (iCCP.equals(marker)) { + do { + --len; + } while (is.read() != 0); + is.read(); + --len; + byte icccom[] = new byte[len]; + int p = 0; + while (len > 0) { + int r = is.read(icccom, p, len); + if (r < 0) + throw new IOException("Premature end of file."); + p += r; + len -= r; + } + byte iccp[] = PdfReader.FlateDecode(icccom, true); + icccom = null; + try { + icc_profile = ICC_Profile.getInstance(iccp); + } + catch (Exception e) { + icc_profile = null; + } + } + else if (IEND.equals(marker)) { + break; + } + else { + Image.skip(is, len); + } + Image.skip(is, 4); + } + } + + PdfObject getColorspace() { + if (icc_profile != null) { + if ((colorType & 2) == 0) + return PdfName.DEVICEGRAY; + else + return PdfName.DEVICERGB; + } + if (gamma == 1f && !hasCHRM) { + if ((colorType & 2) == 0) + return PdfName.DEVICEGRAY; + else + return PdfName.DEVICERGB; + } + else { + PdfArray array = new PdfArray(); + PdfDictionary dic = new PdfDictionary(); + if ((colorType & 2) == 0) { + if (gamma == 1f) + return PdfName.DEVICEGRAY; + array.add(PdfName.CALGRAY); + dic.put(PdfName.GAMMA, new PdfNumber(gamma)); + dic.put(PdfName.WHITEPOINT, new PdfLiteral("[1 1 1]")); + array.add(dic); + } + else { + PdfObject wp = new PdfLiteral("[1 1 1]"); + array.add(PdfName.CALRGB); + if (gamma != 1f) { + PdfArray gm = new PdfArray(); + PdfNumber n = new PdfNumber(gamma); + gm.add(n); + gm.add(n); + gm.add(n); + dic.put(PdfName.GAMMA, gm); + } + if (hasCHRM) { + float z = yW*((xG-xB)*yR-(xR-xB)*yG+(xR-xG)*yB); + float YA = yR*((xG-xB)*yW-(xW-xB)*yG+(xW-xG)*yB)/z; + float XA = YA*xR/yR; + float ZA = YA*((1-xR)/yR-1); + float YB = -yG*((xR-xB)*yW-(xW-xB)*yR+(xW-xR)*yB)/z; + float XB = YB*xG/yG; + float ZB = YB*((1-xG)/yG-1); + float YC = yB*((xR-xG)*yW-(xW-xG)*yW+(xW-xR)*yG)/z; + float XC = YC*xB/yB; + float ZC = YC*((1-xB)/yB-1); + float XW = XA+XB+XC; + float YW = 1;//YA+YB+YC; + float ZW = ZA+ZB+ZC; + PdfArray wpa = new PdfArray(); + wpa.add(new PdfNumber(XW)); + wpa.add(new PdfNumber(YW)); + wpa.add(new PdfNumber(ZW)); + wp = wpa; + PdfArray matrix = new PdfArray(); + matrix.add(new PdfNumber(XA)); + matrix.add(new PdfNumber(YA)); + matrix.add(new PdfNumber(ZA)); + matrix.add(new PdfNumber(XB)); + matrix.add(new PdfNumber(YB)); + matrix.add(new PdfNumber(ZB)); + matrix.add(new PdfNumber(XC)); + matrix.add(new PdfNumber(YC)); + matrix.add(new PdfNumber(ZC)); + dic.put(PdfName.MATRIX, matrix); + } + dic.put(PdfName.WHITEPOINT, wp); + array.add(dic); + } + return array; + } + } + + Image getImage() throws IOException { + readPng(); + try { + int pal0 = 0; + int palIdx = 0; + palShades = false; + if (trans != null) { + for (int k = 0; k < trans.length; ++k) { + int n = trans[k] & 0xff; + if (n == 0) { + ++pal0; + palIdx = k; + } + if (n != 0 && n != 255) { + palShades = true; + break; + } + } + } + if ((colorType & 4) != 0) + palShades = true; + genBWMask = (!palShades && (pal0 > 1 || transRedGray >= 0)); + if (!palShades && !genBWMask && pal0 == 1) { + additional.put(PdfName.MASK, new PdfLiteral("["+palIdx+" "+palIdx+"]")); + } + boolean needDecode = (interlaceMethod == 1) || (bitDepth == 16) || ((colorType & 4) != 0) || palShades || genBWMask; + switch (colorType) { + case 0: + inputBands = 1; + break; + case 2: + inputBands = 3; + break; + case 3: + inputBands = 1; + break; + case 4: + inputBands = 2; + break; + case 6: + inputBands = 4; + break; + } + if (needDecode) + decodeIdat(); + int components = inputBands; + if ((colorType & 4) != 0) + --components; + int bpc = bitDepth; + if (bpc == 16) + bpc = 8; + Image img; + if (image != null) + img = Image.getInstance(width, height, components, bpc, image); + else { + img = new ImgRaw(width, height, components, bpc, idat.toByteArray()); + img.setDeflated(true); + PdfDictionary decodeparms = new PdfDictionary(); + decodeparms.put(PdfName.BITSPERCOMPONENT, new PdfNumber(bitDepth)); + decodeparms.put(PdfName.PREDICTOR, new PdfNumber(15)); + decodeparms.put(PdfName.COLUMNS, new PdfNumber(width)); + decodeparms.put(PdfName.COLORS, new PdfNumber((colorType == 3 || (colorType & 2) == 0) ? 1 : 3)); + additional.put(PdfName.DECODEPARMS, decodeparms); + } + if (additional.get(PdfName.COLORSPACE) == null) + additional.put(PdfName.COLORSPACE, getColorspace()); + if (intent != null) + additional.put(PdfName.INTENT, intent); + if (additional.size() > 0) + img.setAdditional(additional); + if (icc_profile != null) + img.tagICC(icc_profile); + if (palShades) { + Image im2 = Image.getInstance(width, height, 1, 8, smask); + im2.makeMask(); + img.setImageMask(im2); + } + if (genBWMask) { + Image im2 = Image.getInstance(width, height, 1, 1, smask); + im2.makeMask(); + img.setImageMask(im2); + } + img.setDpi(dpiX, dpiY); + img.setXYRatio(XYRatio); + img.setOriginalType(Image.ORIGINAL_PNG); + return img; + } + catch (Exception e) { + throw new ExceptionConverter(e); + } + } + + void decodeIdat() { + int nbitDepth = bitDepth; + if (nbitDepth == 16) + nbitDepth = 8; + int size = -1; + bytesPerPixel = (bitDepth == 16) ? 2 : 1; + switch (colorType) { + case 0: + size = (nbitDepth * width + 7) / 8 * height; + break; + case 2: + size = width * 3 * height; + bytesPerPixel *= 3; + break; + case 3: + if (interlaceMethod == 1) + size = (nbitDepth * width + 7) / 8 * height; + bytesPerPixel = 1; + break; + case 4: + size = width * height; + bytesPerPixel *= 2; + break; + case 6: + size = width * 3 * height; + bytesPerPixel *= 4; + break; + } + if (size >= 0) + image = new byte[size]; + if (palShades) + smask = new byte[width * height]; + else if (genBWMask) + smask = new byte[(width + 7) / 8 * height]; + ByteArrayInputStream bai = new ByteArrayInputStream(idat.getBuf(), 0, idat.size()); + InputStream infStream = new InflaterInputStream(bai, new Inflater()); + dataStream = new DataInputStream(infStream); + + if (interlaceMethod != 1) { + decodePass(0, 0, 1, 1, width, height); + } + else { + decodePass(0, 0, 8, 8, (width + 7)/8, (height + 7)/8); + decodePass(4, 0, 8, 8, (width + 3)/8, (height + 7)/8); + decodePass(0, 4, 4, 8, (width + 3)/4, (height + 3)/8); + decodePass(2, 0, 4, 4, (width + 1)/4, (height + 3)/4); + decodePass(0, 2, 2, 4, (width + 1)/2, (height + 1)/4); + decodePass(1, 0, 2, 2, width/2, (height + 1)/2); + decodePass(0, 1, 1, 2, width, height/2); + } + + } + + void decodePass( int xOffset, int yOffset, + int xStep, int yStep, + int passWidth, int passHeight) { + if ((passWidth == 0) || (passHeight == 0)) { + return; + } + + int bytesPerRow = (inputBands*passWidth*bitDepth + 7)/8; + byte[] curr = new byte[bytesPerRow]; + byte[] prior = new byte[bytesPerRow]; + + // Decode the (sub)image row-by-row + int srcY, dstY; + for (srcY = 0, dstY = yOffset; + srcY < passHeight; + srcY++, dstY += yStep) { + // Read the filter type byte and a row of data + int filter = 0; + try { + filter = dataStream.read(); + dataStream.readFully(curr, 0, bytesPerRow); + } catch (Exception e) { + // empty on purpose + } + + switch (filter) { + case PNG_FILTER_NONE: + break; + case PNG_FILTER_SUB: + decodeSubFilter(curr, bytesPerRow, bytesPerPixel); + break; + case PNG_FILTER_UP: + decodeUpFilter(curr, prior, bytesPerRow); + break; + case PNG_FILTER_AVERAGE: + decodeAverageFilter(curr, prior, bytesPerRow, bytesPerPixel); + break; + case PNG_FILTER_PAETH: + decodePaethFilter(curr, prior, bytesPerRow, bytesPerPixel); + break; + default: + // Error -- uknown filter type + throw new RuntimeException("PNG filter unknown."); + } + + processPixels(curr, xOffset, xStep, dstY, passWidth); + + // Swap curr and prior + byte[] tmp = prior; + prior = curr; + curr = tmp; + } + } + + void processPixels(byte curr[], int xOffset, int step, int y, int width) { + int srcX, dstX; + + int out[] = getPixel(curr); + int sizes = 0; + switch (colorType) { + case 0: + case 3: + case 4: + sizes = 1; + break; + case 2: + case 6: + sizes = 3; + break; + } + if (image != null) { + dstX = xOffset; + int yStride = (sizes*this.width*(bitDepth == 16 ? 8 : bitDepth)+ 7)/8; + for (srcX = 0; srcX < width; srcX++) { + setPixel(image, out, inputBands * srcX, sizes, dstX, y, bitDepth, yStride); + dstX += step; + } + } + if (palShades) { + if ((colorType & 4) != 0) { + if (bitDepth == 16) { + for (int k = 0; k < width; ++k) + out[k * inputBands + sizes] >>>= 8; + } + int yStride = this.width; + dstX = xOffset; + for (srcX = 0; srcX < width; srcX++) { + setPixel(smask, out, inputBands * srcX + sizes, 1, dstX, y, 8, yStride); + dstX += step; + } + } + else { //colorType 3 + int yStride = this.width; + int v[] = new int[1]; + dstX = xOffset; + for (srcX = 0; srcX < width; srcX++) { + int idx = out[srcX]; + if (idx < trans.length) + v[0] = trans[idx]; + setPixel(smask, v, 0, 1, dstX, y, 8, yStride); + dstX += step; + } + } + } + else if (genBWMask) { + switch (colorType) { + case 3: { + int yStride = (this.width + 7) / 8; + int v[] = new int[1]; + dstX = xOffset; + for (srcX = 0; srcX < width; srcX++) { + int idx = out[srcX]; + if (idx < trans.length) + v[0] = (trans[idx] == 0 ? 1 : 0); + setPixel(smask, v, 0, 1, dstX, y, 1, yStride); + dstX += step; + } + break; + } + case 0: { + int yStride = (this.width + 7) / 8; + int v[] = new int[1]; + dstX = xOffset; + for (srcX = 0; srcX < width; srcX++) { + int g = out[srcX]; + v[0] = (g == transRedGray ? 1 : 0); + setPixel(smask, v, 0, 1, dstX, y, 1, yStride); + dstX += step; + } + break; + } + case 2: { + int yStride = (this.width + 7) / 8; + int v[] = new int[1]; + dstX = xOffset; + for (srcX = 0; srcX < width; srcX++) { + int markRed = inputBands * srcX; + v[0] = (out[markRed] == transRedGray && out[markRed + 1] == transGreen + && out[markRed + 2] == transBlue ? 1 : 0); + setPixel(smask, v, 0, 1, dstX, y, 1, yStride); + dstX += step; + } + break; + } + } + } + } + + static int getPixel(byte image[], int x, int y, int bitDepth, int bytesPerRow) { + if (bitDepth == 8) { + int pos = bytesPerRow * y + x; + return image[pos] & 0xff; + } + else { + int pos = bytesPerRow * y + x / (8 / bitDepth); + int v = image[pos] >> (8 - bitDepth * (x % (8 / bitDepth))- bitDepth); + return v & ((1 << bitDepth) - 1); + } + } + + static void setPixel(byte image[], int data[], int offset, int size, int x, int y, int bitDepth, int bytesPerRow) { + if (bitDepth == 8) { + int pos = bytesPerRow * y + size * x; + for (int k = 0; k < size; ++k) + image[pos + k] = (byte)data[k + offset]; + } + else if (bitDepth == 16) { + int pos = bytesPerRow * y + size * x; + for (int k = 0; k < size; ++k) + image[pos + k] = (byte)(data[k + offset] >>> 8); + } + else { + int pos = bytesPerRow * y + x / (8 / bitDepth); + int v = data[offset] << (8 - bitDepth * (x % (8 / bitDepth))- bitDepth); + image[pos] |= v; + } + } + + int[] getPixel(byte curr[]) { + switch (bitDepth) { + case 8: { + int out[] = new int[curr.length]; + for (int k = 0; k < out.length; ++k) + out[k] = curr[k] & 0xff; + return out; + } + case 16: { + int out[] = new int[curr.length / 2]; + for (int k = 0; k < out.length; ++k) + out[k] = ((curr[k * 2] & 0xff) << 8) + (curr[k * 2 + 1] & 0xff); + return out; + } + default: { + int out[] = new int[curr.length * 8 / bitDepth]; + int idx = 0; + int passes = 8 / bitDepth; + int mask = (1 << bitDepth) - 1; + for (int k = 0; k < curr.length; ++k) { + for (int j = passes - 1; j >= 0; --j) { + out[idx++] = (curr[k] >>> (bitDepth * j)) & mask; + } + } + return out; + } + } + } + + private static void decodeSubFilter(byte[] curr, int count, int bpp) { + for (int i = bpp; i < count; i++) { + int val; + + val = curr[i] & 0xff; + val += curr[i - bpp] & 0xff; + + curr[i] = (byte)val; + } + } + + private static void decodeUpFilter(byte[] curr, byte[] prev, + int count) { + for (int i = 0; i < count; i++) { + int raw = curr[i] & 0xff; + int prior = prev[i] & 0xff; + + curr[i] = (byte)(raw + prior); + } + } + + private static void decodeAverageFilter(byte[] curr, byte[] prev, + int count, int bpp) { + int raw, priorPixel, priorRow; + + for (int i = 0; i < bpp; i++) { + raw = curr[i] & 0xff; + priorRow = prev[i] & 0xff; + + curr[i] = (byte)(raw + priorRow/2); + } + + for (int i = bpp; i < count; i++) { + raw = curr[i] & 0xff; + priorPixel = curr[i - bpp] & 0xff; + priorRow = prev[i] & 0xff; + + curr[i] = (byte)(raw + (priorPixel + priorRow)/2); + } + } + + private static int paethPredictor(int a, int b, int c) { + int p = a + b - c; + int pa = Math.abs(p - a); + int pb = Math.abs(p - b); + int pc = Math.abs(p - c); + + if ((pa <= pb) && (pa <= pc)) { + return a; + } else if (pb <= pc) { + return b; + } else { + return c; + } + } + + private static void decodePaethFilter(byte[] curr, byte[] prev, + int count, int bpp) { + int raw, priorPixel, priorRow, priorRowPixel; + + for (int i = 0; i < bpp; i++) { + raw = curr[i] & 0xff; + priorRow = prev[i] & 0xff; + + curr[i] = (byte)(raw + priorRow); + } + + for (int i = bpp; i < count; i++) { + raw = curr[i] & 0xff; + priorPixel = curr[i - bpp] & 0xff; + priorRow = prev[i] & 0xff; + priorRowPixel = prev[i - bpp] & 0xff; + + curr[i] = (byte)(raw + paethPredictor(priorPixel, + priorRow, + priorRowPixel)); + } + } + + static class NewByteArrayOutputStream extends ByteArrayOutputStream { + public byte[] getBuf() { + return buf; + } + } + +/** + * Gets an int from an InputStream. + * + * @param is an InputStream + * @return the value of an int + */ + + public static final int getInt(InputStream is) throws IOException { + return (is.read() << 24) + (is.read() << 16) + (is.read() << 8) + is.read(); + } + +/** + * Gets a word from an InputStream. + * + * @param is an InputStream + * @return the value of an int + */ + + public static final int getWord(InputStream is) throws IOException { + return (is.read() << 8) + is.read(); + } + +/** + * Gets a String from an InputStream. + * + * @param is an InputStream + * @return the value of an int + */ + + public static final String getString(InputStream is) throws IOException { + StringBuffer buf = new StringBuffer(); + for (int i = 0; i < 4; i++) { + buf.append((char)is.read()); + } + return buf.toString(); + } + +} -- cgit v1.2.3