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

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

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

import java.awt.Color;
import java.awt.Point;
import java.awt.Transparency;
import java.awt.color.ColorSpace;
import java.awt.image.ColorModel;
import java.awt.image.ComponentColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.DataBufferUShort;
import java.awt.image.IndexColorModel;
import java.awt.image.Raster;
import java.awt.image.RenderedImage;
import java.awt.image.SampleModel;
import java.awt.image.WritableRaster;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.SequenceInputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;
import java.util.zip.Inflater;
import java.util.zip.InflaterInputStream;

import javax.imageio.stream.ImageInputStream;

import org.apache.xmlgraphics.image.codec.util.ImageDecoderImpl;
import org.apache.xmlgraphics.image.codec.util.ImageInputStreamSeekableStreamAdapter;
import org.apache.xmlgraphics.image.codec.util.PropertyUtil;
import org.apache.xmlgraphics.image.codec.util.SeekableStream;
import org.apache.xmlgraphics.image.codec.util.SimpleRenderedImage;
import org.apache.xmlgraphics.image.loader.ImageSize;
import org.apache.xmlgraphics.image.loader.impl.PNGConstants;

// CSOFF: ConstantName
// CSOFF: InnerAssignment
// CSOFF: MethodName
// CSOFF: MissingSwitchDefault
// CSOFF: MultipleVariableDeclarations
// CSOFF: NoWhitespaceAfter
// CSOFF: OperatorWrap
// CSOFF: WhitespaceAround

