You are viewing a plain text version of this content. The canonical link for it is here.
Posted to sanselan-commits@incubator.apache.org by cm...@apache.org on 2007/11/29 02:27:15 UTC

svn commit: r599250 [7/15] - in /incubator/sanselan/trunk/src/main/java/org: apache/sanselan/ apache/sanselan/color/ apache/sanselan/common/ apache/sanselan/common/byteSources/ apache/sanselan/common/mylzw/ apache/sanselan/formats/bmp/ apache/sanselan/...

Added: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/PngImageParser.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/PngImageParser.java?rev=599250&view=auto
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/PngImageParser.java (added)
+++ incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/PngImageParser.java Wed Nov 28 18:27:05 2007
@@ -0,0 +1,941 @@
+/*
+ * 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.
+ */
+package org.apache.sanselan.formats.png;
+
+import java.awt.Dimension;
+import java.awt.color.ColorSpace;
+import java.awt.color.ICC_ColorSpace;
+import java.awt.color.ICC_Profile;
+import java.awt.image.BufferedImage;
+import java.awt.image.ColorModel;
+import java.awt.image.DataBuffer;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.util.Map;
+import java.util.Vector;
+import java.util.zip.InflaterInputStream;
+
+import org.apache.sanselan.ColorTools;
+import org.apache.sanselan.ImageFormat;
+import org.apache.sanselan.ImageInfo;
+import org.apache.sanselan.ImageParser;
+import org.apache.sanselan.ImageReadException;
+import org.apache.sanselan.ImageWriteException;
+import org.apache.sanselan.common.IImageMetadata;
+import org.apache.sanselan.common.ImageMetadata;
+import org.apache.sanselan.common.byteSources.ByteSource;
+import org.apache.sanselan.formats.png.chunks.PNGChunk;
+import org.apache.sanselan.formats.png.chunks.PNGChunkIDAT;
+import org.apache.sanselan.formats.png.chunks.PNGChunkIHDR;
+import org.apache.sanselan.formats.png.chunks.PNGChunkPLTE;
+import org.apache.sanselan.formats.png.chunks.PNGChunkgAMA;
+import org.apache.sanselan.formats.png.chunks.PNGChunkiCCP;
+import org.apache.sanselan.formats.png.chunks.PNGChunkpHYs;
+import org.apache.sanselan.formats.png.chunks.PNGChunktEXt;
+import org.apache.sanselan.formats.png.chunks.PNGChunkzTXt;
+import org.apache.sanselan.formats.png.chunks.PNGTextChunk;
+import org.apache.sanselan.formats.transparencyfilters.TransparencyFilter;
+import org.apache.sanselan.formats.transparencyfilters.TransparencyFilterGrayscale;
+import org.apache.sanselan.formats.transparencyfilters.TransparencyFilterIndexedColor;
+import org.apache.sanselan.formats.transparencyfilters.TransparencyFilterTrueColor;
+import org.apache.sanselan.icc.IccProfileParser;
+import org.apache.sanselan.util.Debug;
+import org.apache.sanselan.util.IOUtils;
+
+public class PngImageParser extends ImageParser implements PngConstants
+{
+
+	public PngImageParser()
+	{
+		//		setDebug(true);
+	}
+
+	public String getName()
+	{
+		return "Png-Custom";
+	}
+
+	public String getDefaultExtension()
+	{
+		return DEFAULT_EXTENSION;
+	}
+
+	private static final String DEFAULT_EXTENSION = ".png";
+
+	private static final String ACCEPTED_EXTENSIONS[] = {
+		DEFAULT_EXTENSION,
+	};
+
+	protected String[] getAcceptedExtensions()
+	{
+		return ACCEPTED_EXTENSIONS;
+	}
+
+	protected ImageFormat[] getAcceptedTypes()
+	{
+		return new ImageFormat[]{
+			ImageFormat.IMAGE_FORMAT_PNG, //
+		};
+	}
+
+	//	private final static int tRNS = CharsToQuad('t', 'R', 'N', 's');
+
+	private boolean keepChunk(int ChunkType, int ChunkTypes[])
+	{
+		//		System.out.println("keepChunk: ");
+		if (ChunkTypes == null)
+			return true;
+
+		for (int i = 0; i < ChunkTypes.length; i++)
+		{
+			if (ChunkTypes[i] == ChunkType)
+				return true;
+		}
+		return false;
+	}
+
+	private Vector readChunks(InputStream is, int ChunkTypes[],
+			boolean return_after_first) throws ImageReadException, IOException
+	{
+		Vector result = new Vector();
+
+		while (true)
+		{
+			if (debug)
+				System.out.println("");
+
+			int length = read4Bytes("Length", is, "Not a Valid PNG File");
+			int chunkType = read4Bytes("ChunkType", is, "Not a Valid PNG File");
+
+			if (debug)
+			{
+				printCharQuad("ChunkType", chunkType);
+				debugNumber("Length", length, 4);
+			}
+			boolean keep = keepChunk(chunkType, ChunkTypes);
+
+			byte bytes[] = null;
+			if (keep)
+			{
+				bytes = readByteArray("Chunk Data", length, is,
+						"Not a Valid PNG File: Couldn't read Chunk Data.");
+			}
+			else
+				skipBytes(is, length, "Not a Valid PNG File");
+
+			if (debug)
+				if (bytes != null)
+					debugNumber("bytes", bytes.length, 4);
+
+			int CRC = read4Bytes("CRC", is, "Not a Valid PNG File");
+
+			if (keep)
+			{
+				if (chunkType == iCCP)
+					result.add(new PNGChunkiCCP(length, chunkType, CRC, bytes));
+				else if (chunkType == tEXt)
+					result.add(new PNGChunktEXt(length, chunkType, CRC, bytes));
+				else if (chunkType == zTXt)
+					result.add(new PNGChunkzTXt(length, chunkType, CRC, bytes));
+				else if (chunkType == IHDR)
+					result.add(new PNGChunkIHDR(length, chunkType, CRC, bytes));
+				else if (chunkType == PLTE)
+					result.add(new PNGChunkPLTE(length, chunkType, CRC, bytes));
+				else if (chunkType == pHYs)
+					result.add(new PNGChunkpHYs(length, chunkType, CRC, bytes));
+				else if (chunkType == IDAT)
+					result.add(new PNGChunkIDAT(length, chunkType, CRC, bytes));
+				else if (chunkType == gAMA)
+					result.add(new PNGChunkgAMA(length, chunkType, CRC, bytes));
+				else
+					result.add(new PNGChunk(length, chunkType, CRC, bytes));
+
+				if (return_after_first)
+					return result;
+			}
+
+			if (chunkType == IEND)
+				break;
+
+		}
+
+		return result;
+
+	}
+
+	private void readSignature(InputStream is) throws ImageReadException,
+			IOException
+	{
+		readAndVerifyBytes(is, PNG_Signature,
+				"Not a Valid PNG Segment: Incorrect Signature");
+
+	}
+
+	private Vector readChunks(ByteSource byteSource, int ChunkTypes[],
+			boolean return_after_first) throws ImageReadException, IOException
+	{
+		InputStream is = null;
+
+		try
+		{
+			is = byteSource.getInputStream();
+
+			Vector chunks = null;
+
+			readSignature(is);
+			chunks = readChunks(is, ChunkTypes, return_after_first);
+			return chunks;
+		}
+		finally
+		{
+			try
+			{
+				is.close();
+			}
+			catch (Exception e)
+			{
+				Debug.debug(e);
+			}
+		}
+	}
+
+	public byte[] getICCProfileBytes(ByteSource byteSource)
+			throws ImageReadException, IOException
+	{
+		Vector chunks = readChunks(byteSource, new int[]{
+			iCCP,
+		}, true);
+
+		if ((chunks == null) || (chunks.size() < 1))
+		{
+			//			throw new ImageReadException("Png: No chunks");
+			return null;
+		}
+
+		if (chunks.size() > 1)
+			throw new ImageReadException(
+					"PNG contains more than one ICC Profile ");
+
+		PNGChunkiCCP pngChunkiCCP = (PNGChunkiCCP) chunks.get(0);
+		byte bytes[] = pngChunkiCCP.UncompressedProfile;
+
+		return (bytes);
+	}
+
+	public Dimension getImageSize(ByteSource byteSource)
+			throws ImageReadException, IOException
+	{
+		Vector chunks = readChunks(byteSource, new int[]{
+			IHDR,
+		}, true);
+
+		if ((chunks == null) || (chunks.size() < 1))
+			throw new ImageReadException("Png: No chunks");
+
+		if (chunks.size() > 1)
+			throw new ImageReadException("PNG contains more than one Header");
+
+		PNGChunkIHDR pngChunkIHDR = (PNGChunkIHDR) chunks.get(0);
+
+		return new Dimension(pngChunkIHDR.width, pngChunkIHDR.height);
+	}
+
+	public byte[] embedICCProfile(byte image[], byte profile[])
+	{
+		return null;
+	}
+
+	public boolean embedICCProfile(File src, File dst, byte profile[])
+	{
+		return false;
+	}
+
+	public IImageMetadata getMetadata(ByteSource byteSource, Map params)
+			throws ImageReadException, IOException
+	{
+		Vector chunks = readChunks(byteSource, new int[]{
+				tEXt, zTXt,
+		}, true);
+
+		if ((chunks == null) || (chunks.size() < 1))
+			return null;
+
+		ImageMetadata result = new ImageMetadata();
+
+		for (int i = 0; i < chunks.size(); i++)
+		{
+			PNGTextChunk chunk = (PNGTextChunk) chunks.get(i);
+
+			result.add(chunk.getKeyword(), chunk.getText());
+		}
+
+		return result;
+	}
+
+	private boolean isGrayscale(int colorType) throws ImageReadException
+	{
+		//		Color type is a single-byte integer that describes the interpretation of the 
+		//		image data. Color type codes represent sums of the following values: 
+		//			1 (palette used), 2 (color used), and 4 (alpha channel used). 
+		//			Valid values are 0, 2, 3, 4, and 6.
+		//
+		//		Bit depth restrictions for each color type are imposed to simplify implementations 
+		//		and to prohibit combinations that do not compress well. Decoders must support all 
+		//		valid combinations of bit depth and color type. The allowed combinations are:
+		//
+		//		   Color    Allowed    Interpretation
+		//		   Type    Bit Depths
+		//		   
+		//		   0       1,2,4,8,16  Each pixel is a grayscale sample.
+		//		   
+		//		   2       8,16        Each pixel is an R,G,B triple.
+		//		   
+		//		   3       1,2,4,8     Each pixel is a palette index;
+		//		                       a PLTE chunk must appear.
+		//		   
+		//		   4       8,16        Each pixel is a grayscale sample,
+		//		                       followed by an alpha sample.
+		//		   
+		//		   6       8,16        Each pixel is an R,G,B triple,
+		//		                       followed by an alpha sample.
+		switch (colorType)
+		{
+			case COLOR_TYPE_GREYSCALE :
+				return true;
+			case COLOR_TYPE_TRUE_COLOR :
+				return false;
+			case COLOR_TYPE_INDEXED_COLOR :
+				return false;
+			case COLOR_TYPE_GREYSCALE_WITH_ALPHA :
+				return true;
+			case COLOR_TYPE_TRUE_COLOR_WITH_ALPHA :
+				return false;
+		}
+
+		//		return -1;
+		throw new ImageReadException("PNG: unknown color type: " + colorType);
+	}
+
+	private int samplesPerPixel(int colorType) throws ImageReadException
+	{
+		//		Color type is a single-byte integer that describes the interpretation of the 
+		//		image data. Color type codes represent sums of the following values: 
+		//			1 (palette used), 2 (color used), and 4 (alpha channel used). 
+		//			Valid values are 0, 2, 3, 4, and 6.
+		//
+		//		Bit depth restrictions for each color type are imposed to simplify implementations 
+		//		and to prohibit combinations that do not compress well. Decoders must support all 
+		//		valid combinations of bit depth and color type. The allowed combinations are:
+		//
+		//		   Color    Allowed    Interpretation
+		//		   Type    Bit Depths
+		//		   
+		//		   0       1,2,4,8,16  Each pixel is a grayscale sample.
+		//		   
+		//		   2       8,16        Each pixel is an R,G,B triple.
+		//		   
+		//		   3       1,2,4,8     Each pixel is a palette index;
+		//		                       a PLTE chunk must appear.
+		//		   
+		//		   4       8,16        Each pixel is a grayscale sample,
+		//		                       followed by an alpha sample.
+		//		   
+		//		   6       8,16        Each pixel is an R,G,B triple,
+		//		                       followed by an alpha sample.
+		switch (colorType)
+		{
+			case COLOR_TYPE_GREYSCALE :
+				return 1;
+			case COLOR_TYPE_TRUE_COLOR :
+				return 3;
+			case COLOR_TYPE_INDEXED_COLOR :
+				return 1; // is this accurate ?  how may bits per index?
+			case COLOR_TYPE_GREYSCALE_WITH_ALPHA :
+				return 2;
+			case COLOR_TYPE_TRUE_COLOR_WITH_ALPHA :
+				return 4;
+		}
+
+		//		return -1;
+		throw new ImageReadException("PNG: unknown color type: " + colorType);
+	}
+
+	private Vector filterChunks(Vector v, int type)
+	{
+		Vector result = new Vector();
+
+		for (int i = 0; i < v.size(); i++)
+		{
+			PNGChunk chunk = (PNGChunk) v.get(i);
+			if (chunk.chunkType == type)
+				result.add(chunk);
+		}
+
+		return result;
+	}
+
+	private boolean hasAlphaChannel(int ColorType) throws ImageReadException,
+			IOException
+	{
+		switch (ColorType)
+		{
+			case COLOR_TYPE_GREYSCALE : //       1,2,4,8,16  Each pixel is a grayscale sample.
+			case COLOR_TYPE_TRUE_COLOR : //     8,16        Each pixel is an R,G,B triple.
+			case COLOR_TYPE_INDEXED_COLOR : //     1,2,4,8     Each pixel is a palette index;
+				return false;
+			case COLOR_TYPE_GREYSCALE_WITH_ALPHA : //     8,16        Each pixel is a grayscale sample,
+				//					                       followed by an alpha sample.
+			case COLOR_TYPE_TRUE_COLOR_WITH_ALPHA : //    8,16        Each pixel is an R,G,B triple,
+				//					                       followed by an alpha sample.
+				return true;
+			default :
+				throw new ImageReadException("PNG: unknown color type: "
+						+ ColorType);
+		}
+	}
+
+	private String getColorTypeDescription(int ColorType)
+	{
+		switch (ColorType)
+		{
+			case COLOR_TYPE_GREYSCALE : //       1,2,4,8,16  Each pixel is a grayscale sample.
+				return "grayscale";
+			case COLOR_TYPE_TRUE_COLOR : //     8,16        Each pixel is an R,G,B triple.
+				return "rgb";
+			case COLOR_TYPE_INDEXED_COLOR : //     1,2,4,8     Each pixel is a palette index;
+				return "indexed rgb";
+			case COLOR_TYPE_GREYSCALE_WITH_ALPHA : //     8,16        Each pixel is a grayscale sample,
+				//					                       followed by an alpha sample.
+				return "grayscale w/ alpha";
+			case COLOR_TYPE_TRUE_COLOR_WITH_ALPHA : //    8,16        Each pixel is an R,G,B triple,
+				//					                       followed by an alpha sample.
+				return "RGB w/ alpha";
+			default :
+				return "Unknown Color Type";
+		}
+	}
+
+	// TODO: I have been too casual about making inner classes subclass of BinaryFileParser
+	// I may not have always preserved byte order correctly.  
+
+	private TransparencyFilter getTransparencyFilter(int ColorType,
+			PNGChunk pngChunktRNS) throws ImageReadException, IOException
+	{
+		//		this.printCharQuad("pngChunktRNS.ChunkType", pngChunktRNS.ChunkType);
+		//		this.debugNumber("pngChunktRNS.Length", pngChunktRNS.Length);
+
+		switch (ColorType)
+		{
+			case COLOR_TYPE_GREYSCALE : //       1,2,4,8,16  Each pixel is a grayscale sample.
+				return new TransparencyFilterGrayscale(pngChunktRNS.bytes);
+			case COLOR_TYPE_TRUE_COLOR : //     8,16        Each pixel is an R,G,B triple.
+				return new TransparencyFilterTrueColor(pngChunktRNS.bytes);
+			case COLOR_TYPE_INDEXED_COLOR : //     1,2,4,8     Each pixel is a palette index;
+				return new TransparencyFilterIndexedColor(pngChunktRNS.bytes);
+			case COLOR_TYPE_GREYSCALE_WITH_ALPHA : //     8,16        Each pixel is a grayscale sample,
+			case COLOR_TYPE_TRUE_COLOR_WITH_ALPHA : //    8,16        Each pixel is an R,G,B triple,
+			default :
+				throw new ImageReadException(
+						"Simple Transparency not compatible with ColorType: "
+								+ ColorType);
+		}
+	}
+
+	public ImageInfo getImageInfo(ByteSource byteSource)
+			throws ImageReadException, IOException
+	{
+		Vector chunks = readChunks(byteSource, new int[]{
+				IHDR, pHYs, tEXt, zTXt, tRNS, PLTE,
+		}, false);
+
+		//		if(chunks!=null)
+		//		System.out.println("chunks: " + chunks.size());
+
+		if ((chunks == null) || (chunks.size() < 1))
+			throw new ImageReadException("PNG: no chunks");
+
+		Vector IHDRs = filterChunks(chunks, IHDR);
+		if (IHDRs.size() != 1)
+			throw new ImageReadException("PNG contains more than one Header");
+
+		PNGChunkIHDR pngChunkIHDR = (PNGChunkIHDR) IHDRs.get(0);
+		PNGChunk pngChunktRNS = null;
+
+		boolean isTransparent = false;
+
+		Vector tRNSs = filterChunks(chunks, tRNS);
+		if (tRNSs.size() > 0)
+		{
+			isTransparent = true;
+			pngChunktRNS = (PNGChunk) IHDRs.get(0);
+		}
+		else
+			hasAlphaChannel(pngChunkIHDR.colorType);
+
+		PNGChunkpHYs pngChunkpHYs = null;
+
+		Vector pHYss = filterChunks(chunks, pHYs);
+		if (pHYss.size() > 1)
+			throw new ImageReadException("PNG contains more than one pHYs: "
+					+ pHYss.size());
+		else if (pHYss.size() == 1)
+			pngChunkpHYs = (PNGChunkpHYs) pHYss.get(0);
+
+		Vector tEXts = filterChunks(chunks, tEXt);
+		Vector zTXts = filterChunks(chunks, zTXt);
+
+		//			private class PNGChunkpHYs extends PNGChunk
+		//			{
+		//				public final int PixelsPerUnitXAxis;
+		//				public final int PixelsPerUnitYAxis;
+		//				public final int UnitSpecifier;
+
+		{
+			//			private class PNGChunkIHDR extends PNGChunk
+			//			{
+			//				public final int Width;
+			//				public final int Height;
+			//				public final int BitDepth;
+			//				public final int ColorType;
+			//				public final int CompressionMethod;
+			//				public final int FilterMethod;
+			//				public final int InterlaceMethod;
+
+			Vector Comments = new Vector();
+
+			for (int i = 0; i < tEXts.size(); i++)
+			{
+				PNGChunktEXt pngChunktEXt = (PNGChunktEXt) tEXts.get(i);
+				Comments.add(pngChunktEXt.Keyword + ": " + pngChunktEXt.Text);
+			}
+			for (int i = 0; i < zTXts.size(); i++)
+			{
+				PNGChunkzTXt pngChunkzTXt = (PNGChunkzTXt) zTXts.get(i);
+				Comments.add(pngChunkzTXt.Keyword + ": " + pngChunkzTXt.Text);
+			}
+
+			int BitsPerPixel = pngChunkIHDR.bitDepth
+					* samplesPerPixel(pngChunkIHDR.colorType);
+			ImageFormat Format = ImageFormat.IMAGE_FORMAT_PNG;
+			String FormatName = "PNG Portable Network Graphics";
+			int Height = pngChunkIHDR.height;
+			String MimeType = "image/png";
+			int NumberOfImages = 1;
+			int Width = pngChunkIHDR.width;
+			boolean isProgressive = (pngChunkIHDR.interlaceMethod != 0);
+
+			int PhysicalHeightDpi = -1;
+			float PhysicalHeightInch = -1;
+			int PhysicalWidthDpi = -1;
+			float PhysicalWidthInch = -1;
+
+			//			if (pngChunkpHYs != null)
+			//			{
+			//				System.out.println("\t" + "pngChunkpHYs.UnitSpecifier: " + pngChunkpHYs.UnitSpecifier );
+			//				System.out.println("\t" + "pngChunkpHYs.PixelsPerUnitYAxis: " + pngChunkpHYs.PixelsPerUnitYAxis );
+			//				System.out.println("\t" + "pngChunkpHYs.PixelsPerUnitXAxis: " + pngChunkpHYs.PixelsPerUnitXAxis );
+			//			}
+			if ((pngChunkpHYs != null) && (pngChunkpHYs.UnitSpecifier == 1)) // meters
+			{
+				double meters_per_inch = 0.0254;
+
+				PhysicalWidthDpi = (int) Math
+						.round((double) pngChunkpHYs.PixelsPerUnitXAxis
+								* meters_per_inch);
+				PhysicalWidthInch = (float) ((double) Width
+						* (double) pngChunkpHYs.PixelsPerUnitXAxis * meters_per_inch);
+				PhysicalHeightDpi = (int) Math
+						.round((double) pngChunkpHYs.PixelsPerUnitYAxis
+								* meters_per_inch);
+				PhysicalHeightInch = (float) ((double) Height
+						* (double) pngChunkpHYs.PixelsPerUnitYAxis * meters_per_inch);
+			}
+
+			String FormatDetails = "Png";
+
+			boolean usesPalette = false;
+
+			Vector PLTEs = filterChunks(chunks, PLTE);
+			if (PLTEs.size() > 1)
+				usesPalette = true;
+
+			int ColorType;
+			switch (pngChunkIHDR.colorType)
+			{
+				case COLOR_TYPE_GREYSCALE : //       1,2,4,8,16  Each pixel is a grayscale sample.
+				case COLOR_TYPE_GREYSCALE_WITH_ALPHA : //     8,16        Each pixel is a grayscale sample,
+					//					                       followed by an alpha sample.
+					ColorType = ImageInfo.COLOR_TYPE_GRAYSCALE;
+					break;
+				case COLOR_TYPE_TRUE_COLOR : //     8,16        Each pixel is an R,G,B triple.
+				case COLOR_TYPE_INDEXED_COLOR : //     1,2,4,8     Each pixel is a palette index;
+				case COLOR_TYPE_TRUE_COLOR_WITH_ALPHA : //    8,16        Each pixel is an R,G,B triple,
+					//					                       followed by an alpha sample.
+					ColorType = ImageInfo.COLOR_TYPE_RGB;
+					break;
+				default :
+					throw new ImageReadException("Png: Unknown ColorType: "
+							+ pngChunkIHDR.colorType);
+			}
+
+			String compressionAlgorithm = ImageInfo.COMPRESSION_ALGORITHM_PNG_FILTER;
+
+			ImageInfo result = new ImageInfo(FormatDetails, BitsPerPixel,
+					Comments, Format, FormatName, Height, MimeType,
+					NumberOfImages, PhysicalHeightDpi, PhysicalHeightInch,
+					PhysicalWidthDpi, PhysicalWidthInch, Width, isProgressive,
+					isTransparent, usesPalette, ColorType, compressionAlgorithm);
+
+			return result;
+		}
+	}
+
+	public BufferedImage getBufferedImage(ByteSource byteSource, Map params)
+			throws ImageReadException, IOException
+	{
+		Vector chunks = readChunks(byteSource, new int[]{
+				IHDR, PLTE, IDAT, tRNS, iCCP, gAMA, sRGB,
+		}, false);
+
+		if ((chunks == null) || (chunks.size() < 1))
+			throw new ImageReadException("PNG: no chunks");
+
+		Vector IHDRs = filterChunks(chunks, IHDR);
+		if (IHDRs.size() != 1)
+			throw new ImageReadException("PNG contains more than one Header");
+
+		PNGChunkIHDR pngChunkIHDR = (PNGChunkIHDR) IHDRs.get(0);
+
+		Vector PLTEs = filterChunks(chunks, PLTE);
+		if (PLTEs.size() > 1)
+			throw new ImageReadException("PNG contains more than one Palette");
+
+		PNGChunkPLTE pngChunkPLTE = null;
+		if (PLTEs.size() == 1)
+			pngChunkPLTE = (PNGChunkPLTE) PLTEs.get(0);
+
+		// -----
+
+		Vector IDATs = filterChunks(chunks, IDAT);
+		if (IDATs.size() < 1)
+			throw new ImageReadException("PNG missing image data");
+
+		ByteArrayOutputStream baos = new ByteArrayOutputStream();
+		for (int i = 0; i < IDATs.size(); i++)
+		{
+			PNGChunkIDAT pngChunkIDAT = (PNGChunkIDAT) IDATs.get(i);
+			byte bytes[] = pngChunkIDAT.bytes;
+			//						System.out.println(i + ": bytes: " + bytes.length);
+			baos.write(bytes);
+		}
+
+		byte compressed[] = baos.toByteArray();
+
+		baos = null;
+
+		TransparencyFilter transparencyFilter = null;
+
+		Vector tRNSs = filterChunks(chunks, tRNS);
+		if (tRNSs.size() > 0)
+		{
+			PNGChunk pngChunktRNS = (PNGChunk) tRNSs.get(0);
+			transparencyFilter = getTransparencyFilter(pngChunkIHDR.colorType,
+					pngChunktRNS);
+		}
+
+		ICC_Profile icc_profile = null;
+		GammaCorrection gammaCorrection = null;
+		{
+			Vector sRGBs = filterChunks(chunks, sRGB);
+			Vector gAMAs = filterChunks(chunks, gAMA);
+			Vector iCCPs = filterChunks(chunks, iCCP);
+			if (sRGBs.size() > 1)
+				throw new ImageReadException("PNG: unexpected sRGB chunk");
+			if (gAMAs.size() > 1)
+				throw new ImageReadException("PNG: unexpected gAMA chunk");
+			if (iCCPs.size() > 1)
+				throw new ImageReadException("PNG: unexpected iCCP chunk");
+
+			if (sRGBs.size() == 1)
+			{
+				// no color management neccesary.
+				if (debug)
+					System.out.println("sRGB, no color management neccesary.");
+			}
+			else if (iCCPs.size() == 1)
+			{
+				if (debug)
+					System.out.println("iCCP.");
+
+				PNGChunkiCCP pngChunkiCCP = (PNGChunkiCCP) iCCPs.get(0);
+				byte bytes[] = pngChunkiCCP.UncompressedProfile;
+
+				icc_profile = ICC_Profile.getInstance(bytes);
+			}
+			else if (gAMAs.size() == 1)
+			{
+				PNGChunkgAMA pngChunkgAMA = (PNGChunkgAMA) gAMAs.get(0);
+				double gamma = pngChunkgAMA.getGamma();
+				//				if (debug)
+				//				System.out.println("gamma: " + gamma);
+
+				// charles: what is the correct target value here?
+				//				double targetGamma = 2.2;
+				double targetGamma = 1.0;
+				double diff = Math.abs(targetGamma - gamma);
+				if (diff >= 0.5)
+					gammaCorrection = new GammaCorrection(gamma, targetGamma);
+
+				if (gammaCorrection != null)
+					if (pngChunkPLTE != null)
+						pngChunkPLTE.correct(gammaCorrection);
+
+			}
+		}
+
+		{
+			int width = pngChunkIHDR.width;
+			int height = pngChunkIHDR.height;
+			int colorType = pngChunkIHDR.colorType;
+			int bitDepth = pngChunkIHDR.bitDepth;
+
+			//			System.out.println("color_type: " + color_type);
+			//			System.out.println("BitDepth: " + BitDepth);
+
+//			int transfer_type;
+			//			int BytesPerSample;
+			int bitsPerSample = bitDepth;
+
+			if (pngChunkIHDR.filterMethod != 0)
+				throw new ImageReadException("PNG: unknown FilterMethod: "
+						+ pngChunkIHDR.filterMethod);
+
+//			transfer_type = DataBuffer.TYPE_BYTE;
+			//			switch (BitDepth)
+			//			{
+			//				case 1 :
+			//				case 2 :
+			//				case 4 :
+			//				case 8 :
+			//					transfer_type = DataBuffer.TYPE_BYTE;
+			//					//					BytesPerSample = 1;
+			//					break;
+			//				case 16 :
+			//					transfer_type = DataBuffer.TYPE_INT;
+			//					//					BytesPerSample = 2;
+			//					break;
+			//				default :
+			//					throw new ImageReadException("PNG: unknown bit depth: " + BitDepth);
+			//			}
+
+			ColorModel cm = null;
+			int samplesPerPixel = samplesPerPixel(pngChunkIHDR.colorType);
+			boolean isGrayscale = isGrayscale(pngChunkIHDR.colorType);
+
+			//			switch (colorType)
+			//			{
+			//				case 0 : //       1,2,4,8,16  Each pixel is a grayscale sample.
+			//				{
+			//					ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
+			//
+			//					//					BytesPerPixel = getBytesPerPixel( BitsPerSample,  SamplesPerPixel);
+			//					//					BytesPerPixel = BytesPerSample * SamplesPerPixel;
+			//					cm = new ComponentColorModel(cs, true, false,
+			//							ColorModel.TRANSLUCENT, transfer_type);
+			//					break;
+			//				}
+			//				case 2 : //     8,16        Each pixel is an R,G,B triple.
+			//				{
+			//					ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
+			//
+			//					//					BytesPerPixel = getBytesPerPixel( BitsPerSample,  SamplesPerPixel);
+			//					//					BytesPerPixel = BytesPerSample * SamplesPerPixel;
+			//					cm = new ComponentColorModel(cs, true, false,
+			//							ColorModel.TRANSLUCENT, transfer_type);
+			//					//										cm = ColorModel.getRGBdefault();
+			//					break;
+			//				}
+			//					//					   
+			//				case 3 : //     1,2,4,8     Each pixel is a palette index;
+			//					//					                       a PLTE chunk must appear.
+			//				{
+			//					ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
+			//
+			//					//					BytesPerPixel = getBytesPerPixel( BitsPerSample,  SamplesPerPixel);
+			//					//					BytesPerPixel = BytesPerSample * SamplesPerPixel;
+			//					cm = new ComponentColorModel(cs, true, false,
+			//							ColorModel.TRANSLUCENT, transfer_type);
+			//					//										cm = ColorModel.getRGBdefault();
+			//
+			//					//					SamplesPerPixel = 1;
+			//					//					//					bits_per_pixel = 8;
+			//					//					//					BytesPerPixel = 1;
+			//					//					cm = ColorModel.getRGBdefault();
+			//					//					//					ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
+			//					//					//					cm = new ComponentColorModel(cs, false, false,
+			//					//					//							ColorModel.OPAQUE, transfer_type);
+			//					break;
+			//				}
+			//				case 4 : //     8,16        Each pixel is a grayscale sample,
+			//					//					                       followed by an alpha sample.
+			//				{
+			//					ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
+			//
+			//					//					BytesPerPixel = getBytesPerPixel( BitsPerSample,  SamplesPerPixel);
+			//					//					BytesPerPixel = BytesPerSample * SamplesPerPixel;
+			//					cm = new ComponentColorModel(cs, true, false,
+			//							ColorModel.TRANSLUCENT, transfer_type);
+			//					break;
+			//				}
+			//				case 6 : //    8,16        Each pixel is an R,G,B triple,
+			//				{
+			//					ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
+			//
+			//					//					BytesPerPixel = getBytesPerPixel( BitsPerSample,  SamplesPerPixel);
+			//					//					BytesPerPixel = BytesPerSample * SamplesPerPixel;
+			//					cm = new ComponentColorModel(cs, true, false,
+			//							ColorModel.TRANSLUCENT, transfer_type);
+			//					break;
+			//				}
+			//				default :
+			//					throw new ImageReadException("PNG: unknown color type: "
+			//							+ BitDepth);
+			//			}
+
+			//			cm = ColorModel.getRGBdefault();
+			//
+
+			int bitsPerPixel = bitsPerSample * samplesPerPixel;
+//			Debug.debug("bitsPerSample", bitsPerSample);
+//			Debug.debug("samplesPerPixel", samplesPerPixel);
+
+			boolean hasAlpha = colorType == COLOR_TYPE_GREYSCALE_WITH_ALPHA
+					|| colorType == COLOR_TYPE_TRUE_COLOR_WITH_ALPHA;
+
+			BufferedImage result;
+			if (isGrayscale)
+				result = getBufferedImageFactory(params)
+						.getGrayscaleBufferedImage(width, height, hasAlpha);
+			else
+				result = getBufferedImageFactory(params).getColorBufferedImage(
+						width, height, hasAlpha);
+
+			ByteArrayInputStream bais = new ByteArrayInputStream(compressed);
+			InflaterInputStream iis = new InflaterInputStream(bais);
+
+			byte bytes[] = IOUtils.getInputStreamBytes(iis);
+
+			bais = new ByteArrayInputStream(compressed);
+			iis = new InflaterInputStream(bais);
+
+			//			ZInputStream iis = new ZInputStream(bais);
+			//			ByteArrayInputStream iis = new ByteArrayInputStream(uncompressed);
+
+//			int bitsPerScanLine = bitsPerPixel * width;
+
+//			Debug.debug("bitsPerScanLine", bitsPerScanLine);
+//			Debug.debug("bitsPerPixel", bitsPerPixel);
+
+			ScanExpediter scanExpediter;
+
+			if (pngChunkIHDR.interlaceMethod == 0)
+				scanExpediter = new ScanExpediterSimple(width, height, iis,
+						result, colorType, bitDepth, bitsPerPixel,
+						pngChunkPLTE, gammaCorrection, transparencyFilter);
+			else if (pngChunkIHDR.interlaceMethod == 1)
+				scanExpediter = new ScanExpediterInterlaced(width, height, iis,
+						result, colorType, bitDepth, bitsPerPixel,
+						pngChunkPLTE, gammaCorrection, transparencyFilter);
+			else
+				throw new ImageReadException("Unknown InterlaceMethod: "
+						+ pngChunkIHDR.interlaceMethod);
+
+			scanExpediter.drive();
+
+			if (icc_profile != null)
+			{
+				Boolean is_srgb = new IccProfileParser().issRGB(icc_profile);
+				if (is_srgb == null || !is_srgb.booleanValue())
+				{
+					ICC_ColorSpace cs = new ICC_ColorSpace(icc_profile);
+
+					ColorModel srgbCM = ColorModel.getRGBdefault();
+					ColorSpace cs_sRGB = srgbCM.getColorSpace();
+
+					result = new ColorTools().convertBetweenColorSpaces(result,
+							cs, cs_sRGB);
+				}
+			}
+
+			return result;
+
+		}
+
+	}
+
+	public boolean dumpImageFile(PrintWriter pw, ByteSource byteSource)
+			throws ImageReadException, IOException
+	{
+		ImageInfo imageInfo = getImageInfo(byteSource);
+		if (imageInfo == null)
+			return false;
+
+		imageInfo.toString(pw, "");
+
+		{
+			Vector chunks = readChunks(byteSource, null, false);
+			{
+				Vector IHDRs = filterChunks(chunks, IHDR);
+				if (IHDRs.size() != 1)
+				{
+					if (debug)
+						System.out.println("PNG contains more than one Header");
+					return false;
+				}
+				PNGChunkIHDR pngChunkIHDR = (PNGChunkIHDR) IHDRs.get(0);
+				pw.println("Color: "
+						+ getColorTypeDescription(pngChunkIHDR.colorType));
+			}
+
+			pw.println("chunks: " + chunks.size());
+
+			if ((chunks == null) || (chunks.size() < 1))
+				return false;
+
+			for (int i = 0; i < chunks.size(); i++)
+			{
+				PNGChunk chunk = (PNGChunk) chunks.get(i);
+				printCharQuad(pw, "\t" + i + ": ", chunk.chunkType);
+			}
+		}
+
+		pw.println("");
+
+		return true;
+	}
+
+	public void writeImage(BufferedImage src, OutputStream os, Map params)
+			throws ImageWriteException, IOException
+	{
+		new PngWriter(params).writeImage(src, os, params);
+	}
+}

