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/17 21:58:40 UTC
svn commit: r596008 [7/15] - in /incubator/sanselan/trunk/src:
main/java/org/apache/sanselan/ main/java/org/apache/sanselan/color/
main/java/org/apache/sanselan/common/
main/java/org/apache/sanselan/common/byteSources/
main/java/org/apache/sanselan/com...
Added: incubator/sanselan/trunk/src/main/java/org/cmc/sanselan/formats/png/PngImageParser.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/cmc/sanselan/formats/png/PngImageParser.java?rev=596008&view=auto
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/cmc/sanselan/formats/png/PngImageParser.java (added)
+++ incubator/sanselan/trunk/src/main/java/org/cmc/sanselan/formats/png/PngImageParser.java Sat Nov 17 13:58:22 2007
@@ -0,0 +1,939 @@
+/*
+ * 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.cmc.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.cmc.sanselan.ColorTools;
+import org.cmc.sanselan.ImageFormat;
+import org.cmc.sanselan.ImageInfo;
+import org.cmc.sanselan.ImageParser;
+import org.cmc.sanselan.ImageReadException;
+import org.cmc.sanselan.ImageWriteException;
+import org.cmc.sanselan.common.IImageMetadata;
+import org.cmc.sanselan.common.ImageMetadata;
+import org.cmc.sanselan.common.byteSources.ByteSource;
+import org.cmc.sanselan.formats.png.chunks.PNGChunk;
+import org.cmc.sanselan.formats.png.chunks.PNGChunkIDAT;
+import org.cmc.sanselan.formats.png.chunks.PNGChunkIHDR;
+import org.cmc.sanselan.formats.png.chunks.PNGChunkPLTE;
+import org.cmc.sanselan.formats.png.chunks.PNGChunkgAMA;
+import org.cmc.sanselan.formats.png.chunks.PNGChunkiCCP;
+import org.cmc.sanselan.formats.png.chunks.PNGChunkpHYs;
+import org.cmc.sanselan.formats.png.chunks.PNGChunktEXt;
+import org.cmc.sanselan.formats.png.chunks.PNGChunkzTXt;
+import org.cmc.sanselan.formats.png.chunks.PNGTextChunk;
+import org.cmc.sanselan.formats.transparencyfilters.TransparencyFilter;
+import org.cmc.sanselan.formats.transparencyfilters.TransparencyFilterGrayscale;
+import org.cmc.sanselan.formats.transparencyfilters.TransparencyFilterIndexedColor;
+import org.cmc.sanselan.formats.transparencyfilters.TransparencyFilterTrueColor;
+import org.cmc.sanselan.icc.IccProfileParser;
+import org.cmc.sanselan.util.Debug;
+
+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 0 :
+ return true;
+ case 2 :
+ return false;
+ case 3 :
+ return false;
+ case 4 :
+ return true;
+ case 6 :
+ 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 0 :
+ return 1;
+ case 2 :
+ return 3;
+ case 3 :
+ return 1; // is this accurate ? how may bits per index?
+ case 4 :
+ return 2;
+ case 6 :
+ 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 0 : // 1,2,4,8,16 Each pixel is a grayscale sample.
+ case 2 : // 8,16 Each pixel is an R,G,B triple.
+ case 3 : // 1,2,4,8 Each pixel is a palette index;
+ return false;
+ case 4 : // 8,16 Each pixel is a grayscale sample,
+ // followed by an alpha sample.
+ case 6 : // 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 0 : // 1,2,4,8,16 Each pixel is a grayscale sample.
+ return "grayscale";
+ case 2 : // 8,16 Each pixel is an R,G,B triple.
+ return "rgb";
+ case 3 : // 1,2,4,8 Each pixel is a palette index;
+ return "indexed rgb";
+ case 4 : // 8,16 Each pixel is a grayscale sample,
+ // followed by an alpha sample.
+ return "grayscale w/ alpha";
+ case 6 : // 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 0 : // 1,2,4,8,16 Each pixel is a grayscale sample.
+ return new TransparencyFilterGrayscale(pngChunktRNS.bytes);
+ case 2 : // 8,16 Each pixel is an R,G,B triple.
+ return new TransparencyFilterTrueColor(pngChunktRNS.bytes);
+ case 3 : // 1,2,4,8 Each pixel is a palette index;
+ return new TransparencyFilterIndexedColor(pngChunktRNS.bytes);
+ case 4 : // 8,16 Each pixel is a grayscale sample,
+ case 6 : // 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 0 : // 1,2,4,8,16 Each pixel is a grayscale sample.
+ case 4 : // 8,16 Each pixel is a grayscale sample,
+ // followed by an alpha sample.
+ ColorType = ImageInfo.COLOR_TYPE_GRAYSCALE;
+ break;
+ case 2 : // 8,16 Each pixel is an R,G,B triple.
+ case 3 : // 1,2,4,8 Each pixel is a palette index;
+ case 6 : // 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);
+
+
+ //
+ // WritableRaster raster = cm.createCompatibleWritableRaster(width,
+ // height);
+ //
+ // boolean premult = cm.isAlphaPremultiplied();
+ //
+ // BufferedImage result = new BufferedImage(cm, raster, premult, null);
+ BufferedImage result;
+ if (isGrayscale)
+ result = getBufferedImageFactory(params)
+ .getGrayscaleBufferedImage(width, height);
+ else
+ result = getBufferedImageFactory(params).getColorBufferedImage(
+ width, height);
+
+ ByteArrayInputStream bais = new ByteArrayInputStream(compressed);
+ InflaterInputStream 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/cmc/sanselan/formats/png/PngImageParser.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: incubator/sanselan/trunk/src/main/java/org/cmc/sanselan/formats/png/PngWriter.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/cmc/sanselan/formats/png/PngWriter.java?rev=596008&view=auto
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/cmc/sanselan/formats/png/PngWriter.java (added)
+++ incubator/sanselan/trunk/src/main/java/org/cmc/sanselan/formats/png/PngWriter.java Sat Nov 17 13:58:22 2007
@@ -0,0 +1,514 @@
+/*
+ * 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.cmc.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.cmc.sanselan.ImageWriteException;
+import org.cmc.sanselan.palette.MedianCutQuantizer;
+import org.cmc.sanselan.palette.Palette;
+import org.cmc.sanselan.palette.PaletteFactory;
+import org.cmc.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();
+
+ 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);
+ }
+ }
+ }
+ }
+ 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);
+
+ 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", 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/cmc/sanselan/formats/png/PngWriter.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: incubator/sanselan/trunk/src/main/java/org/cmc/sanselan/formats/png/ScanExpediter.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/cmc/sanselan/formats/png/ScanExpediter.java?rev=596008&view=auto
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/cmc/sanselan/formats/png/ScanExpediter.java (added)
+++ incubator/sanselan/trunk/src/main/java/org/cmc/sanselan/formats/png/ScanExpediter.java Sat Nov 17 13:58:22 2007
@@ -0,0 +1,288 @@
+/*
+ * 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.cmc.sanselan.formats.png;
+
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.cmc.sanselan.ImageReadException;
+import org.cmc.sanselan.common.BinaryFileParser;
+import org.cmc.sanselan.formats.png.chunks.PNGChunkPLTE;
+import org.cmc.sanselan.formats.png.scanlinefilters.ScanlineFilter;
+import org.cmc.sanselan.formats.png.scanlinefilters.ScanlineFilterAverage;
+import org.cmc.sanselan.formats.png.scanlinefilters.ScanlineFilterNone;
+import org.cmc.sanselan.formats.png.scanlinefilters.ScanlineFilterPaeth;
+import org.cmc.sanselan.formats.png.scanlinefilters.ScanlineFilterSub;
+import org.cmc.sanselan.formats.png.scanlinefilters.ScanlineFilterUp;
+import org.cmc.sanselan.formats.transparencyfilters.TransparencyFilter;
+import org.cmc.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/cmc/sanselan/formats/png/ScanExpediter.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: incubator/sanselan/trunk/src/main/java/org/cmc/sanselan/formats/png/ScanExpediterInterlaced.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/cmc/sanselan/formats/png/ScanExpediterInterlaced.java?rev=596008&view=auto
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/cmc/sanselan/formats/png/ScanExpediterInterlaced.java (added)
+++ incubator/sanselan/trunk/src/main/java/org/cmc/sanselan/formats/png/ScanExpediterInterlaced.java Sat Nov 17 13:58:22 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.cmc.sanselan.formats.png;
+
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.cmc.sanselan.ImageReadException;
+import org.cmc.sanselan.formats.png.chunks.PNGChunkPLTE;
+import org.cmc.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/cmc/sanselan/formats/png/ScanExpediterInterlaced.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: incubator/sanselan/trunk/src/main/java/org/cmc/sanselan/formats/png/ScanExpediterSimple.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/cmc/sanselan/formats/png/ScanExpediterSimple.java?rev=596008&view=auto
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/cmc/sanselan/formats/png/ScanExpediterSimple.java (added)
+++ incubator/sanselan/trunk/src/main/java/org/cmc/sanselan/formats/png/ScanExpediterSimple.java Sat Nov 17 13:58:22 2007
@@ -0,0 +1,85 @@
+/*
+ * 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.cmc.sanselan.formats.png;
+
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.cmc.sanselan.ImageReadException;
+import org.cmc.sanselan.formats.png.chunks.PNGChunkPLTE;
+import org.cmc.sanselan.formats.transparencyfilters.TransparencyFilter;
+import org.cmc.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
+ {
+ 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++)
+ {
+ 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/cmc/sanselan/formats/png/ScanExpediterSimple.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: incubator/sanselan/trunk/src/main/java/org/cmc/sanselan/formats/png/chunks/PNGChunk.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/cmc/sanselan/formats/png/chunks/PNGChunk.java?rev=596008&view=auto
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/cmc/sanselan/formats/png/chunks/PNGChunk.java (added)
+++ incubator/sanselan/trunk/src/main/java/org/cmc/sanselan/formats/png/chunks/PNGChunk.java Sat Nov 17 13:58:22 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.cmc.sanselan.formats.png.chunks;
+
+import java.io.ByteArrayInputStream;
+
+import org.cmc.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/cmc/sanselan/formats/png/chunks/PNGChunk.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: incubator/sanselan/trunk/src/main/java/org/cmc/sanselan/formats/png/chunks/PNGChunkIDAT.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/cmc/sanselan/formats/png/chunks/PNGChunkIDAT.java?rev=596008&view=auto
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/cmc/sanselan/formats/png/chunks/PNGChunkIDAT.java (added)
+++ incubator/sanselan/trunk/src/main/java/org/cmc/sanselan/formats/png/chunks/PNGChunkIDAT.java Sat Nov 17 13:58:22 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.cmc.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/cmc/sanselan/formats/png/chunks/PNGChunkIDAT.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: incubator/sanselan/trunk/src/main/java/org/cmc/sanselan/formats/png/chunks/PNGChunkIHDR.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/cmc/sanselan/formats/png/chunks/PNGChunkIHDR.java?rev=596008&view=auto
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/cmc/sanselan/formats/png/chunks/PNGChunkIHDR.java (added)
+++ incubator/sanselan/trunk/src/main/java/org/cmc/sanselan/formats/png/chunks/PNGChunkIHDR.java Sat Nov 17 13:58:22 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.cmc.sanselan.formats.png.chunks;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+
+import org.cmc.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/cmc/sanselan/formats/png/chunks/PNGChunkIHDR.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: incubator/sanselan/trunk/src/main/java/org/cmc/sanselan/formats/png/chunks/PNGChunkPLTE.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/cmc/sanselan/formats/png/chunks/PNGChunkPLTE.java?rev=596008&view=auto
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/cmc/sanselan/formats/png/chunks/PNGChunkPLTE.java (added)
+++ incubator/sanselan/trunk/src/main/java/org/cmc/sanselan/formats/png/chunks/PNGChunkPLTE.java Sat Nov 17 13:58:22 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.cmc.sanselan.formats.png.chunks;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+
+import org.cmc.sanselan.ImageReadException;
+import org.cmc.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/cmc/sanselan/formats/png/chunks/PNGChunkPLTE.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: incubator/sanselan/trunk/src/main/java/org/cmc/sanselan/formats/png/chunks/PNGChunkgAMA.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/cmc/sanselan/formats/png/chunks/PNGChunkgAMA.java?rev=596008&view=auto
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/cmc/sanselan/formats/png/chunks/PNGChunkgAMA.java (added)
+++ incubator/sanselan/trunk/src/main/java/org/cmc/sanselan/formats/png/chunks/PNGChunkgAMA.java Sat Nov 17 13:58:22 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.cmc.sanselan.formats.png.chunks;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+
+import org.cmc.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/cmc/sanselan/formats/png/chunks/PNGChunkgAMA.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: incubator/sanselan/trunk/src/main/java/org/cmc/sanselan/formats/png/chunks/PNGChunkiCCP.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/cmc/sanselan/formats/png/chunks/PNGChunkiCCP.java?rev=596008&view=auto
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/cmc/sanselan/formats/png/chunks/PNGChunkiCCP.java (added)
+++ incubator/sanselan/trunk/src/main/java/org/cmc/sanselan/formats/png/chunks/PNGChunkiCCP.java Sat Nov 17 13:58:22 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.cmc.sanselan.formats.png.chunks;
+
+import java.io.IOException;
+
+import org.cmc.sanselan.ImageReadException;
+import org.cmc.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/cmc/sanselan/formats/png/chunks/PNGChunkiCCP.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: incubator/sanselan/trunk/src/main/java/org/cmc/sanselan/formats/png/chunks/PNGChunkpHYs.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/cmc/sanselan/formats/png/chunks/PNGChunkpHYs.java?rev=596008&view=auto
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/cmc/sanselan/formats/png/chunks/PNGChunkpHYs.java (added)
+++ incubator/sanselan/trunk/src/main/java/org/cmc/sanselan/formats/png/chunks/PNGChunkpHYs.java Sat Nov 17 13:58:22 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.cmc.sanselan.formats.png.chunks;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+
+import org.cmc.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/cmc/sanselan/formats/png/chunks/PNGChunkpHYs.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: incubator/sanselan/trunk/src/main/java/org/cmc/sanselan/formats/png/chunks/PNGChunktEXt.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/cmc/sanselan/formats/png/chunks/PNGChunktEXt.java?rev=596008&view=auto
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/cmc/sanselan/formats/png/chunks/PNGChunktEXt.java (added)
+++ incubator/sanselan/trunk/src/main/java/org/cmc/sanselan/formats/png/chunks/PNGChunktEXt.java Sat Nov 17 13:58:22 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.cmc.sanselan.formats.png.chunks;
+
+import java.io.IOException;
+
+import org.cmc.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/cmc/sanselan/formats/png/chunks/PNGChunktEXt.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: incubator/sanselan/trunk/src/main/java/org/cmc/sanselan/formats/png/chunks/PNGChunkzTXt.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/cmc/sanselan/formats/png/chunks/PNGChunkzTXt.java?rev=596008&view=auto
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/cmc/sanselan/formats/png/chunks/PNGChunkzTXt.java (added)
+++ incubator/sanselan/trunk/src/main/java/org/cmc/sanselan/formats/png/chunks/PNGChunkzTXt.java Sat Nov 17 13:58:22 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.cmc.sanselan.formats.png.chunks;
+
+import java.io.IOException;
+
+import org.cmc.sanselan.ImageReadException;
+import org.cmc.sanselan.common.ZLibInflater;
+
+public class PNGChunkzTXt extends PNGTextChunk
+{
+
+ // private final PngImageParser parser;
+ public final String Keyword, Text;
+
+ public PNGChunkzTXt(
+ // 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 Keyword_bytes[] = new byte[index];
+ System.arraycopy(bytes, 0, Keyword_bytes, 0, index);
+ Keyword = new String(Keyword_bytes);
+
+ int CompressionMethod = bytes[index + 1];
+
+ int CompressedTextLength = bytes.length - (index + 1 + 1);
+ byte CompressedText[] = new byte[CompressedTextLength];
+ System.arraycopy(bytes, index + 1 + 1, CompressedText, 0,
+ CompressedTextLength);
+
+ if (getDebug())
+ {
+ System.out.println("Keyword: " + Keyword);
+ }
+
+ Text = new String(new ZLibInflater().zlibInflate(CompressedText));
+
+ if (getDebug())
+ {
+ 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/cmc/sanselan/formats/png/chunks/PNGChunkzTXt.java
------------------------------------------------------------------------------
svn:eol-style = native