Version:$Id: PNGImageDecoder.java 1732018 2016-02-24 04:51:06Z gadams $
/** * @version $Id: PNGImageDecoder.java 1732018 2016-02-24 04:51:06Z gadams $ */
public class PNGImageDecoder extends ImageDecoderImpl { public PNGImageDecoder(InputStream input, PNGDecodeParam param) { super(input, param); } @Override public RenderedImage decodeAsRenderedImage(int page) throws IOException { if (page != 0) { throw new IOException(PropertyUtil.getString("PNGImageDecoder19")); } return new PNGImage(input, (PNGDecodeParam)param); } public static void readPNGHeader(ImageInputStream inputStream, ImageSize size) throws IOException { SeekableStream seekStream = new ImageInputStreamSeekableStreamAdapter(inputStream) { public void close() throws IOException { } }; PNGImage pngImage = new PNGImage(seekStream); size.setSizeInPixels(pngImage.getWidth(), pngImage.getHeight()); double dpiHorz = size.getDpiHorizontal(); double dpiVert = size.getDpiVertical(); if (pngImage.unitSpecifier == 1) { if (pngImage.xPixelsPerUnit != 0) { dpiHorz = pngImage.xPixelsPerUnit * 0.0254; } if (pngImage.yPixelsPerUnit != 0) { dpiVert = pngImage.yPixelsPerUnit * 0.0254; } } size.setResolution(dpiHorz, dpiVert); size.calcSizeFromPixels(); } }
TO DO: zTXt chunks
/** * TO DO: * * zTXt chunks * */
class PNGImage extends SimpleRenderedImage implements PNGConstants { private static final String[] colorTypeNames = { "Grayscale", "Error", "Truecolor", "Index", "Grayscale with alpha", "Error", "Truecolor with alpha" }; private int[][] bandOffsets = { null, { 0 }, // G { 0, 1 }, // GA in GA order { 0, 1, 2 }, // RGB in RGB order { 0, 1, 2, 3 } // RGBA in RGBA order }; private int bitDepth; private int colorType; private int compressionMethod; private int filterMethod; private int interlaceMethod; private int paletteEntries; private byte[] redPalette; private byte[] greenPalette; private byte[] bluePalette; private byte[] alphaPalette; private int bkgdRed; private int bkgdGreen; private int bkgdBlue; private int grayTransparentAlpha; private int redTransparentAlpha; private int greenTransparentAlpha; private int blueTransparentAlpha; private int maxOpacity; private int[] significantBits; // Parameter information // If true, the user wants destination alpha where applicable. private boolean suppressAlpha; // If true, perform palette lookup internally private boolean expandPalette; // If true, output < 8 bit gray images in 8 bit components format private boolean output8BitGray; // Create an alpha channel in the destination color model. private boolean outputHasAlphaPalette; // Perform gamma correction on the image private boolean performGammaCorrection; // Expand GA to GGGA for compatbility with Java2D private boolean expandGrayAlpha; // Produce an instance of PNGEncodeParam private boolean generateEncodeParam; // PNGDecodeParam controlling decode process private PNGDecodeParam decodeParam; // PNGEncodeParam to store file details in private PNGEncodeParam encodeParam; private boolean emitProperties = true; private float fileGamma = 45455 / 100000.0F; private float userExponent = 1.0F; private float displayExponent = 2.2F; private float[] chromaticity; private int sRGBRenderingIntent = -1; // Post-processing step implied by above parameters private int postProcess = POST_NONE; protected int xPixelsPerUnit; protected int yPixelsPerUnit; protected int unitSpecifier; // Possible post-processing steps // Do nothing private static final int POST_NONE = 0; // Gamma correct only private static final int POST_GAMMA = 1; // Push gray values through grayLut to expand to 8 bits private static final int POST_GRAY_LUT = 2; // Push gray values through grayLut to expand to 8 bits, add alpha private static final int POST_GRAY_LUT_ADD_TRANS = 3; // Push palette value through R,G,B lookup tables private static final int POST_PALETTE_TO_RGB = 4; // Push palette value through R,G,B,A lookup tables private static final int POST_PALETTE_TO_RGBA = 5; // Add transparency to a given gray value (w/ optional gamma) private static final int POST_ADD_GRAY_TRANS = 6; // Add transparency to a given RGB value (w/ optional gamma) private static final int POST_ADD_RGB_TRANS = 7; // Remove the alpha channel from a gray image (w/ optional gamma) private static final int POST_REMOVE_GRAY_TRANS = 8; // Remove the alpha channel from an RGB image (w/optional gamma) private static final int POST_REMOVE_RGB_TRANS = 9; // Mask to add expansion of GA -> GGGA private static final int POST_EXP_MASK = 16; // Expand gray to G/G/G private static final int POST_GRAY_ALPHA_EXP = POST_NONE | POST_EXP_MASK; // Expand gray to G/G/G through a gamma lut private static final int POST_GAMMA_EXP = POST_GAMMA | POST_EXP_MASK; // Push gray values through grayLut to expand to 8 bits, expand, add alpha private static final int POST_GRAY_LUT_ADD_TRANS_EXP = POST_GRAY_LUT_ADD_TRANS | POST_EXP_MASK; // Add transparency to a given gray value, expand private static final int POST_ADD_GRAY_TRANS_EXP = POST_ADD_GRAY_TRANS | POST_EXP_MASK; private List<InputStream> streamVec = new ArrayList<InputStream>(); private DataInputStream dataStream; private int bytesPerPixel; // number of bytes per input pixel private int inputBands; private int outputBands; // Number of private chunks private int chunkIndex; private List textKeys = new ArrayList(); private List textStrings = new ArrayList(); private List ztextKeys = new ArrayList(); private List ztextStrings = new ArrayList(); private WritableRaster theTile; private int[] gammaLut; private void initGammaLut(int bits) { double exp = (double)userExponent / (fileGamma * displayExponent); int numSamples = 1 << bits; int maxOutSample = (bits == 16) ? 65535 : 255; gammaLut = new int[numSamples]; for (int i = 0; i < numSamples; i++) { double gbright = (double)i / (numSamples - 1); double gamma = Math.pow(gbright, exp); int igamma = (int)(gamma * maxOutSample + 0.5); if (igamma > maxOutSample) { igamma = maxOutSample; } gammaLut[i] = igamma; } } private final byte[][] expandBits = { null, { (byte)0x00, (byte)0xff }, { (byte)0x00, (byte)0x55, (byte)0xaa, (byte)0xff }, null, { (byte)0x00, (byte)0x11, (byte)0x22, (byte)0x33, (byte)0x44, (byte)0x55, (byte)0x66, (byte)0x77, (byte)0x88, (byte)0x99, (byte)0xaa, (byte)0xbb, (byte)0xcc, (byte)0xdd, (byte)0xee, (byte)0xff } }; private int[] grayLut; private void initGrayLut(int bits) { int len = 1 << bits; grayLut = new int[len]; if (performGammaCorrection) { System.arraycopy(gammaLut, 0, grayLut, 0, len); } else { for (int i = 0; i < len; i++) { grayLut[i] = expandBits[bits][i]; } } } public PNGImage(InputStream stream) throws IOException { DataInputStream distream = new DataInputStream(stream); long magic = distream.readLong(); if (magic != PNG_SIGNATURE) { throw new IOException("Not a png file"); } while (true) { String chunkType = PNGChunk.getChunkType(distream); if (chunkType.equals(PNGChunk.ChunkType.IHDR.name())) { PNGChunk chunk = PNGChunk.readChunk(distream); parse_IHDR_chunk(chunk); } else if (chunkType.equals(PNGChunk.ChunkType.pHYs.name())) { PNGChunk chunk = PNGChunk.readChunk(distream); parse_pHYs_chunk(chunk); return; } else if (chunkType.equals(PNGChunk.ChunkType.IEND.name())) { return; } else { PNGChunk.readChunk(distream); } } } public PNGImage(InputStream stream, PNGDecodeParam decodeParam) throws IOException { if (!stream.markSupported()) { stream = new BufferedInputStream(stream); } DataInputStream distream = new DataInputStream(stream); if (decodeParam == null) { decodeParam = new PNGDecodeParam(); } this.decodeParam = decodeParam; // Get parameter values this.suppressAlpha = decodeParam.getSuppressAlpha(); this.expandPalette = decodeParam.getExpandPalette(); this.output8BitGray = decodeParam.getOutput8BitGray(); this.expandGrayAlpha = decodeParam.getExpandGrayAlpha(); if (decodeParam.getPerformGammaCorrection()) { this.userExponent = decodeParam.getUserExponent(); this.displayExponent = decodeParam.getDisplayExponent(); performGammaCorrection = true; output8BitGray = true; } this.generateEncodeParam = decodeParam.getGenerateEncodeParam(); if (emitProperties) { properties.put("file_type", "PNG v. 1.0"); } try { long magic = distream.readLong(); if (magic != PNG_SIGNATURE) { String msg = PropertyUtil.getString("PNGImageDecoder0"); throw new RuntimeException(msg); } } catch (IOException ioe) { ioe.printStackTrace(); String msg = PropertyUtil.getString("PNGImageDecoder1"); throw new RuntimeException(msg); } do { // try { PNGChunk chunk; String chunkType = PNGChunk.getChunkType(distream); if (chunkType.equals(PNGChunk.ChunkType.IHDR.name())) { chunk = PNGChunk.readChunk(distream); parse_IHDR_chunk(chunk); } else if (chunkType.equals(PNGChunk.ChunkType.PLTE.name())) { chunk = PNGChunk.readChunk(distream); parse_PLTE_chunk(chunk); } else if (chunkType.equals(PNGChunk.ChunkType.IDAT.name())) { chunk = PNGChunk.readChunk(distream); streamVec.add(new ByteArrayInputStream(chunk.getData())); } else if (chunkType.equals(PNGChunk.ChunkType.IEND.name())) { chunk = PNGChunk.readChunk(distream); try { parse_IEND_chunk(chunk); } catch (Exception e) { e.printStackTrace(); String msg = PropertyUtil.getString("PNGImageDecoder2"); throw new RuntimeException(msg); } break; // fall through to the bottom } else if (chunkType.equals(PNGChunk.ChunkType.bKGD.name())) { chunk = PNGChunk.readChunk(distream); parse_bKGD_chunk(chunk); } else if (chunkType.equals(PNGChunk.ChunkType.cHRM.name())) { chunk = PNGChunk.readChunk(distream); parse_cHRM_chunk(chunk); } else if (chunkType.equals(PNGChunk.ChunkType.gAMA.name())) { chunk = PNGChunk.readChunk(distream); parse_gAMA_chunk(chunk); } else if (chunkType.equals(PNGChunk.ChunkType.hIST.name())) { chunk = PNGChunk.readChunk(distream); parse_hIST_chunk(chunk); } else if (chunkType.equals(PNGChunk.ChunkType.iCCP.name())) { chunk = PNGChunk.readChunk(distream); parse_iCCP_chunk(chunk); } else if (chunkType.equals(PNGChunk.ChunkType.pHYs.name())) { chunk = PNGChunk.readChunk(distream); parse_pHYs_chunk(chunk); } else if (chunkType.equals(PNGChunk.ChunkType.sBIT.name())) { chunk = PNGChunk.readChunk(distream); parse_sBIT_chunk(chunk); } else if (chunkType.equals(PNGChunk.ChunkType.sRGB.name())) { chunk = PNGChunk.readChunk(distream); parse_sRGB_chunk(chunk); } else if (chunkType.equals(PNGChunk.ChunkType.tEXt.name())) { chunk = PNGChunk.readChunk(distream); parse_tEXt_chunk(chunk); } else if (chunkType.equals(PNGChunk.ChunkType.tIME.name())) { chunk = PNGChunk.readChunk(distream); parse_tIME_chunk(chunk); } else if (chunkType.equals(PNGChunk.ChunkType.tRNS.name())) { chunk = PNGChunk.readChunk(distream); parse_tRNS_chunk(chunk); } else if (chunkType.equals(PNGChunk.ChunkType.zTXt.name())) { chunk = PNGChunk.readChunk(distream); parse_zTXt_chunk(chunk); } else { chunk = PNGChunk.readChunk(distream); // Output the chunk data in raw form String type = chunk.getTypeString(); byte[] data = chunk.getData(); if (encodeParam != null) { encodeParam.addPrivateChunk(type, data); } if (emitProperties) { String key = "chunk_" + chunkIndex++ + ':' + type; properties.put(key.toLowerCase(Locale.getDefault()), data); } } // } catch (Exception e) { // e.printStackTrace(); // String msg = PropertyUtil.getString("PNGImageDecoder2"); // throw new RuntimeException(msg); // } } while (true); // Final post-processing if (significantBits == null) { significantBits = new int[inputBands]; for (int i = 0; i < inputBands; i++) { significantBits[i] = bitDepth; } if (emitProperties) { properties.put("significant_bits", significantBits); } } } private void parse_IHDR_chunk(PNGChunk chunk) { tileWidth = width = chunk.getInt4(0); tileHeight = height = chunk.getInt4(4); bitDepth = chunk.getInt1(8); if ((bitDepth != 1) && (bitDepth != 2) && (bitDepth != 4) && (bitDepth != 8) && (bitDepth != 16)) { // Error -- bad bit depth String msg = PropertyUtil.getString("PNGImageDecoder3"); throw new RuntimeException(msg); } maxOpacity = (1 << bitDepth) - 1; colorType = chunk.getInt1(9); if ((colorType != PNG_COLOR_GRAY) && (colorType != PNG_COLOR_RGB) && (colorType != PNG_COLOR_PALETTE) && (colorType != PNG_COLOR_GRAY_ALPHA) && (colorType != PNG_COLOR_RGB_ALPHA)) { System.out.println(PropertyUtil.getString("PNGImageDecoder4")); } if ((colorType == PNG_COLOR_RGB) && (bitDepth < 8)) { // Error -- RGB images must have 8 or 16 bits String msg = PropertyUtil.getString("PNGImageDecoder5"); throw new RuntimeException(msg); } if ((colorType == PNG_COLOR_PALETTE) && (bitDepth == 16)) { // Error -- palette images must have < 16 bits String msg = PropertyUtil.getString("PNGImageDecoder6"); throw new RuntimeException(msg); } if ((colorType == PNG_COLOR_GRAY_ALPHA) && (bitDepth < 8)) { // Error -- gray/alpha images must have >= 8 bits String msg = PropertyUtil.getString("PNGImageDecoder7"); throw new RuntimeException(msg); } if ((colorType == PNG_COLOR_RGB_ALPHA) && (bitDepth < 8)) { // Error -- RGB/alpha images must have >= 8 bits String msg = PropertyUtil.getString("PNGImageDecoder8"); throw new RuntimeException(msg); } if (emitProperties) { properties.put("color_type", colorTypeNames[colorType]); } if (generateEncodeParam) { if (colorType == PNG_COLOR_PALETTE) { encodeParam = new PNGEncodeParam.Palette(); } else if (colorType == PNG_COLOR_GRAY || colorType == PNG_COLOR_GRAY_ALPHA) { encodeParam = new PNGEncodeParam.Gray(); } else { encodeParam = new PNGEncodeParam.RGB(); } decodeParam.setEncodeParam(encodeParam); } if (encodeParam != null) { encodeParam.setBitDepth(bitDepth); } if (emitProperties) { properties.put("bit_depth", bitDepth); } if (performGammaCorrection) { // Assume file gamma is 1/2.2 unless we get a gAMA chunk float gamma = (1.0F / 2.2F) * (displayExponent / userExponent); if (encodeParam != null) { encodeParam.setGamma(gamma); } if (emitProperties) { properties.put("gamma", gamma); } } compressionMethod = chunk.getInt1(10); if (compressionMethod != 0) { // Error -- only know about compression method 0 String msg = PropertyUtil.getString("PNGImageDecoder9"); throw new RuntimeException(msg); } filterMethod = chunk.getInt1(11); if (filterMethod != 0) { // Error -- only know about filter method 0 String msg = PropertyUtil.getString("PNGImageDecoder10"); throw new RuntimeException(msg); } interlaceMethod = chunk.getInt1(12); if (interlaceMethod == 0) { if (encodeParam != null) { encodeParam.setInterlacing(false); } if (emitProperties) { properties.put("interlace_method", "None"); } } else if (interlaceMethod == 1) { if (encodeParam != null) { encodeParam.setInterlacing(true); } if (emitProperties) { properties.put("interlace_method", "Adam7"); } } else { // Error -- only know about Adam7 interlacing String msg = PropertyUtil.getString("PNGImageDecoder11"); throw new RuntimeException(msg); } bytesPerPixel = (bitDepth == 16) ? 2 : 1; switch (colorType) { case PNG_COLOR_GRAY: inputBands = 1; outputBands = 1; if (output8BitGray && (bitDepth < 8)) { postProcess = POST_GRAY_LUT; } else if (performGammaCorrection) { postProcess = POST_GAMMA; } else { postProcess = POST_NONE; } break; case PNG_COLOR_RGB: inputBands = 3; bytesPerPixel *= 3; outputBands = 3; if (performGammaCorrection) { postProcess = POST_GAMMA; } else { postProcess = POST_NONE; } break; case PNG_COLOR_PALETTE: inputBands = 1; bytesPerPixel = 1; outputBands = expandPalette ? 3 : 1; if (expandPalette) { postProcess = POST_PALETTE_TO_RGB; } else { postProcess = POST_NONE; } break; case PNG_COLOR_GRAY_ALPHA: inputBands = 2; bytesPerPixel *= 2; if (suppressAlpha) { outputBands = 1; postProcess = POST_REMOVE_GRAY_TRANS; } else { if (performGammaCorrection) { postProcess = POST_GAMMA; } else { postProcess = POST_NONE; } if (expandGrayAlpha) { postProcess |= POST_EXP_MASK; outputBands = 4; } else { outputBands = 2; } } break; case PNG_COLOR_RGB_ALPHA: inputBands = 4; bytesPerPixel *= 4; outputBands = (!suppressAlpha) ? 4 : 3; if (suppressAlpha) { postProcess = POST_REMOVE_RGB_TRANS; } else if (performGammaCorrection) { postProcess = POST_GAMMA; } else { postProcess = POST_NONE; } break; } } private void parse_IEND_chunk(PNGChunk chunk) throws Exception { // Store text strings int textLen = textKeys.size(); String[] textArray = new String[2 * textLen]; for (int i = 0; i < textLen; i++) { String key = (String)textKeys.get(i); String val = (String)textStrings.get(i); textArray[2 * i] = key; textArray[2 * i + 1] = val; if (emitProperties) { String uniqueKey = "text_" + i + ':' + key; properties.put(uniqueKey.toLowerCase(Locale.getDefault()), val); } } if (encodeParam != null) { encodeParam.setText(textArray); } // Store compressed text strings int ztextLen = ztextKeys.size(); String[] ztextArray = new String[2 * ztextLen]; for (int i = 0; i < ztextLen; i++) { String key = (String)ztextKeys.get(i); String val = (String)ztextStrings.get(i); ztextArray[2 * i] = key; ztextArray[2 * i + 1] = val; if (emitProperties) { String uniqueKey = "ztext_" + i + ':' + key; properties.put(uniqueKey.toLowerCase(Locale.getDefault()), val); } } if (encodeParam != null) { encodeParam.setCompressedText(ztextArray); } // Parse prior IDAT chunks InputStream seqStream = new SequenceInputStream(Collections.enumeration(streamVec)); InputStream infStream = new InflaterInputStream(seqStream, new Inflater()); dataStream = new DataInputStream(infStream); // Create an empty WritableRaster int depth = bitDepth; if ((colorType == PNG_COLOR_GRAY) && (bitDepth < 8) && output8BitGray) { depth = 8; } if ((colorType == PNG_COLOR_PALETTE) && expandPalette) { depth = 8; } int bytesPerRow = (outputBands * width * depth + 7) / 8; int scanlineStride = (depth == 16) ? (bytesPerRow / 2) : bytesPerRow; theTile = createRaster(width, height, outputBands, scanlineStride, depth); if (performGammaCorrection && (gammaLut == null)) { initGammaLut(bitDepth); } if ((postProcess == POST_GRAY_LUT) || (postProcess == POST_GRAY_LUT_ADD_TRANS) || (postProcess == POST_GRAY_LUT_ADD_TRANS_EXP)) { initGrayLut(bitDepth); } decodeImage(interlaceMethod == 1); sampleModel = theTile.getSampleModel(); if ((colorType == PNG_COLOR_PALETTE) && !expandPalette) { if (outputHasAlphaPalette) { colorModel = new IndexColorModel(bitDepth, paletteEntries, redPalette, greenPalette, bluePalette, alphaPalette); } else { colorModel = new IndexColorModel(bitDepth, paletteEntries, redPalette, greenPalette, bluePalette); } } else if ((colorType == PNG_COLOR_GRAY) && (bitDepth < 8) && !output8BitGray) { byte[] palette = expandBits[bitDepth]; colorModel = new IndexColorModel(bitDepth, palette.length, palette, palette, palette); } else { colorModel = createComponentColorModel(sampleModel); } } private static final int[] GrayBits8 = { 8 }; private static final ComponentColorModel colorModelGray8 = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_GRAY), GrayBits8, false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE); private static final int[] GrayAlphaBits8 = { 8, 8 }; private static final ComponentColorModel colorModelGrayAlpha8 = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_GRAY), GrayAlphaBits8, true, false, Transparency.TRANSLUCENT, DataBuffer.TYPE_BYTE); private static final int[] GrayBits16 = { 16 }; private static final ComponentColorModel colorModelGray16 = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_GRAY), GrayBits16, false, false, Transparency.OPAQUE, DataBuffer.TYPE_USHORT); private static final int[] GrayAlphaBits16 = { 16, 16 }; private static final ComponentColorModel colorModelGrayAlpha16 = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_GRAY), GrayAlphaBits16, true, false, Transparency.TRANSLUCENT, DataBuffer.TYPE_USHORT); private static final int[] GrayBits32 = { 32 }; private static final ComponentColorModel colorModelGray32 = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_GRAY), GrayBits32, false, false, Transparency.OPAQUE, DataBuffer.TYPE_INT); private static final int[] GrayAlphaBits32 = { 32, 32 }; private static final ComponentColorModel colorModelGrayAlpha32 = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_GRAY), GrayAlphaBits32, true, false, Transparency.TRANSLUCENT, DataBuffer.TYPE_INT); private static final int[] RGBBits8 = { 8, 8, 8 }; private static final ComponentColorModel colorModelRGB8 = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), RGBBits8, false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE); private static final int[] RGBABits8 = { 8, 8, 8, 8 }; private static final ComponentColorModel colorModelRGBA8 = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), RGBABits8, true, false, Transparency.TRANSLUCENT, DataBuffer.TYPE_BYTE); private static final int[] RGBBits16 = { 16, 16, 16 }; private static final ComponentColorModel colorModelRGB16 = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), RGBBits16, false, false, Transparency.OPAQUE, DataBuffer.TYPE_USHORT); private static final int[] RGBABits16 = { 16, 16, 16, 16 }; private static final ComponentColorModel colorModelRGBA16 = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), RGBABits16, true, false, Transparency.TRANSLUCENT, DataBuffer.TYPE_USHORT); private static final int[] RGBBits32 = { 32, 32, 32 }; private static final ComponentColorModel colorModelRGB32 = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), RGBBits32, false, false, Transparency.OPAQUE, DataBuffer.TYPE_INT); private static final int[] RGBABits32 = { 32, 32, 32, 32 }; private static final ComponentColorModel colorModelRGBA32 = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), RGBABits32, true, false, Transparency.TRANSLUCENT, DataBuffer.TYPE_INT);
A convenience method to create an instance of ComponentColorModel suitable for use with the given SampleModel. The SampleModel should have a data type of DataBuffer.TYPE_BYTE, TYPE_USHORT, or TYPE_INT and between 1 and 4 bands. Depending on the number of bands of the SampleModel, either a gray, gray+alpha, rgb, or rgb+alpha ColorModel is returned.
/** * A convenience method to create an instance of * <code>ComponentColorModel</code> suitable for use with the * given <code>SampleModel</code>. The <code>SampleModel</code> * should have a data type of <code>DataBuffer.TYPE_BYTE</code>, * <code>TYPE_USHORT</code>, or <code>TYPE_INT</code> and between * 1 and 4 bands. Depending on the number of bands of the * <code>SampleModel</code>, either a gray, gray+alpha, rgb, or * rgb+alpha <code>ColorModel</code> is returned. */
public static ColorModel createComponentColorModel(SampleModel sm) { int type = sm.getDataType(); int bands = sm.getNumBands(); ComponentColorModel cm = null; if (type == DataBuffer.TYPE_BYTE) { switch (bands) { case 1: cm = colorModelGray8; break; case 2: cm = colorModelGrayAlpha8; break; case 3: cm = colorModelRGB8; break; case 4: cm = colorModelRGBA8; break; } } else if (type == DataBuffer.TYPE_USHORT) { switch (bands) { case 1: cm = colorModelGray16; break; case 2: cm = colorModelGrayAlpha16; break; case 3: cm = colorModelRGB16; break; case 4: cm = colorModelRGBA16; break; } } else if (type == DataBuffer.TYPE_INT) { switch (bands) { case 1: cm = colorModelGray32; break; case 2: cm = colorModelGrayAlpha32; break; case 3: cm = colorModelRGB32; break; case 4: cm = colorModelRGBA32; break; } } return cm; } private void parse_PLTE_chunk(PNGChunk chunk) { paletteEntries = chunk.getLength() / 3; redPalette = new byte[paletteEntries]; greenPalette = new byte[paletteEntries]; bluePalette = new byte[paletteEntries]; int pltIndex = 0; // gAMA chunk must precede PLTE chunk if (performGammaCorrection) { if (gammaLut == null) { initGammaLut(bitDepth == 16 ? 16 : 8); } for (int i = 0; i < paletteEntries; i++) { byte r = chunk.getByte(pltIndex++); byte g = chunk.getByte(pltIndex++); byte b = chunk.getByte(pltIndex++); redPalette[i] = (byte)gammaLut[r & 0xff]; greenPalette[i] = (byte)gammaLut[g & 0xff]; bluePalette[i] = (byte)gammaLut[b & 0xff]; } } else { for (int i = 0; i < paletteEntries; i++) { redPalette[i] = chunk.getByte(pltIndex++); greenPalette[i] = chunk.getByte(pltIndex++); bluePalette[i] = chunk.getByte(pltIndex++); } } } private void parse_bKGD_chunk(PNGChunk chunk) { switch (colorType) { case PNG_COLOR_PALETTE: int bkgdIndex = chunk.getByte(0) & 0xff; bkgdRed = redPalette[bkgdIndex] & 0xff; bkgdGreen = greenPalette[bkgdIndex] & 0xff; bkgdBlue = bluePalette[bkgdIndex] & 0xff; if (encodeParam != null) { ((PNGEncodeParam.Palette)encodeParam).setBackgroundPaletteIndex(bkgdIndex); } break; case PNG_COLOR_GRAY: case PNG_COLOR_GRAY_ALPHA: int bkgdGray = chunk.getInt2(0); bkgdRed = bkgdGreen = bkgdBlue = bkgdGray; if (encodeParam != null) { ((PNGEncodeParam.Gray)encodeParam).setBackgroundGray(bkgdGray); } break; case PNG_COLOR_RGB: case PNG_COLOR_RGB_ALPHA: bkgdRed = chunk.getInt2(0); bkgdGreen = chunk.getInt2(2); bkgdBlue = chunk.getInt2(4); int[] bkgdRGB = new int[3]; bkgdRGB[0] = bkgdRed; bkgdRGB[1] = bkgdGreen; bkgdRGB[2] = bkgdBlue; if (encodeParam != null) { ((PNGEncodeParam.RGB)encodeParam).setBackgroundRGB(bkgdRGB); } break; } int r = 0; int g = 0; int b = 0; if (bitDepth < 8) { r = expandBits[bitDepth][bkgdRed]; g = expandBits[bitDepth][bkgdGreen]; b = expandBits[bitDepth][bkgdBlue]; } else if (bitDepth == 8) { r = bkgdRed; g = bkgdGreen; b = bkgdBlue; } else if (bitDepth == 16) { r = bkgdRed >> 8; g = bkgdGreen >> 8; b = bkgdBlue >> 8; } if (emitProperties) { properties.put("background_color", new Color(r, g, b)); } } private void parse_cHRM_chunk(PNGChunk chunk) { // If an sRGB chunk exists, ignore cHRM chunks if (sRGBRenderingIntent != -1) { return; } chromaticity = new float[8]; chromaticity[0] = chunk.getInt4(0) / 100000.0F; chromaticity[1] = chunk.getInt4(4) / 100000.0F; chromaticity[2] = chunk.getInt4(8) / 100000.0F; chromaticity[3] = chunk.getInt4(12) / 100000.0F; chromaticity[4] = chunk.getInt4(16) / 100000.0F; chromaticity[5] = chunk.getInt4(20) / 100000.0F; chromaticity[6] = chunk.getInt4(24) / 100000.0F; chromaticity[7] = chunk.getInt4(28) / 100000.0F; if (encodeParam != null) { encodeParam.setChromaticity(chromaticity); } if (emitProperties) { properties.put("white_point_x", chromaticity[0]); properties.put("white_point_y", chromaticity[1]); properties.put("red_x", chromaticity[2]); properties.put("red_y", chromaticity[3]); properties.put("green_x", chromaticity[4]); properties.put("green_y", chromaticity[5]); properties.put("blue_x", chromaticity[6]); properties.put("blue_y", chromaticity[7]); } } private void parse_gAMA_chunk(PNGChunk chunk) { // If an sRGB chunk exists, ignore gAMA chunks if (sRGBRenderingIntent != -1) { return; } fileGamma = chunk.getInt4(0) / 100000.0F; float exp = performGammaCorrection ? displayExponent / userExponent : 1.0F; if (encodeParam != null) { encodeParam.setGamma(fileGamma * exp); } if (emitProperties) { properties.put("gamma", fileGamma * exp); } } private void parse_hIST_chunk(PNGChunk chunk) { if (redPalette == null) { String msg = PropertyUtil.getString("PNGImageDecoder18"); throw new RuntimeException(msg); } int length = redPalette.length; int[] hist = new int[length]; for (int i = 0; i < length; i++) { hist[i] = chunk.getInt2(2 * i); } if (encodeParam != null) { encodeParam.setPaletteHistogram(hist); } } private void parse_iCCP_chunk(PNGChunk chunk) { // String name = ""; // todo simplify this // byte b; // int textIndex = 0; // while ((b = chunk.getByte(textIndex++)) != 0) { // name += (char)b; // } } private void parse_pHYs_chunk(PNGChunk chunk) { xPixelsPerUnit = chunk.getInt4(0); yPixelsPerUnit = chunk.getInt4(4); unitSpecifier = chunk.getInt1(8); if (encodeParam != null) { encodeParam.setPhysicalDimension(xPixelsPerUnit, yPixelsPerUnit, unitSpecifier); } if (emitProperties) { properties.put("x_pixels_per_unit", xPixelsPerUnit); properties.put("y_pixels_per_unit", yPixelsPerUnit); properties.put("pixel_aspect_ratio", (float) xPixelsPerUnit / yPixelsPerUnit); if (unitSpecifier == 1) { properties.put("pixel_units", "Meters"); } else if (unitSpecifier != 0) { // Error -- unit specifier must be 0 or 1 String msg = PropertyUtil.getString("PNGImageDecoder12"); throw new RuntimeException(msg); } } } private void parse_sBIT_chunk(PNGChunk chunk) { if (colorType == PNG_COLOR_PALETTE) { significantBits = new int[3]; } else { significantBits = new int[inputBands]; } for (int i = 0; i < significantBits.length; i++) { int bits = chunk.getByte(i); int depth = (colorType == PNG_COLOR_PALETTE) ? 8 : bitDepth; if (bits <= 0 || bits > depth) { // Error -- significant bits must be between 0 and // image bit depth. String msg = PropertyUtil.getString("PNGImageDecoder13"); throw new RuntimeException(msg); } significantBits[i] = bits; } if (encodeParam != null) { encodeParam.setSignificantBits(significantBits); } if (emitProperties) { properties.put("significant_bits", significantBits); } } private void parse_sRGB_chunk(PNGChunk chunk) { sRGBRenderingIntent = chunk.getByte(0); // The presence of an sRGB chunk implies particular // settings for gamma and chroma. fileGamma = 45455 / 100000.0F; chromaticity = new float[8]; chromaticity[0] = 31270 / 10000.0F; chromaticity[1] = 32900 / 10000.0F; chromaticity[2] = 64000 / 10000.0F; chromaticity[3] = 33000 / 10000.0F; chromaticity[4] = 30000 / 10000.0F; chromaticity[5] = 60000 / 10000.0F; chromaticity[6] = 15000 / 10000.0F; chromaticity[7] = 6000 / 10000.0F; if (performGammaCorrection) { // File gamma is 1/2.2 float gamma = fileGamma * (displayExponent / userExponent); if (encodeParam != null) { encodeParam.setGamma(gamma); encodeParam.setChromaticity(chromaticity); } if (emitProperties) { properties.put("gamma", gamma); properties.put("white_point_x", chromaticity[0]); properties.put("white_point_y", chromaticity[1]); properties.put("red_x", chromaticity[2]); properties.put("red_y", chromaticity[3]); properties.put("green_x", chromaticity[4]); properties.put("green_y", chromaticity[5]); properties.put("blue_x", chromaticity[6]); properties.put("blue_y", chromaticity[7]); } } } private void parse_tEXt_chunk(PNGChunk chunk) { byte b; StringBuffer key = new StringBuffer(); int textIndex = 0; while ((b = chunk.getByte(textIndex++)) != 0) { key.append((char)b); } StringBuilder value = new StringBuilder(); for (int i = textIndex; i < chunk.getLength(); i++) { value.append((char)chunk.getByte(i)); } textKeys.add(key.toString()); textStrings.add(value.toString()); } private void parse_tIME_chunk(PNGChunk chunk) { int year = chunk.getInt2(0); int month = chunk.getInt1(2) - 1; int day = chunk.getInt1(3); int hour = chunk.getInt1(4); int minute = chunk.getInt1(5); int second = chunk.getInt1(6); TimeZone gmt = TimeZone.getTimeZone("GMT"); GregorianCalendar cal = new GregorianCalendar(gmt); cal.set(year, month, day, hour, minute, second); Date date = cal.getTime(); if (encodeParam != null) { encodeParam.setModificationTime(date); } if (emitProperties) { properties.put("timestamp", date); } } private void parse_tRNS_chunk(PNGChunk chunk) { if (colorType == PNG_COLOR_PALETTE) { int entries = chunk.getLength(); if (entries > paletteEntries) { // Error -- mustn't have more alpha than RGB palette entries String msg = PropertyUtil.getString("PNGImageDecoder14"); throw new RuntimeException(msg); } // Load beginning of palette from the chunk alphaPalette = new byte[paletteEntries]; for (int i = 0; i < entries; i++) { alphaPalette[i] = chunk.getByte(i); } // Fill rest of palette with 255 for (int i = entries; i < paletteEntries; i++) { alphaPalette[i] = (byte)255; } if (!suppressAlpha) { if (expandPalette) { postProcess = POST_PALETTE_TO_RGBA; outputBands = 4; } else { outputHasAlphaPalette = true; } } } else if (colorType == PNG_COLOR_GRAY) { grayTransparentAlpha = chunk.getInt2(0); if (!suppressAlpha) { if (bitDepth < 8) { output8BitGray = true; maxOpacity = 255; postProcess = POST_GRAY_LUT_ADD_TRANS; } else { postProcess = POST_ADD_GRAY_TRANS; } if (expandGrayAlpha) { outputBands = 4; postProcess |= POST_EXP_MASK; } else { outputBands = 2; } if (encodeParam != null) { ((PNGEncodeParam.Gray)encodeParam).setTransparentGray(grayTransparentAlpha); } } } else if (colorType == PNG_COLOR_RGB) { redTransparentAlpha = chunk.getInt2(0); greenTransparentAlpha = chunk.getInt2(2); blueTransparentAlpha = chunk.getInt2(4); if (!suppressAlpha) { outputBands = 4; postProcess = POST_ADD_RGB_TRANS; if (encodeParam != null) { int[] rgbTrans = new int[3]; rgbTrans[0] = redTransparentAlpha; rgbTrans[1] = greenTransparentAlpha; rgbTrans[2] = blueTransparentAlpha; ((PNGEncodeParam.RGB)encodeParam).setTransparentRGB(rgbTrans); } } } else if (colorType == PNG_COLOR_GRAY_ALPHA || colorType == PNG_COLOR_RGB_ALPHA) { // Error -- GA or RGBA image can't have a tRNS chunk. String msg = PropertyUtil.getString("PNGImageDecoder15"); throw new RuntimeException(msg); } } private void parse_zTXt_chunk(PNGChunk chunk) { int textIndex = 0; StringBuffer key = new StringBuffer(); byte b; while ((b = chunk.getByte(textIndex++)) != 0) { key.append((char)b); } // skip method textIndex++; StringBuffer value = new StringBuffer(); try { int length = chunk.getLength() - textIndex; byte[] data = chunk.getData(); InputStream cis = new ByteArrayInputStream(data, textIndex, length); InputStream iis = new InflaterInputStream(cis); int c; while ((c = iis.read()) != -1) { value.append((char)c); } ztextKeys.add(key.toString()); ztextStrings.add(value.toString()); } catch (Exception e) { e.printStackTrace(); } } private WritableRaster createRaster(int width, int height, int bands, int scanlineStride, int bitDepth) { DataBuffer dataBuffer; WritableRaster ras = null; Point origin = new Point(0, 0); if ((bitDepth < 8) && (bands == 1)) { dataBuffer = new DataBufferByte(height * scanlineStride); ras = Raster.createPackedRaster(dataBuffer, width, height, bitDepth, origin); } else if (bitDepth <= 8) { dataBuffer = new DataBufferByte(height * scanlineStride); ras = Raster.createInterleavedRaster(dataBuffer, width, height, scanlineStride, bands, bandOffsets[bands], origin); } else { dataBuffer = new DataBufferUShort(height * scanlineStride); ras = Raster.createInterleavedRaster(dataBuffer, width, height, scanlineStride, bands, bandOffsets[bands], origin); } return ras; } // Data filtering methods 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; int priorPixel; int 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 void decodePaethFilter(byte[] curr, byte[] prev, int count, int bpp) { int raw; int priorPixel; int priorRow; int 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 + PNGEncodeParam.paethPredictor(priorPixel, priorRow, priorRowPixel)); } } private void processPixels(int process, Raster src, WritableRaster dst, int xOffset, int step, int y, int width) { int srcX; int dstX; // Create an array suitable for holding one pixel int[] ps = src.getPixel(0, 0, (int[])null); int[] pd = dst.getPixel(0, 0, (int[])null); dstX = xOffset; switch (process) { case POST_NONE: for (srcX = 0; srcX < width; srcX++) { src.getPixel(srcX, 0, ps); dst.setPixel(dstX, y, ps); dstX += step; } break; case POST_GAMMA: for (srcX = 0; srcX < width; srcX++) { src.getPixel(srcX, 0, ps); for (int i = 0; i < inputBands; i++) { int x = ps[i]; ps[i] = gammaLut[x]; } dst.setPixel(dstX, y, ps); dstX += step; } break; case POST_GRAY_LUT: for (srcX = 0; srcX < width; srcX++) { src.getPixel(srcX, 0, ps); pd[0] = grayLut[ps[0]]; dst.setPixel(dstX, y, pd); dstX += step; } break; case POST_GRAY_LUT_ADD_TRANS: for (srcX = 0; srcX < width; srcX++) { src.getPixel(srcX, 0, ps); int val = ps[0]; pd[0] = grayLut[val]; if (val == grayTransparentAlpha) { pd[1] = 0; } else { pd[1] = maxOpacity; } dst.setPixel(dstX, y, pd); dstX += step; } break; case POST_PALETTE_TO_RGB: for (srcX = 0; srcX < width; srcX++) { src.getPixel(srcX, 0, ps); int val = ps[0]; pd[0] = redPalette[val]; pd[1] = greenPalette[val]; pd[2] = bluePalette[val]; dst.setPixel(dstX, y, pd); dstX += step; } break; case POST_PALETTE_TO_RGBA: for (srcX = 0; srcX < width; srcX++) { src.getPixel(srcX, 0, ps); int val = ps[0]; pd[0] = redPalette[val]; pd[1] = greenPalette[val]; pd[2] = bluePalette[val]; pd[3] = alphaPalette[val]; dst.setPixel(dstX, y, pd); dstX += step; } break; case POST_ADD_GRAY_TRANS: for (srcX = 0; srcX < width; srcX++) { src.getPixel(srcX, 0, ps); int val = ps[0]; if (performGammaCorrection) { val = gammaLut[val]; } pd[0] = val; if (val == grayTransparentAlpha) { pd[1] = 0; } else { pd[1] = maxOpacity; } dst.setPixel(dstX, y, pd); dstX += step; } break; case POST_ADD_RGB_TRANS: for (srcX = 0; srcX < width; srcX++) { src.getPixel(srcX, 0, ps); int r = ps[0]; int g = ps[1]; int b = ps[2]; if (performGammaCorrection) { pd[0] = gammaLut[r]; pd[1] = gammaLut[g]; pd[2] = gammaLut[b]; } else { pd[0] = r; pd[1] = g; pd[2] = b; } if ((r == redTransparentAlpha) && (g == greenTransparentAlpha) && (b == blueTransparentAlpha)) { pd[3] = 0; } else { pd[3] = maxOpacity; } dst.setPixel(dstX, y, pd); dstX += step; } break; case POST_REMOVE_GRAY_TRANS: for (srcX = 0; srcX < width; srcX++) { src.getPixel(srcX, 0, ps); int g = ps[0]; if (performGammaCorrection) { pd[0] = gammaLut[g]; } else { pd[0] = g; } dst.setPixel(dstX, y, pd); dstX += step; } break; case POST_REMOVE_RGB_TRANS: for (srcX = 0; srcX < width; srcX++) { src.getPixel(srcX, 0, ps); int r = ps[0]; int g = ps[1]; int b = ps[2]; if (performGammaCorrection) { pd[0] = gammaLut[r]; pd[1] = gammaLut[g]; pd[2] = gammaLut[b]; } else { pd[0] = r; pd[1] = g; pd[2] = b; } dst.setPixel(dstX, y, pd); dstX += step; } break; case POST_GAMMA_EXP: for (srcX = 0; srcX < width; srcX++) { src.getPixel(srcX, 0, ps); int val = ps[0]; int alpha = ps[1]; int gamma = gammaLut[val]; pd[0] = gamma; pd[1] = gamma; pd[2] = gamma; pd[3] = alpha; dst.setPixel(dstX, y, pd); dstX += step; } break; case POST_GRAY_ALPHA_EXP: for (srcX = 0; srcX < width; srcX++) { src.getPixel(srcX, 0, ps); int val = ps[0]; int alpha = ps[1]; pd[0] = val; pd[1] = val; pd[2] = val; pd[3] = alpha; dst.setPixel(dstX, y, pd); dstX += step; } break; case POST_ADD_GRAY_TRANS_EXP: for (srcX = 0; srcX < width; srcX++) { src.getPixel(srcX, 0, ps); int val = ps[0]; if (performGammaCorrection) { val = gammaLut[val]; } pd[0] = val; pd[1] = val; pd[2] = val; if (val == grayTransparentAlpha) { pd[3] = 0; } else { pd[3] = maxOpacity; } dst.setPixel(dstX, y, pd); dstX += step; } break; case POST_GRAY_LUT_ADD_TRANS_EXP: for (srcX = 0; srcX < width; srcX++) { src.getPixel(srcX, 0, ps); int val = ps[0]; int val2 = grayLut[val]; pd[0] = val2; pd[1] = val2; pd[2] = val2; if (val == grayTransparentAlpha) { pd[3] = 0; } else { pd[3] = maxOpacity; } dst.setPixel(dstX, y, pd); dstX += step; } break; } }
Reads in an image of a given size and returns it as a WritableRaster.
/** * Reads in an image of a given size and returns it as a * WritableRaster. */
private void decodePass(WritableRaster imRas, 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; int eltsPerRow = (bitDepth == 16) ? bytesPerRow / 2 : bytesPerRow; byte[] curr = new byte[bytesPerRow]; byte[] prior = new byte[bytesPerRow]; // Create a 1-row tall Raster to hold the data WritableRaster passRow = createRaster(passWidth, 1, inputBands, eltsPerRow, bitDepth); DataBuffer dataBuffer = passRow.getDataBuffer(); int type = dataBuffer.getDataType(); byte[] byteData = null; short[] shortData = null; if (type == DataBuffer.TYPE_BYTE) { byteData = ((DataBufferByte)dataBuffer).getData(); } else { shortData = ((DataBufferUShort)dataBuffer).getData(); } // Decode the (sub)image row-by-row int srcY; int 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) { e.printStackTrace(); } 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 String msg = PropertyUtil.getString("PNGImageDecoder16"); throw new RuntimeException(msg); } // Copy data into passRow byte by byte if (bitDepth < 16) { System.arraycopy(curr, 0, byteData, 0, bytesPerRow); } else { int idx = 0; for (int j = 0; j < eltsPerRow; j++) { shortData[j] = (short)((curr[idx] << 8) | (curr[idx + 1] & 0xff)); idx += 2; } } processPixels(postProcess, passRow, imRas, xOffset, xStep, dstY, passWidth); // Swap curr and prior byte[] tmp = prior; prior = curr; curr = tmp; } } private void decodeImage(boolean useInterlacing) { if (!useInterlacing) { decodePass(theTile, 0, 0, 1, 1, width, height); } else { decodePass(theTile, 0, 0, 8, 8, (width + 7) / 8, (height + 7) / 8); decodePass(theTile, 4, 0, 8, 8, (width + 3) / 8, (height + 7) / 8); decodePass(theTile, 0, 4, 4, 8, (width + 3) / 4, (height + 3) / 8); decodePass(theTile, 2, 0, 4, 4, (width + 1) / 4, (height + 3) / 4); decodePass(theTile, 0, 2, 2, 4, (width + 1) / 2, (height + 1) / 4); decodePass(theTile, 1, 0, 2, 2, width / 2, (height + 1) / 2); decodePass(theTile, 0, 1, 1, 2, width, height / 2); } } // RenderedImage stuff public Raster getTile(int tileX, int tileY) { if (tileX != 0 || tileY != 0) { // Error -- bad tile requested String msg = PropertyUtil.getString("PNGImageDecoder17"); throw new IllegalArgumentException(msg); } return theTile; } }