Propchange: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/PngImageParser.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/PngWriter.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/PngWriter.java?rev=599250&view=auto
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/PngWriter.java (added)
+++ incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/PngWriter.java Wed Nov 28 18:27:05 2007
@@ -0,0 +1,524 @@
+/*
+ * 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.
+ */
+package org.apache.sanselan.formats.png;
+
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.zip.DeflaterOutputStream;
+
+import org.apache.sanselan.ImageWriteException;
+import org.apache.sanselan.palette.MedianCutQuantizer;
+import org.apache.sanselan.palette.Palette;
+import org.apache.sanselan.palette.PaletteFactory;
+import org.apache.sanselan.util.Debug;
+
+public class PngWriter implements PngConstants
+{
+	private final boolean verbose;
+
+	public PngWriter(boolean verbose)
+	{
+		this.verbose = verbose;
+	}
+
+	public PngWriter(Map params)
+	{
+		this.verbose = getParamBoolean(params, PARAM_KEY_VERBOSE, false);
+	}
+
+	private boolean getParamBoolean(Map params, Object key,
+			boolean default_value)
+	{
+		boolean result = default_value;
+		{
+			Object o = params == null ? null : params.get(key);
+			if (o != null && o instanceof Boolean)
+				result = ((Boolean) o).booleanValue();
+		}
+		return result;
+	}
+
+	/*
+	 1. IHDR: image header, which is the first chunk in a PNG datastream.
+	 2. PLTE: palette table associated with indexed PNG images.
+	 3. IDAT: image data chunks.
+	 4. IEND: image trailer, which is the last chunk in a PNG datastream.
+
+	 The remaining 14 chunk types are termed ancillary chunk types, which encoders may generate and decoders may interpret.
+
+	 1. Transparency information: tRNS (see 11.3.2: Transparency information).
+	 2. Colour space information: cHRM, gAMA, iCCP, sBIT, sRGB (see 11.3.3: Colour space information).
+	 3. Textual information: iTXt, tEXt, zTXt (see 11.3.4: Textual information).
+	 4. Miscellaneous information: bKGD, hIST, pHYs, sPLT (see 11.3.5: Miscellaneous information).
+	 5. Time information: tIME (see 11.3.6: Time stamp information).
+
+	 */
+
+	private final void writeInt(OutputStream os, int value) throws IOException
+	{
+		os.write(0xff & (value >> 24));
+		os.write(0xff & (value >> 16));
+		os.write(0xff & (value >> 8));
+		os.write(0xff & (value >> 0));
+	}
+
+	private final void writeChunk(OutputStream os, byte chunk_type[],
+			byte data[]) throws IOException
+	{
+		int data_length = data == null ? 0 : data.length;
+		writeInt(os, data_length);
+		os.write(chunk_type);
+		if (data != null)
+			os.write(data);
+
+		//		Debug.debug("writeChunk chunk_type", chunk_type);
+		//		Debug.debug("writeChunk data", data);
+
+		{
+			PngCrc png_crc = new PngCrc();
+
+			long crc1 = png_crc
+					.start_partial_crc(chunk_type, chunk_type.length);
+			long crc2 = data == null ? crc1 : png_crc.continue_partial_crc(
+					crc1, data, data.length);
+			int crc = (int) png_crc.finish_partial_crc(crc2);
+
+			//			Debug.debug("crc1", crc1 + " (" + Long.toHexString(crc1)
+			//					+ ")");
+			//			Debug.debug("crc2", crc2 + " (" + Long.toHexString(crc2)
+			//					+ ")");
+			//			Debug.debug("crc3", crc + " (" + Integer.toHexString(crc)
+			//					+ ")");
+
+			writeInt(os, crc);
+		}
+	}
+
+	private static class ImageHeader
+	{
+		public final int width;
+		public final int height;
+		public final byte bit_depth;
+		public final byte colorType;
+		public final byte compression_method;
+		public final byte filter_method;
+		public final byte interlace_method;
+
+		public ImageHeader(int width, int height, byte bit_depth,
+				byte colorType, byte compression_method, byte filter_method,
+				byte interlace_method)
+		{
+			this.width = width;
+			this.height = height;
+			this.bit_depth = bit_depth;
+			this.colorType = colorType;
+			this.compression_method = compression_method;
+			this.filter_method = filter_method;
+			this.interlace_method = interlace_method;
+		}
+
+	}
+
+	private void writeChunkIHDR(OutputStream os, ImageHeader value)
+			throws IOException
+	{
+		ByteArrayOutputStream baos = new ByteArrayOutputStream();
+		writeInt(baos, value.width);
+		writeInt(baos, value.height);
+		baos.write(0xff & value.bit_depth);
+		baos.write(0xff & value.colorType);
+		baos.write(0xff & value.compression_method);
+		baos.write(0xff & value.filter_method);
+		baos.write(0xff & value.interlace_method);
+
+		//		Debug.debug("baos", baos.toByteArray());
+
+		writeChunk(os, IHDR_CHUNK_TYPE, baos.toByteArray());
+	}
+
+	private void writeChunkPLTE(OutputStream os, Palette palette)
+			throws IOException
+	{
+		int length = palette.length();
+		byte bytes[] = new byte[length * 3];
+
+		//		Debug.debug("length", length);
+		for (int i = 0; i < length; i++)
+		{
+			int rgb = palette.getEntry(i);
+			int index = i * 3;
+			//			Debug.debug("index", index);
+			bytes[index + 0] = (byte) (0xff & (rgb >> 16));
+			bytes[index + 1] = (byte) (0xff & (rgb >> 8));
+			bytes[index + 2] = (byte) (0xff & (rgb >> 0));
+		}
+
+		writeChunk(os, PLTE_CHUNK_TYPE, bytes);
+	}
+
+	private void writeChunkIEND(OutputStream os) throws IOException
+	{
+		writeChunk(os, IEND_CHUNK_TYPE, null);
+	}
+
+	private void writeChunkIDAT(OutputStream os, byte bytes[])
+			throws IOException
+	{
+		writeChunk(os, IDAT_CHUNK_TYPE, bytes);
+	}
+
+	private byte getColourType(boolean hasAlpha, boolean isGrayscale)
+	{
+		byte result;
+
+		boolean index = false; // charles
+
+		if (index)
+			result = COLOR_TYPE_INDEXED_COLOR;
+		else if (isGrayscale)
+		{
+			if (hasAlpha)
+				result = COLOR_TYPE_GREYSCALE_WITH_ALPHA;
+			else
+				result = COLOR_TYPE_GREYSCALE;
+		}
+		else if (hasAlpha)
+			result = COLOR_TYPE_TRUE_COLOR_WITH_ALPHA;
+		else
+			result = COLOR_TYPE_TRUE_COLOR;
+
+		return result;
+	}
+
+	private byte getBitDepth(final byte colorType, Map params)
+	{
+		byte result = 8;
+
+		Object o = params.get(PARAM_KEY_PNG_BIT_DEPTH);
+		if (o != null && o instanceof Number)
+		{
+			int value = ((Number) o).intValue();
+			switch (value)
+			{
+				case 1 :
+				case 2 :
+				case 4 :
+				case 8 :
+				case 16 :
+					result = (byte) value;
+				default :
+			}
+			switch (colorType)
+			{
+				case COLOR_TYPE_GREYSCALE :
+					break;
+				case COLOR_TYPE_INDEXED_COLOR :
+					result = (byte) Math.min(8, result);
+					break;
+				case COLOR_TYPE_GREYSCALE_WITH_ALPHA :
+				case COLOR_TYPE_TRUE_COLOR :
+				case COLOR_TYPE_TRUE_COLOR_WITH_ALPHA :
+					result = (byte) Math.max(8, result);
+					break;
+				default :
+			}
+		}
+
+		return result;
+	}
+
+	/*
+	 between two chunk types indicates alternatives.
+	 Table 5.3 — Chunk ordering rules Critical chunks
+	 (shall appear in this order, except PLTE is optional)
+	 Chunk name 	Multiple allowed 	Ordering constraints
+	 IHDR 	No 	Shall be first
+	 PLTE 	No 	Before first IDAT
+	 IDAT 	Yes 	Multiple IDAT chunks shall be consecutive
+	 IEND 	No 	Shall be last
+	 Ancillary chunks
+	 (need not appear in this order)
+	 Chunk name 	Multiple allowed 	Ordering constraints
+	 cHRM 	No 	Before PLTE and IDAT
+	 gAMA 	No 	Before PLTE and IDAT
+	 iCCP 	No 	Before PLTE and IDAT. If the iCCP chunk is present, the sRGB chunk should not be present.
+	 sBIT 	No 	Before PLTE and IDAT
+	 sRGB 	No 	Before PLTE and IDAT. If the sRGB chunk is present, the iCCP chunk should not be present.
+	 bKGD 	No 	After PLTE; before IDAT
+	 hIST 	No 	After PLTE; before IDAT
+	 tRNS 	No 	After PLTE; before IDAT
+	 pHYs 	No 	Before IDAT
+	 sPLT 	Yes 	Before IDAT
+	 tIME 	No 	None
+	 iTXt 	Yes 	None
+	 tEXt 	Yes 	None
+	 zTXt 	Yes 	None
+	 */
+
+	public void writeImage(BufferedImage src, OutputStream os, Map params)
+			throws ImageWriteException, IOException
+	{
+		// make copy of params; we'll clear keys as we consume them.
+		params = new Hashtable(params);
+
+		// clear format key.
+		if (params.containsKey(PARAM_KEY_FORMAT))
+			params.remove(PARAM_KEY_FORMAT);
+		// clear verbose key.
+		if (params.containsKey(PARAM_KEY_VERBOSE))
+			params.remove(PARAM_KEY_VERBOSE);
+
+		Map rawParams = new HashMap(params);
+		if (params.containsKey(PARAM_KEY_PNG_FORCE_TRUE_COLOR))
+			params.remove(PARAM_KEY_PNG_FORCE_TRUE_COLOR);
+		if (params.containsKey(PARAM_KEY_PNG_FORCE_INDEXED_COLOR))
+			params.remove(PARAM_KEY_PNG_FORCE_INDEXED_COLOR);
+		if (params.containsKey(PARAM_KEY_PNG_BIT_DEPTH))
+			params.remove(PARAM_KEY_PNG_BIT_DEPTH);
+		if (params.size() > 0)
+		{
+			Object firstKey = params.keySet().iterator().next();
+			throw new ImageWriteException("Unknown parameter: " + firstKey);
+		}
+		params = rawParams;
+
+		int width = src.getWidth();
+		int height = src.getHeight();
+
+		boolean hasAlpha = new PaletteFactory().hasTransparency(src);
+		if (verbose)
+			Debug.debug("hasAlpha", hasAlpha);
+		//			int transparency = new PaletteFactory().getTransparency(src);
+
+		boolean isGrayscale = new PaletteFactory().isGrayscale(src);
+		if (verbose)
+			Debug.debug("isGrayscale", isGrayscale);
+
+		byte colorType;
+		{
+			boolean force_indexed_color = getParamBoolean(params,
+					PARAM_KEY_PNG_FORCE_INDEXED_COLOR, false);
+			boolean force_true_color = getParamBoolean(params,
+					PARAM_KEY_PNG_FORCE_TRUE_COLOR, false);
+
+			if (force_indexed_color && force_true_color)
+				throw new ImageWriteException(
+						"Params: Cannot force both indexed and true color modes");
+			else if (force_indexed_color)
+			{
+				colorType = COLOR_TYPE_INDEXED_COLOR;
+			}
+			else if (force_true_color)
+			{
+				colorType = (byte) (hasAlpha
+						? COLOR_TYPE_TRUE_COLOR_WITH_ALPHA
+						: COLOR_TYPE_TRUE_COLOR);
+			}
+			else
+				colorType = getColourType(hasAlpha, isGrayscale);
+			if (verbose)
+				Debug.debug("colorType", colorType);
+		}
+
+		byte bit_depth = getBitDepth(colorType, params);
+		if (verbose)
+			Debug.debug("bit_depth", bit_depth);
+
+		int sample_depth;
+		if (colorType == COLOR_TYPE_INDEXED_COLOR)
+			sample_depth = 8;
+		else
+			sample_depth = bit_depth;
+		if (verbose)
+			Debug.debug("sample_depth", sample_depth);
+
+		{
+			os.write(PNG_Signature);
+		}
+		{
+			// IHDR  	Shall be first
+
+			byte compression_method = COMPRESSION_TYPE_INFLATE_DEFLATE;
+			byte filter_method = FILTER_METHOD_ADAPTIVE;
+			byte interlace_method = INTERLACE_METHOD_NONE; // charles
+
+			ImageHeader image_header = new ImageHeader(width, height,
+					bit_depth, colorType, compression_method, filter_method,
+					interlace_method);
+
+			writeChunkIHDR(os, image_header);
+		}
+
+		{
+			// sRGB 	No 	Before PLTE and IDAT. If the sRGB chunk is present, the iCCP chunk should not be present.
+
+			// charles
+		}
+
+		Palette palette = null;
+		if (colorType == COLOR_TYPE_INDEXED_COLOR)
+		{
+			// PLTE 	No 	Before first IDAT
+
+			int max_colors = hasAlpha ? 255 : 256;
+
+			palette = new MedianCutQuantizer(true).process(src, max_colors,
+					verbose);
+			//			Palette palette2 = new PaletteFactory().makePaletteSimple(src,
+			//					max_colors);
+
+			//			palette.dump();
+
+			writeChunkPLTE(os, palette);
+		}
+
+		{
+			//						Debug.debug("writing IDAT");
+
+			// IDAT 	Yes 	Multiple IDAT chunks shall be consecutive
+
+			byte uncompressed[];
+			{
+				ByteArrayOutputStream baos = new ByteArrayOutputStream();
+
+				boolean useAlpha = colorType == COLOR_TYPE_GREYSCALE_WITH_ALPHA
+						|| colorType == COLOR_TYPE_TRUE_COLOR_WITH_ALPHA;
+
+
+				int row[] = new int[width];
+				for (int y = 0; y < height; y++)
+				{
+					//					Debug.debug("y", y + "/" + height);
+					src.getRGB(0, y, width, 1, row, 0, width);
+
+					byte filter_type = FILTER_TYPE_NONE;
+					baos.write(filter_type);
+					for (int x = 0; x < width; x++)
+					{
+						int argb = row[x];
+
+						if (palette != null)
+						{
+							int index = palette.getPaletteIndex(argb);
+							baos.write(0xff & index);
+						}
+						else
+						{
+							int alpha = 0xff & (argb >> 24);
+							int red = 0xff & (argb >> 16);
+							int green = 0xff & (argb >> 8);
+							int blue = 0xff & (argb >> 0);
+
+							if (isGrayscale)
+							{
+								int gray = (red + green + blue) / 3;
+								//								if(y==0)
+								//								{
+								//									Debug.debug(x + ", " + y + " argb", Integer.toHexString(argb));
+								////									Debug.debug(x + ", " + y + " gray", gray);
+								////									Debug.debug(x + ", " + y + " gray", gray);
+								//									Debug.debug(x + ", " + y + " gray", gray + " " + Integer.toHexString(gray));
+								//									Debug.debug();
+								//								}
+								baos.write(gray);
+							}
+							else
+							{
+								baos.write(red);
+								baos.write(green);
+								baos.write(blue);
+							}
+							if (useAlpha)
+								baos.write(alpha);
+
+
+						}
+					}
+				}
+				uncompressed = baos.toByteArray();
+			}
+//			Debug.debug("uncompressed", uncompressed.length);
+
+			ByteArrayOutputStream baos = new ByteArrayOutputStream();
+			DeflaterOutputStream dos = new DeflaterOutputStream(baos);
+			int chunk_size = 256 * 1024;
+			for (int index = 0; index < uncompressed.length; index += chunk_size)
+			{
+				int end = Math.min(uncompressed.length, index + chunk_size);
+				int length = end - index;
+
+
+				dos.write(uncompressed, index, length);
+				dos.flush();
+				baos.flush();
+
+				byte compressed[] = baos.toByteArray();
+				baos.reset();
+				if (compressed.length > 0)
+				{
+//					Debug.debug("compressed", compressed.length);
+					writeChunkIDAT(os, compressed);
+				}
+
+			}
+			{
+				dos.finish();
+				byte compressed[] = baos.toByteArray();
+				if (compressed.length > 0)
+				{
+//					Debug.debug("compressed final", compressed.length);
+					writeChunkIDAT(os, compressed);
+				}
+			}
+		}
+
+		{
+			// IEND 	No 	Shall be last
+
+			writeChunkIEND(os);
+		}
+
+		/*
+		 Ancillary chunks
+		 (need not appear in this order)
+		 Chunk name 	Multiple allowed 	Ordering constraints
+		 cHRM 	No 	Before PLTE and IDAT
+		 gAMA 	No 	Before PLTE and IDAT
+		 iCCP 	No 	Before PLTE and IDAT. If the iCCP chunk is present, the sRGB chunk should not be present.
+		 sBIT 	No 	Before PLTE and IDAT
+		 sRGB 	No 	Before PLTE and IDAT. If the sRGB chunk is present, the iCCP chunk should not be present.
+		 bKGD 	No 	After PLTE; before IDAT
+		 hIST 	No 	After PLTE; before IDAT
+		 tRNS 	No 	After PLTE; before IDAT
+		 pHYs 	No 	Before IDAT
+		 sPLT 	Yes 	Before IDAT
+		 tIME 	No 	None
+		 iTXt 	Yes 	None
+		 tEXt 	Yes 	None
+		 zTXt 	Yes 	None
+		 */
+
+		os.close();
+	} // todo: filter types
+	// proper colour types
+	// srgb, etc.
+}

Propchange: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/PngWriter.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/ScanExpediter.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/ScanExpediter.java?rev=599250&view=auto
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/ScanExpediter.java (added)
+++ incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/ScanExpediter.java Wed Nov 28 18:27:05 2007
@@ -0,0 +1,289 @@
+/*
+ * 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.
+ */
+package org.apache.sanselan.formats.png;
+
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.sanselan.ImageReadException;
+import org.apache.sanselan.common.BinaryFileParser;
+import org.apache.sanselan.formats.png.chunks.PNGChunkPLTE;
+import org.apache.sanselan.formats.png.scanlinefilters.ScanlineFilter;
+import org.apache.sanselan.formats.png.scanlinefilters.ScanlineFilterAverage;
+import org.apache.sanselan.formats.png.scanlinefilters.ScanlineFilterNone;
+import org.apache.sanselan.formats.png.scanlinefilters.ScanlineFilterPaeth;
+import org.apache.sanselan.formats.png.scanlinefilters.ScanlineFilterSub;
+import org.apache.sanselan.formats.png.scanlinefilters.ScanlineFilterUp;
+import org.apache.sanselan.formats.transparencyfilters.TransparencyFilter;
+import org.apache.sanselan.util.Debug;
+
+public abstract class ScanExpediter extends BinaryFileParser
+{
+	protected final int width;
+	protected final int height;
+	protected final InputStream is;
+	protected final BufferedImage bi;
+	protected final int colorType;
+	protected final int bitDepth;
+	protected final int bytesPerPixel;
+	protected final int bitsPerPixel;
+	protected final PNGChunkPLTE fPNGChunkPLTE;
+	protected final GammaCorrection gammaCorrection;
+	protected final TransparencyFilter transparencyFilter;
+
+	public ScanExpediter(int width, int height, InputStream is,
+			BufferedImage bi, int color_type, int bitDepth, int bitsPerPixel,
+			PNGChunkPLTE pngChunkPLTE, GammaCorrection gammaCorrection,
+			TransparencyFilter transparencyFilter)
+
+	{
+		this.width = width;
+		this.height = height;
+		this.is = is;
+		this.bi = bi;
+		this.colorType = color_type;
+		this.bitDepth = bitDepth;
+		this.bytesPerPixel = this.getBitsToBytesRoundingUp(bitsPerPixel);
+		this.bitsPerPixel = bitsPerPixel;
+		this.fPNGChunkPLTE = pngChunkPLTE;
+		this.gammaCorrection = gammaCorrection;
+		this.transparencyFilter = transparencyFilter;
+
+//		Debug.debug("BitDepth", bitDepth);
+//		Debug.debug("bitsPerPixel", bitsPerPixel);
+		//		Debug.debug("colorType", colorType);
+
+		//		buffer = bi.getRaster().getDataBuffer();
+
+	}
+
+	protected int getBitsToBytesRoundingUp(int bits)
+	{
+		int bytes = bits / 8;
+		if ((bits % 8 > 0))
+			bytes++;
+		return bytes;
+	}
+
+	protected final int getPixelARGB(int alpha, int red, int green, int blue)
+	{
+		int rgb = ((0xff & alpha) << 24) | ((0xff & red) << 16)
+				| ((0xff & green) << 8) | ((0xff & blue) << 0);
+
+		return rgb;
+	}
+
+	protected final int getPixelRGB(int red, int green, int blue)
+	{
+		return getPixelARGB(0xff, red, green, blue);
+	}
+
+	public abstract void drive() throws ImageReadException, IOException;
+
+	private long count = 0;
+
+	protected int getRGB(BitParser bitParser, int pixel_index_in_scanline)
+			throws ImageReadException, IOException
+	{
+		switch (colorType)
+		{
+			case 0 : //       1,2,4,8,16  Each pixel is a grayscale sample.
+			{
+				int sample = bitParser.getSampleAsByte(pixel_index_in_scanline,
+						0);
+
+				//				if (verbose)
+				//					Debug.debug("sample", Integer.toHexString(sample));
+
+				if (gammaCorrection != null)
+				{
+					sample = gammaCorrection.correctSample(sample);
+					//					if (verbose)
+					//						Debug.debug("gammaCorrection", Integer
+					//								.toHexString(sample));
+				}
+
+				int rgb = getPixelRGB(sample, sample, sample);
+				//				if (verbose)
+				//					Debug.debug("rgb", Integer.toHexString(rgb));
+
+				if (transparencyFilter != null)
+					rgb = transparencyFilter.filter(rgb, sample);
+
+				return rgb;
+
+			}
+			case 2 : //     8,16        Each pixel is an R,G,B triple.
+			{
+				int red = bitParser.getSampleAsByte(pixel_index_in_scanline, 0);
+				int green = bitParser.getSampleAsByte(pixel_index_in_scanline,
+						1);
+				int blue = bitParser
+						.getSampleAsByte(pixel_index_in_scanline, 2);
+
+				int rgb = getPixelRGB(red, green, blue);
+
+				//				count++;
+				//
+				//				if ((count% 256) == 0)
+				//				{
+				//				this.debugNumber("before: " + count , rgb, 4);
+				//				}
+
+				if (transparencyFilter != null)
+					rgb = transparencyFilter.filter(rgb, -1);
+
+				if (gammaCorrection != null)
+				{
+					int alpha = (0xff000000 & rgb) >> 24; // make sure to preserve transparency
+					red = gammaCorrection.correctSample(red);
+					green = gammaCorrection.correctSample(green);
+					blue = gammaCorrection.correctSample(blue);
+					rgb = getPixelARGB(alpha, red, green, blue);
+				}
+
+				//				if ((count% 256) == 0)
+				//				{
+				//				this.debugNumber("after: " + count , rgb, 4);
+				//				}
+
+				return rgb;
+			}
+				//					   
+			case 3 : //     1,2,4,8     Each pixel is a palette index;
+				//					                       a PLTE chunk must appear.
+			{
+				int index = bitParser.getSample(pixel_index_in_scanline, 0);
+
+				int rgb = fPNGChunkPLTE.getRGB(index);
+
+				if (transparencyFilter != null)
+					rgb = transparencyFilter.filter(rgb, index);
+
+				return rgb;
+				//				return 0xffff0000;
+			}
+			case 4 : //     8,16        Each pixel is a grayscale sample,
+				//					                       followed by an alpha sample.
+			{
+				int sample = bitParser.getSampleAsByte(pixel_index_in_scanline,
+						0);
+				int alpha = bitParser.getSampleAsByte(pixel_index_in_scanline,
+						1);
+
+				if (gammaCorrection != null)
+					sample = gammaCorrection.correctSample(sample);
+
+				int rgb = getPixelARGB(alpha, sample, sample, sample);
+				return rgb;
+
+			}
+			case 6 : //    8,16        Each pixel is an R,G,B triple,
+			{
+				int red = bitParser.getSampleAsByte(pixel_index_in_scanline, 0);
+				int green = bitParser.getSampleAsByte(pixel_index_in_scanline,
+						1);
+				int blue = bitParser
+						.getSampleAsByte(pixel_index_in_scanline, 2);
+				int alpha = bitParser.getSampleAsByte(pixel_index_in_scanline,
+						3);
+
+				if (gammaCorrection != null)
+				{
+					red = gammaCorrection.correctSample(red);
+					green = gammaCorrection.correctSample(green);
+					blue = gammaCorrection.correctSample(blue);
+				}
+
+				int rgb = getPixelARGB(alpha, red, green, blue);
+				return rgb;
+			}
+			default :
+				throw new ImageReadException("PNG: unknown color type: "
+						+ colorType);
+		}
+	}
+
+	protected ScanlineFilter getScanlineFilter(int filter_type,
+			int BytesPerPixel) throws ImageReadException, IOException
+	{
+		ScanlineFilter filter;
+
+		switch (filter_type)
+		{
+			case 0 : //None
+				filter = new ScanlineFilterNone();
+				break;
+
+			case 1 : // Sub
+				filter = new ScanlineFilterSub(BytesPerPixel);
+				break;
+
+			case 2 : //Up
+				filter = new ScanlineFilterUp(BytesPerPixel);
+				break;
+
+			case 3 : // Average
+				filter = new ScanlineFilterAverage(BytesPerPixel);
+				break;
+
+			case 4 : // Paeth
+				filter = new ScanlineFilterPaeth(BytesPerPixel);
+				break;
+
+			default :
+				throw new ImageReadException("PNG: unknown filter_type: "
+						+ filter_type);
+
+		}
+
+		return filter;
+	}
+
+	protected byte[] unfilterScanline(
+	//			int filter_method,
+			int filter_type, byte src[], byte prev[], int BytesPerPixel)
+			throws ImageReadException, IOException
+	{
+		ScanlineFilter filter = getScanlineFilter(filter_type, BytesPerPixel);
+
+		byte dst[] = new byte[src.length];
+		filter.unfilter(src, dst, prev);
+		return dst;
+	}
+
+	protected byte[] getNextScanline(InputStream is, int length, byte prev[],
+			int BytesPerPixel) throws ImageReadException, IOException
+	{
+		int filter_type = is.read();
+		if (filter_type < 0)
+			throw new ImageReadException("PNG: missing filter type");
+		//			System.out.println("\t" + "filter: " + filter_type);
+
+		
+		//				byte unfiltered[] = new byte[length];
+		byte scanline[] = this.readByteArray("scanline", length, is,
+				"PNG: missing image data");
+
+		byte unfiltered[] = unfilterScanline(filter_type, scanline, prev,
+				BytesPerPixel);
+
+		return unfiltered;
+	}
+
+}
\ No newline at end of file

Propchange: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/ScanExpediter.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/ScanExpediterInterlaced.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/ScanExpediterInterlaced.java?rev=599250&view=auto
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/ScanExpediterInterlaced.java (added)
+++ incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/ScanExpediterInterlaced.java Wed Nov 28 18:27:05 2007
@@ -0,0 +1,122 @@
+/*
+ * 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.
+ */
+package org.apache.sanselan.formats.png;
+
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.sanselan.ImageReadException;
+import org.apache.sanselan.formats.png.chunks.PNGChunkPLTE;
+import org.apache.sanselan.formats.transparencyfilters.TransparencyFilter;
+
+public class ScanExpediterInterlaced extends ScanExpediter
+{
+	public ScanExpediterInterlaced(int width, int height, InputStream is,
+			BufferedImage bi, int color_type, int BitDepth, int bits_per_pixel,
+			PNGChunkPLTE fPNGChunkPLTE, GammaCorrection fGammaCorrection,
+			TransparencyFilter fTransparencyFilter)
+
+	{
+		super(width, height, is, bi, color_type, BitDepth, bits_per_pixel,
+				fPNGChunkPLTE, fGammaCorrection, fTransparencyFilter);
+	}
+
+	private void visit(int x, int y, BufferedImage bi, BitParser fBitParser,
+			int color_type, int pixel_index_in_scanline,
+			PNGChunkPLTE fPNGChunkPLTE, GammaCorrection fGammaCorrection)
+			throws ImageReadException, IOException
+	{
+		int rgb = getRGB(fBitParser,
+		//					color_type, 
+				pixel_index_in_scanline
+		//					,
+		//					fPNGChunkPLTE, fGammaCorrection
+		);
+
+		bi.setRGB(x, y, rgb);
+
+		//				buffer.setElem(y * width +x , rgb);
+
+	}
+
+	public static final int Starting_Row[] = {
+			0, 0, 4, 0, 2, 0, 1
+	};
+	public static final int Starting_Col[] = {
+			0, 4, 0, 2, 0, 1, 0
+	};
+	public static final int Row_Increment[] = {
+			8, 8, 8, 4, 4, 2, 2
+	};
+	public static final int Col_Increment[] = {
+			8, 8, 4, 4, 2, 2, 1
+	};
+	public static final int Block_Height[] = {
+			8, 8, 4, 4, 2, 2, 1
+	};
+	public static final int Block_Width[] = {
+			8, 4, 4, 2, 2, 1, 1
+	};
+
+	public void drive() throws ImageReadException, IOException
+	{
+
+		int pass = 1;
+		while (pass <= 7)
+		{
+			byte prev[] = null;
+
+			int y = Starting_Row[pass - 1];
+			//				int y_stride = Row_Increment[pass - 1];
+			boolean rows_in_pass = (y < height);
+			while (y < height)
+			{
+				int x = Starting_Col[pass - 1];
+				int pixel_index_in_scanline = 0;
+
+				if (x < width)
+				{
+					//						 only get data if there are pixels in this scanline/pass
+					int ColumnsInRow = 1 + ((width - Starting_Col[pass - 1] - 1) / Col_Increment[pass - 1]);
+					int bitsPerScanLine = bitsPerPixel * ColumnsInRow;
+					int pixel_bytes_per_scan_line = getBitsToBytesRoundingUp(bitsPerScanLine);
+
+					byte unfiltered[] = getNextScanline(is,
+							pixel_bytes_per_scan_line, prev, bytesPerPixel);
+
+					prev = unfiltered;
+
+					BitParser fBitParser = new BitParser(unfiltered,
+							bitsPerPixel, bitDepth);
+
+					while (x < width)
+					{
+						visit(x, y, bi, fBitParser, colorType,
+								pixel_index_in_scanline, fPNGChunkPLTE,
+								gammaCorrection);
+
+						x = x + Col_Increment[pass - 1];
+						pixel_index_in_scanline++;
+					}
+				}
+				y = y + Row_Increment[pass - 1];
+			}
+			pass = pass + 1;
+		}
+	}
+}
\ No newline at end of file

Propchange: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/ScanExpediterInterlaced.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/ScanExpediterSimple.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/ScanExpediterSimple.java?rev=599250&view=auto
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/ScanExpediterSimple.java (added)
+++ incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/ScanExpediterSimple.java Wed Nov 28 18:27:05 2007
@@ -0,0 +1,88 @@
+/*
+ * 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.
+ */
+package org.apache.sanselan.formats.png;
+
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.sanselan.ImageReadException;
+import org.apache.sanselan.formats.png.chunks.PNGChunkPLTE;
+import org.apache.sanselan.formats.transparencyfilters.TransparencyFilter;
+import org.apache.sanselan.util.Debug;
+
+public class ScanExpediterSimple extends ScanExpediter
+{
+	public ScanExpediterSimple(int width, int height, InputStream is,
+			BufferedImage bi, int color_type, int BitDepth, int bitsPerPixel,
+			PNGChunkPLTE pngChunkPLTE, GammaCorrection gammaCorrection,
+			TransparencyFilter transparencyFilter)
+
+	{
+		super(width, height, is, bi, color_type, BitDepth, bitsPerPixel,
+				pngChunkPLTE, gammaCorrection, transparencyFilter);
+	}
+
+	public void drive() throws ImageReadException, IOException
+	{
+//		Debug.debug("width", width);
+		int bitsPerScanLine = bitsPerPixel * width;
+//		Debug.debug("bitsPerPixel", bitsPerPixel);
+//		Debug.debug("bitsPerScanLine", bitsPerScanLine);
+		int pixelBytesPerScanLine = getBitsToBytesRoundingUp(bitsPerScanLine);
+//		Debug.debug("pixelBytesPerScanLine", pixelBytesPerScanLine);
+		byte prev[] = null;
+
+		for (int y = 0; y < height; y++)
+		{
+//			Debug.debug("getNextScanline", y + "/" + height + ", " + pixelBytesPerScanLine);
+
+			byte unfiltered[] = getNextScanline(is, pixelBytesPerScanLine,
+					prev, bytesPerPixel);
+
+			prev = unfiltered;
+
+			BitParser bitParser = new BitParser(unfiltered, bitsPerPixel,
+					bitDepth);
+
+			for (int x = 0; x < width; x++)
+			{
+				int rgb = getRGB(bitParser, x);
+
+				//				if (((x + y) % 256) == 0)
+				//				{
+				//					this.debugNumber(x + ", " + y, rgb, 4);
+				//				} 
+				//				this.debugNumber(x + ", " + y, rgb, 4);
+				//				if (y == 0)
+				//					Debug.debug("read " + x + ", " + y, Integer
+				//							.toHexString(rgb));
+
+				//				Debug.debug("pngChunkIHDR.interlaceMethod", pngChunkIHDR.interlaceMethod);
+				bi.setRGB(x, y, rgb);
+				//				buffer.setElem(y * width +x , rgb);
+
+				//				bi.setRGB(x, y, 0xff000000 | rgb);
+				//				bi.setRGB(x, y, 0xff000000);
+				//				bi.setRGB(x, y, 0xffffffff);
+			}
+		}
+
+		//		Debug.debug("bi", bi.getWidth());
+		//		Debug.debug("bi", bi.getHeight());
+	}
+}
\ No newline at end of file

Propchange: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/ScanExpediterSimple.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGChunk.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGChunk.java?rev=599250&view=auto
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGChunk.java (added)
+++ incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGChunk.java Wed Nov 28 18:27:05 2007
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+package org.apache.sanselan.formats.png.chunks;
+
+import java.io.ByteArrayInputStream;
+
+import org.apache.sanselan.common.BinaryFileParser;
+
+public class PNGChunk extends BinaryFileParser
+{
+	public final int length;
+	public final int chunkType;
+	public final int crc;
+	public final byte bytes[];
+
+	public final boolean propertyBits[];
+	public final boolean ancillary, isPrivate, reserved, safeToCopy;
+
+	public PNGChunk(int Length, int ChunkType, int CRC, byte bytes[])
+	{
+		this.length = Length;
+		this.chunkType = ChunkType;
+		this.crc = CRC;
+		this.bytes = bytes;
+
+		propertyBits = new boolean[4];
+		int shift = 24;
+		for (int i = 0; i < 4; i++)
+		{
+			int theByte = 0xff & (ChunkType >> shift);
+			shift -= 8;
+			int theMask = 1 << 5;
+			propertyBits[i] = ((theByte & theMask) > 0);
+		}
+
+		ancillary = propertyBits[0];
+		isPrivate = propertyBits[1];
+		reserved = propertyBits[2];
+		safeToCopy = propertyBits[3];
+	}
+
+	protected ByteArrayInputStream getDataStream()
+	{
+		ByteArrayInputStream is = new ByteArrayInputStream(bytes);
+		return is;
+	}
+
+}
\ No newline at end of file

Propchange: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGChunk.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGChunkIDAT.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGChunkIDAT.java?rev=599250&view=auto
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGChunkIDAT.java (added)
+++ incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGChunkIDAT.java Wed Nov 28 18:27:05 2007
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+package org.apache.sanselan.formats.png.chunks;
+
+import java.io.IOException;
+
+public class PNGChunkIDAT extends PNGChunk
+{
+	public PNGChunkIDAT(int Length, int ChunkType, int CRC, byte bytes[])
+			throws IOException
+	{
+		super(Length, ChunkType, CRC, bytes);
+	}
+}
\ No newline at end of file

Propchange: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGChunkIDAT.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGChunkIHDR.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGChunkIHDR.java?rev=599250&view=auto
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGChunkIHDR.java (added)
+++ incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGChunkIHDR.java Wed Nov 28 18:27:05 2007
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+package org.apache.sanselan.formats.png.chunks;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+
+import org.apache.sanselan.ImageReadException;
+
+public class PNGChunkIHDR extends PNGChunk
+{
+	public final int width;
+	public final int height;
+	public final int bitDepth;
+	public final int colorType;
+	public final int compressionMethod;
+	public final int filterMethod;
+	public final int interlaceMethod;
+
+	public PNGChunkIHDR(int Length, int ChunkType, int CRC, byte bytes[])
+			throws ImageReadException, IOException
+	{
+		super(Length, ChunkType, CRC, bytes);
+
+		ByteArrayInputStream is = new ByteArrayInputStream(bytes);
+		width = read4Bytes("Width", is, "Not a Valid Png File: IHDR Corrupt");
+		height = read4Bytes("Height", is, "Not a Valid Png File: IHDR Corrupt");
+		bitDepth = readByte("BitDepth", is,
+				"Not a Valid Png File: IHDR Corrupt");
+		colorType = readByte("ColorType", is,
+				"Not a Valid Png File: IHDR Corrupt");
+		compressionMethod = readByte("CompressionMethod", is,
+				"Not a Valid Png File: IHDR Corrupt");
+		filterMethod = readByte("FilterMethod", is,
+				"Not a Valid Png File: IHDR Corrupt");
+		interlaceMethod = readByte("InterlaceMethod", is,
+				"Not a Valid Png File: IHDR Corrupt");
+	}
+}
\ No newline at end of file

Propchange: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGChunkIHDR.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGChunkPLTE.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGChunkPLTE.java?rev=599250&view=auto
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGChunkPLTE.java (added)
+++ incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGChunkPLTE.java Wed Nov 28 18:27:05 2007
@@ -0,0 +1,84 @@
+/*
+ * 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.
+ */
+package org.apache.sanselan.formats.png.chunks;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+
+import org.apache.sanselan.ImageReadException;
+import org.apache.sanselan.formats.png.GammaCorrection;
+
+public class PNGChunkPLTE extends PNGChunk
+{
+	public final int rgb[];
+
+	//		public final int red[];
+	//		public final int green[];
+	//		public final int blue[];
+
+	public PNGChunkPLTE(int Length, int ChunkType, int CRC, byte bytes[])
+			throws ImageReadException, IOException
+	{
+		super(Length, ChunkType, CRC, bytes);
+
+		ByteArrayInputStream is = new ByteArrayInputStream(bytes);
+
+		if ((Length % 3) != 0)
+			throw new ImageReadException("PLTE: wrong length: " + Length);
+
+		int count = Length / 3;
+
+		rgb = new int[count];
+		//			red = new int[count];
+		//			green = new int[count];
+		//			blue = new int[count];
+
+		for (int i = 0; i < count; i++)
+		{
+			//				red[i] = read_byte("red[" + i + "]", is,
+			//				"Not a Valid Png File: PLTE Corrupt");
+			//		green[i] = read_byte("green[" + i + "]", is,
+			//				"Not a Valid Png File: PLTE Corrupt");
+			//		blue[i] = read_byte("blue[" + i + "]", is,
+			//				"Not a Valid Png File: PLTE Corrupt");
+			int red = readByte("red[" + i + "]", is,
+					"Not a Valid Png File: PLTE Corrupt");
+			int green = readByte("green[" + i + "]", is,
+					"Not a Valid Png File: PLTE Corrupt");
+			int blue = readByte("blue[" + i + "]", is,
+					"Not a Valid Png File: PLTE Corrupt");
+			rgb[i] = 0xff000000 | ((0xff & red) << 16) | ((0xff & green) << 8)
+					| ((0xff & blue) << 0);
+		}
+	}
+
+	public int getRGB(int index) throws ImageReadException, IOException
+	{
+		if ((index < 0) || (index >= rgb.length))
+			throw new ImageReadException("PNG: unknown Palette reference: "
+					+ index);
+		return rgb[index];
+	}
+
+	public void correct(GammaCorrection fGammaCorrection)
+	{
+		for (int i = 0; i < rgb.length; i++)
+			//		{int pixel = 
+			rgb[i] = fGammaCorrection.correctARGB(rgb[i]);
+	}
+
+}
\ No newline at end of file

Propchange: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGChunkPLTE.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGChunkgAMA.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGChunkgAMA.java?rev=599250&view=auto
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGChunkgAMA.java (added)
+++ incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGChunkgAMA.java Wed Nov 28 18:27:05 2007
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+package org.apache.sanselan.formats.png.chunks;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+
+import org.apache.sanselan.ImageReadException;
+
+public class PNGChunkgAMA extends PNGChunk
+{
+	public final int Gamma;
+
+	public PNGChunkgAMA(int Length, int ChunkType, int CRC, byte bytes[])
+			throws ImageReadException, IOException
+	{
+		super(Length, ChunkType, CRC, bytes);
+
+		ByteArrayInputStream is = new ByteArrayInputStream(bytes);
+		Gamma = read4Bytes("Gamma", is, "Not a Valid Png File: gAMA Corrupt");
+	}
+
+	public double getGamma()
+	{
+		return 1.0 / ((double) Gamma / 100000.0);
+	}
+
+}
\ No newline at end of file

Propchange: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGChunkgAMA.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGChunkiCCP.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGChunkiCCP.java?rev=599250&view=auto
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGChunkiCCP.java (added)
+++ incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGChunkiCCP.java Wed Nov 28 18:27:05 2007
@@ -0,0 +1,79 @@
+/*
+ * 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.
+ */
+package org.apache.sanselan.formats.png.chunks;
+
+import java.io.IOException;
+
+import org.apache.sanselan.ImageReadException;
+import org.apache.sanselan.common.ZLibInflater;
+
+public class PNGChunkiCCP extends PNGChunk
+{
+	//	private final PngImageParser parser;
+	public final String ProfileName;
+	public final int CompressionMethod;
+	public final byte CompressedProfile[];
+	public final byte UncompressedProfile[];
+
+	public PNGChunkiCCP(
+	//			PngImageParser parser, 
+			int Length, int ChunkType, int CRC, byte bytes[])
+			throws ImageReadException, IOException
+	{
+		super(Length, ChunkType, CRC, bytes);
+		//		this.parser = parser;
+
+		{
+			int index = findNull(bytes);
+			if (index < 0)
+				throw new ImageReadException("PNGChunkiCCP: No Profile Name");
+			byte name_bytes[] = new byte[index];
+			System.arraycopy(bytes, 0, name_bytes, 0, index);
+			ProfileName = new String(name_bytes);
+
+			CompressionMethod = bytes[index + 1];
+
+			int CompressedProfileLength = bytes.length - (index + 1 + 1);
+			CompressedProfile = new byte[CompressedProfileLength];
+			System.arraycopy(bytes, index + 1 + 1, CompressedProfile, 0,
+					CompressedProfileLength);
+
+			if (getDebug())
+			{
+				System.out.println("ProfileName: " + ProfileName);
+				System.out.println("ProfileName.length(): "
+						+ ProfileName.length());
+				System.out.println("CompressionMethod: " + CompressionMethod);
+				System.out.println("CompressedProfileLength: "
+						+ CompressedProfileLength);
+				System.out.println("bytes.length: " + bytes.length);
+			}
+
+			UncompressedProfile = new ZLibInflater()
+					.zlibInflate(CompressedProfile);
+
+			if (getDebug())
+			{
+				System.out.println("UncompressedProfile: "
+						+ ((UncompressedProfile == null) ? "null" : ""
+								+ bytes.length));
+			}
+
+		}
+	}
+
+}
\ No newline at end of file

Propchange: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGChunkiCCP.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGChunkpHYs.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGChunkpHYs.java?rev=599250&view=auto
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGChunkpHYs.java (added)
+++ incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGChunkpHYs.java Wed Nov 28 18:27:05 2007
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+package org.apache.sanselan.formats.png.chunks;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+
+import org.apache.sanselan.ImageReadException;
+
+public class PNGChunkpHYs extends PNGChunk
+{
+	public final int PixelsPerUnitXAxis;
+	public final int PixelsPerUnitYAxis;
+	public final int UnitSpecifier;
+
+	public PNGChunkpHYs(int Length, int ChunkType, int CRC, byte bytes[])
+			throws ImageReadException, IOException
+	{
+		super(Length, ChunkType, CRC, bytes);
+
+		ByteArrayInputStream is = new ByteArrayInputStream(bytes);
+
+		PixelsPerUnitXAxis = read4Bytes("PixelsPerUnitXAxis", is,
+				"Not a Valid Png File: pHYs Corrupt");
+		PixelsPerUnitYAxis = read4Bytes("PixelsPerUnitYAxis", is,
+				"Not a Valid Png File: pHYs Corrupt");
+		UnitSpecifier = readByte("Unit specifier", is,
+				"Not a Valid Png File: pHYs Corrupt");
+	}
+
+}
\ No newline at end of file

Propchange: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGChunkpHYs.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGChunktEXt.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGChunktEXt.java?rev=599250&view=auto
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGChunktEXt.java (added)
+++ incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGChunktEXt.java Wed Nov 28 18:27:05 2007
@@ -0,0 +1,79 @@
+/*
+ * 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.
+ */
+package org.apache.sanselan.formats.png.chunks;
+
+import java.io.IOException;
+
+import org.apache.sanselan.ImageReadException;
+
+public class PNGChunktEXt extends PNGTextChunk
+{
+	//		public final int Length;
+	//		public final int ChunkType;
+	//		public final int CRC;
+	//		public final byte bytes[];
+	public final String Keyword, Text;
+
+	//		public final int CompressionMethod;
+
+	public PNGChunktEXt(int Length, int ChunkType, int CRC, byte bytes[])
+			throws ImageReadException, IOException
+	{
+		super(Length, ChunkType, CRC, bytes);
+
+		{
+			//				debug_ByteArray("PNGChunktEXt", bytes);
+
+			int index = findNull(bytes);
+			if (index < 0)
+				throw new ImageReadException("PNGChunktEXt: No Profile Name");
+			byte name_bytes[] = new byte[index];
+			System.arraycopy(bytes, 0, name_bytes, 0, index);
+			Keyword = new String(name_bytes);
+
+			//				CompressionMethod = bytes[index + 1];
+
+			int TextLength = bytes.length - (index + 1);
+			byte Text_bytes[] = new byte[TextLength];
+			System.arraycopy(bytes, index + 1, Text_bytes, 0, TextLength);
+			Text = new String(Text_bytes);
+
+			if (getDebug())
+			{
+				System.out.println("Keyword: " + Keyword);
+				System.out.println("Text: " + Text);
+			}
+
+		}
+	}
+
+	/**
+	 * @return Returns the keyword.
+	 */
+	public String getKeyword()
+	{
+		return Keyword;
+	}
+
+	/**
+	 * @return Returns the text.
+	 */
+	public String getText()
+	{
+		return Text;
+	}
+}
\ No newline at end of file

Propchange: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGChunktEXt.java
------------------------------------------------------------------------------
    svn:eol-style = native