You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by da...@apache.org on 2011/04/02 10:22:31 UTC
svn commit: r1087970 - in /commons/proper/sanselan/trunk/src:
main/java/org/apache/sanselan/ main/java/org/apache/sanselan/formats/pcx/
test/data/images/pcx/ test/data/images/pcx/1/
test/java/org/apache/sanselan/common/byteSources/ test/java/org/apache...
Author: damjan
Date: Sat Apr 2 08:22:30 2011
New Revision: 1087970
URL: http://svn.apache.org/viewvc?rev=1087970&view=rev
Log:
Added support for reading and writing PCX images, and thorough tests.
Added:
commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/pcx/
commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/pcx/PcxConstants.java (with props)
commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/pcx/PcxImageParser.java (with props)
commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/pcx/PcxWriter.java (with props)
commons/proper/sanselan/trunk/src/test/data/images/pcx/
commons/proper/sanselan/trunk/src/test/data/images/pcx/1/
commons/proper/sanselan/trunk/src/test/data/images/pcx/1/1plane1bppCompressed.pcx (with props)
commons/proper/sanselan/trunk/src/test/data/images/pcx/1/1plane1bppUncompressed.pcx (with props)
commons/proper/sanselan/trunk/src/test/data/images/pcx/1/1plane24bppCompressed.pcx (with props)
commons/proper/sanselan/trunk/src/test/data/images/pcx/1/1plane2bppCompressed.pcx (with props)
commons/proper/sanselan/trunk/src/test/data/images/pcx/1/1plane32bppCompressed.pcx (with props)
commons/proper/sanselan/trunk/src/test/data/images/pcx/1/1plane4bppCompressed.pcx (with props)
commons/proper/sanselan/trunk/src/test/data/images/pcx/1/1plane8bppCompressed.pcx (with props)
commons/proper/sanselan/trunk/src/test/data/images/pcx/1/1plane8bppUncompressed.pcx (with props)
commons/proper/sanselan/trunk/src/test/data/images/pcx/1/2plane1bppCompressed.pcx (with props)
commons/proper/sanselan/trunk/src/test/data/images/pcx/1/3plane1bppCompressed.pcx (with props)
commons/proper/sanselan/trunk/src/test/data/images/pcx/1/3plane8bppCompressed.pcx (with props)
commons/proper/sanselan/trunk/src/test/data/images/pcx/1/3plane8bppUncompressed.pcx (with props)
commons/proper/sanselan/trunk/src/test/data/images/pcx/1/4plane1bppCompressed.pcx (with props)
commons/proper/sanselan/trunk/src/test/data/images/pcx/1/Oregon Scientific DS6639 - DSC_0307 - small.pcx (with props)
commons/proper/sanselan/trunk/src/test/data/images/pcx/1/info.txt (with props)
commons/proper/sanselan/trunk/src/test/java/org/apache/sanselan/formats/pcx/
commons/proper/sanselan/trunk/src/test/java/org/apache/sanselan/formats/pcx/PcxBaseTest.java (with props)
commons/proper/sanselan/trunk/src/test/java/org/apache/sanselan/formats/pcx/PcxReadTest.java (with props)
Modified:
commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/ImageFormat.java
commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/ImageParser.java
commons/proper/sanselan/trunk/src/test/java/org/apache/sanselan/common/byteSources/ByteSourceImageTest.java
commons/proper/sanselan/trunk/src/test/java/org/apache/sanselan/roundtrip/RoundtripTest.java
Modified: commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/ImageFormat.java
URL: http://svn.apache.org/viewvc/commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/ImageFormat.java?rev=1087970&r1=1087969&r2=1087970&view=diff
==============================================================================
--- commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/ImageFormat.java (original)
+++ commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/ImageFormat.java Sat Apr 2 08:22:30 2011
@@ -74,6 +74,7 @@ public class ImageFormat
public static final ImageFormat IMAGE_FORMAT_JBIG2 = new ImageFormat("JBig2");
public static final ImageFormat IMAGE_FORMAT_ICNS = new ImageFormat("ICNS");
public static final ImageFormat IMAGE_FORMAT_WBMP = new ImageFormat("WBMP");
+ public static final ImageFormat IMAGE_FORMAT_PCX = new ImageFormat("PCX");
public static final ImageFormat[] getAllFormats()
{
@@ -83,6 +84,7 @@ public class ImageFormat
IMAGE_FORMAT_PSD, IMAGE_FORMAT_PBM, IMAGE_FORMAT_PGM,
IMAGE_FORMAT_PPM, IMAGE_FORMAT_PNM, IMAGE_FORMAT_TGA,
IMAGE_FORMAT_JBIG2, IMAGE_FORMAT_ICNS, IMAGE_FORMAT_WBMP,
+ IMAGE_FORMAT_PCX,
};
return result;
Modified: commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/ImageParser.java
URL: http://svn.apache.org/viewvc/commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/ImageParser.java?rev=1087970&r1=1087969&r2=1087970&view=diff
==============================================================================
--- commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/ImageParser.java (original)
+++ commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/ImageParser.java Sat Apr 2 08:22:30 2011
@@ -38,6 +38,7 @@ import org.apache.sanselan.formats.gif.G
import org.apache.sanselan.formats.icns.IcnsImageParser;
import org.apache.sanselan.formats.ico.IcoImageParser;
import org.apache.sanselan.formats.jpeg.JpegImageParser;
+import org.apache.sanselan.formats.pcx.PcxImageParser;
import org.apache.sanselan.formats.png.PngImageParser;
import org.apache.sanselan.formats.pnm.PNMImageParser;
import org.apache.sanselan.formats.psd.PsdImageParser;
@@ -56,6 +57,7 @@ public abstract class ImageParser extend
new GifImageParser(), new PsdImageParser(),
new PNMImageParser(), new IcoImageParser(),
new IcnsImageParser(), new WbmpImageParser(),
+ new PcxImageParser(),
// new JBig2ImageParser(),
// new TgaImageParser(),
};
Added: commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/pcx/PcxConstants.java
URL: http://svn.apache.org/viewvc/commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/pcx/PcxConstants.java?rev=1087970&view=auto
==============================================================================
--- commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/pcx/PcxConstants.java (added)
+++ commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/pcx/PcxConstants.java Sat Apr 2 08:22:30 2011
@@ -0,0 +1,26 @@
+/*
+ * Licensed 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.
+ * under the License.
+ */
+package org.apache.sanselan.formats.pcx;
+
+import org.apache.sanselan.SanselanConstants;
+
+public interface PcxConstants extends SanselanConstants
+{
+ public static final String PARAM_KEY_PCX_COMPRESSION = "PCX_COMPRESSION";
+ public static final int PCX_COMPRESSION_UNCOMPRESSED = 0;
+ public static final int PCX_COMPRESSION_RLE = 1;
+
+ public static final String PARAM_KEY_PCX_BIT_DEPTH = "PCX_BIT_DEPTH";
+}
Propchange: commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/pcx/PcxConstants.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/pcx/PcxImageParser.java
URL: http://svn.apache.org/viewvc/commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/pcx/PcxImageParser.java?rev=1087970&view=auto
==============================================================================
--- commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/pcx/PcxImageParser.java (added)
+++ commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/pcx/PcxImageParser.java Sat Apr 2 08:22:30 2011
@@ -0,0 +1,589 @@
+/*
+ * 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.pcx;
+
+import java.awt.Dimension;
+import java.awt.Transparency;
+import java.awt.color.ColorSpace;
+import java.awt.image.BufferedImage;
+import java.awt.image.ColorModel;
+import java.awt.image.ComponentColorModel;
+import java.awt.image.DataBuffer;
+import java.awt.image.DataBufferByte;
+import java.awt.image.DataBufferInt;
+import java.awt.image.DirectColorModel;
+import java.awt.image.IndexColorModel;
+import java.awt.image.WritableRaster;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+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.byteSources.ByteSource;
+
+public class PcxImageParser extends ImageParser implements PcxConstants
+{
+ // ZSoft's official spec is at http://www.qzx.com/pc-gpe/pcx.txt
+ // (among other places) but it's pretty thin. The fileformat.info document
+ // at http://www.fileformat.info/format/pcx/egff.htm is a little better
+ // but their gray sample image seems corrupt. PCX files themselves are
+ // the ultimate test but pretty hard to find nowdays, so the best
+ // test is against other image viewers (Irfanview is pretty good).
+ //
+ // Open source projects are generally poor at parsing PCX,
+ // SDL_Image/gdk-pixbuf/Eye of Gnome/GIMP/F-Spot all only do some formats,
+ // don't support uncompressed PCX, and/or don't handle black and white
+ // images properly.
+
+ public PcxImageParser()
+ {
+ super.setByteOrder(BYTE_ORDER_LSB);
+ }
+
+ public String getName()
+ {
+ return "Pcx-Custom";
+ }
+
+ public String getDefaultExtension()
+ {
+ return DEFAULT_EXTENSION;
+ }
+ private static final String DEFAULT_EXTENSION = ".pcx";
+ private static final String ACCEPTED_EXTENSIONS[] =
+ {
+ ".pcx", ".pcc",
+ };
+
+ protected String[] getAcceptedExtensions()
+ {
+ return ACCEPTED_EXTENSIONS;
+ }
+
+ protected ImageFormat[] getAcceptedTypes()
+ {
+ return new ImageFormat[]
+ {
+ ImageFormat.IMAGE_FORMAT_PCX, //
+ };
+ }
+
+ public boolean embedICCProfile(File src, File dst, byte profile[])
+ {
+ return false;
+ }
+
+ public IImageMetadata getMetadata(ByteSource byteSource, Map params)
+ throws ImageReadException, IOException
+ {
+ return null;
+ }
+
+ public ImageInfo getImageInfo(ByteSource byteSource, Map params)
+ throws ImageReadException, IOException
+ {
+ PcxHeader pcxHeader = readPcxHeader(byteSource);
+ Dimension size = getImageSize(byteSource, params);
+ int metricHDpi = (int) (pcxHeader.hDpi * 1000.0 / 2.54);
+ int metricVDpi = (int) (pcxHeader.vDpi * 1000.0 / 2.54);
+ return new ImageInfo("PCX", pcxHeader.nPlanes * pcxHeader.bitsPerPixel, new ArrayList(),
+ ImageFormat.IMAGE_FORMAT_PCX, "ZSoft PCX Image", size.height, "image/x-pcx", 1,
+ metricVDpi, pcxHeader.vDpi / metricVDpi, metricHDpi, pcxHeader.hDpi / metricHDpi,
+ size.width, false, false,
+ !(pcxHeader.nPlanes == 3 && pcxHeader.bitsPerPixel == 8),
+ ImageInfo.COLOR_TYPE_RGB,
+ pcxHeader.encoding == PcxHeader.ENCODING_RLE ?
+ ImageInfo.COMPRESSION_ALGORITHM_RLE : ImageInfo.COMPRESSION_ALGORITHM_NONE);
+ }
+
+ public Dimension getImageSize(ByteSource byteSource,
+ Map params)
+ throws ImageReadException, IOException
+ {
+ PcxHeader pcxHeader = readPcxHeader(byteSource);
+ int xSize = pcxHeader.xMax - pcxHeader.xMin + 1;
+ if (xSize < 0)
+ throw new ImageReadException("Image width is negative");
+ int ySize = pcxHeader.yMax - pcxHeader.yMin + 1;
+ if (ySize < 0)
+ throw new ImageReadException("Image height is negative");
+ return new Dimension(xSize, ySize);
+ }
+
+ public byte[] getICCProfileBytes(ByteSource byteSource,
+ Map params)
+ throws ImageReadException, IOException
+ {
+ return null;
+ }
+
+ static class PcxHeader
+ {
+
+ public static final int ENCODING_UNCOMPRESSED = 0;
+ public static final int ENCODING_RLE = 1;
+ public static final int PALETTE_INFO_COLOR = 1;
+ public static final int PALETTE_INFO_GRAYSCALE = 2;
+ public final int manufacturer; // Always 10 = ZSoft .pcx
+ public final int version; // 0 = PC Paintbrush 2.5
+ // 2 = PC Paintbrush 2.8 with palette
+ // 3 = PC Paintbrush 2.8 w/o palette
+ // 4 = PC Paintbrush for Windows
+ // 5 = PC Paintbrush >= 3.0
+ public final int encoding; // 0 = very old uncompressed format, 1 = .pcx run length encoding
+ public final int bitsPerPixel; // Bits ***PER PLANE*** for each pixel
+ public final int xMin; // window
+ public final int yMin;
+ public final int xMax;
+ public final int yMax;
+ public final int hDpi; // horizontal dpi
+ public final int vDpi; // vertical dpi
+ public final int[] colormap; // palette for <= 16 colors
+ public final int reserved; // Always 0
+ public final int nPlanes; // Number of color planes
+ public final int bytesPerLine; // Number of bytes per scanline plane, must be an even number.
+ public final int paletteInfo; // 1 = Color/BW, 2 = Grayscale, ignored in Paintbrush IV/IV+
+ public final int hScreenSize; // horizontal screen size, in pixels. PaintBrush >= IV only.
+ public final int vScreenSize; // vertical screen size, in pixels. PaintBrush >= IV only.
+
+ public PcxHeader(final int manufacturer, final int version,
+ final int encoding, final int bitsPerPixel,
+ final int xMin, final int yMin,
+ final int xMax, final int yMax,
+ final int hDpi, final int vDpi,
+ final int[] colormap,
+ final int reserved, final int nPlanes,
+ final int bytesPerLine, final int paletteInfo,
+ final int hScreenSize, final int vScreenSize)
+ {
+ this.manufacturer = manufacturer;
+ this.version = version;
+ this.encoding = encoding;
+ this.bitsPerPixel = bitsPerPixel;
+ this.xMin = xMin;
+ this.yMin = yMin;
+ this.xMax = xMax;
+ this.yMax = yMax;
+ this.hDpi = hDpi;
+ this.vDpi = vDpi;
+ this.colormap = colormap;
+ this.reserved = reserved;
+ this.nPlanes = nPlanes;
+ this.bytesPerLine = bytesPerLine;
+ this.paletteInfo = paletteInfo;
+ this.hScreenSize = hScreenSize;
+ this.vScreenSize = vScreenSize;
+ }
+
+ public void dump(PrintWriter pw)
+ {
+ pw.println("PcxHeader");
+ pw.println("Manufacturer: " + manufacturer);
+ pw.println("Version: " + version);
+ pw.println("Encoding: " + encoding);
+ pw.println("BitsPerPixel: " + bitsPerPixel);
+ pw.println("xMin: " + xMin);
+ pw.println("yMin: " + yMin);
+ pw.println("xMax: " + xMax);
+ pw.println("yMax: " + yMax);
+ pw.println("hDpi: " + hDpi);
+ pw.println("vDpi: " + vDpi);
+ pw.print("ColorMap: ");
+ for (int i = 0; i < colormap.length; i++)
+ {
+ if (i > 0)
+ pw.print(",");
+ pw.print("("
+ + (0xff & (colormap[i] >> 16)) + ","
+ + (0xff & (colormap[i] >> 8)) + ","
+ + (0xff & colormap[i]) + ")");
+ }
+ pw.println();
+ pw.println("Reserved: " + reserved);
+ pw.println("nPlanes: " + nPlanes);
+ pw.println("BytesPerLine: " + bytesPerLine);
+ pw.println("PaletteInfo: " + paletteInfo);
+ pw.println("hScreenSize: " + hScreenSize);
+ pw.println("vScreenSize: " + vScreenSize);
+ pw.println();
+ }
+ }
+
+ private PcxHeader readPcxHeader(ByteSource byteSource)
+ throws ImageReadException, IOException
+ {
+ InputStream is = null;
+ try
+ {
+ is = byteSource.getInputStream();
+ return readPcxHeader(is, false);
+ }
+ finally
+ {
+ try
+ {
+ if (is != null)
+ is.close();
+ }
+ catch (IOException ignored)
+ {
+ }
+ }
+ }
+
+ private PcxHeader readPcxHeader(InputStream is, boolean isStrict)
+ throws ImageReadException, IOException
+ {
+ byte[] pcxHeaderBytes = readByteArray("PcxHeader", 128, is, "Not a Valid PCX File");
+ int manufacturer = 0xff & pcxHeaderBytes[0];
+ int version = 0xff & pcxHeaderBytes[1];
+ int encoding = 0xff & pcxHeaderBytes[2];
+ int bitsPerPixel = 0xff & pcxHeaderBytes[3];
+ int xMin = convertByteArrayToShort("xMin", 4, pcxHeaderBytes, BYTE_ORDER_LSB);
+ int yMin = convertByteArrayToShort("yMin", 6, pcxHeaderBytes, BYTE_ORDER_LSB);
+ int xMax = convertByteArrayToShort("xMax", 8, pcxHeaderBytes, BYTE_ORDER_LSB);
+ int yMax = convertByteArrayToShort("yMax", 10, pcxHeaderBytes, BYTE_ORDER_LSB);
+ int hDpi = convertByteArrayToShort("hDpi", 12, pcxHeaderBytes, BYTE_ORDER_LSB);
+ int vDpi = convertByteArrayToShort("vDpi", 14, pcxHeaderBytes, BYTE_ORDER_LSB);
+ int[] colormap = new int[16];
+ for (int i = 0; i < 16; i++)
+ {
+ colormap[i] = 0xff000000
+ | ((0xff & pcxHeaderBytes[16 + 3 * i]) << 16)
+ | ((0xff & pcxHeaderBytes[16 + 3 * i + 1]) << 8)
+ | (0xff & pcxHeaderBytes[16 + 3 * i + 2]);
+ }
+ int reserved = 0xff & pcxHeaderBytes[64];
+ int nPlanes = 0xff & pcxHeaderBytes[65];
+ int bytesPerLine = convertByteArrayToShort("BytesPerLine", 66, pcxHeaderBytes, BYTE_ORDER_LSB);
+ int paletteInfo = convertByteArrayToShort("PaletteInfo", 68, pcxHeaderBytes, BYTE_ORDER_LSB);
+ int hScreenSize = convertByteArrayToShort("hScreenSize", 70, pcxHeaderBytes, BYTE_ORDER_LSB);
+ int vScreenSize = convertByteArrayToShort("vScreenSize", 72, pcxHeaderBytes, BYTE_ORDER_LSB);
+
+ if (manufacturer != 10)
+ throw new ImageReadException("Not a Valid PCX File: manufacturer is " + manufacturer);
+ if (isStrict)
+ {
+ // Note that reserved is sometimes set to a non-zero value
+ // by Paintbrush itself, so it shouldn't be enforced.
+ if (bytesPerLine % 2 != 0)
+ throw new ImageReadException("Not a Valid PCX File: bytesPerLine is odd");
+ }
+
+ return new PcxHeader(manufacturer, version, encoding, bitsPerPixel,
+ xMin, yMin, xMax, yMax, hDpi, vDpi, colormap, reserved,
+ nPlanes, bytesPerLine, paletteInfo, hScreenSize, vScreenSize);
+ }
+
+ public boolean dumpImageFile(PrintWriter pw, ByteSource byteSource)
+ throws ImageReadException, IOException
+ {
+ readPcxHeader(byteSource).dump(pw);
+ return true;
+ }
+
+ private void readScanLine(PcxHeader pcxHeader, InputStream is, byte[] samples)
+ throws IOException, ImageReadException
+ {
+ if (pcxHeader.encoding == PcxHeader.ENCODING_UNCOMPRESSED)
+ {
+ int r;
+ for (int bytesRead = 0; bytesRead < samples.length; bytesRead += r)
+ {
+ r = is.read(samples, bytesRead, samples.length - bytesRead);
+ if (r < 0)
+ throw new ImageReadException("Premature end of file reading image data");
+ }
+ }
+ else
+ {
+ if (pcxHeader.encoding == PcxHeader.ENCODING_RLE)
+ {
+ for (int bytesRead = 0; bytesRead < samples.length;)
+ {
+ byte b = readByte("Pixel", is, "Error reading image data");
+ int count;
+ byte sample;
+ if ((b & 0xc0) == 0xc0)
+ {
+ count = b & 0x3f;
+ sample = readByte("Pixel", is, "Error reading image data");
+ }
+ else
+ {
+ count = 1;
+ sample = b;
+ }
+ for (int i = 0; i < count && bytesRead + i < samples.length; i++)
+ samples[bytesRead + i] = sample;
+ bytesRead += count;
+ }
+ }
+ else
+ throw new ImageReadException("Invalid PCX encoding " + pcxHeader.encoding);
+ }
+ }
+
+ private int[] read256ColorPalette(InputStream stream)
+ throws IOException
+ {
+ byte[] paletteBytes = readByteArray("Palette", 769, stream, "Error reading palette");
+ if (paletteBytes[0] != 12)
+ return null;
+ int[] palette = new int[256];
+ for (int i = 0; i < palette.length; i++)
+ {
+ palette[i] = ((0xff & paletteBytes[1 + 3*i]) << 16)
+ | ((0xff & paletteBytes[1 + 3*i + 1]) << 8)
+ | (0xff & paletteBytes[1 + 3*i + 2]);
+ }
+ return palette;
+ }
+
+ private int[] read256ColorPaletteFromEndOfFile(ByteSource byteSource)
+ throws IOException, ImageReadException
+ {
+ InputStream stream = null;
+ try
+ {
+ stream = byteSource.getInputStream();
+ long toSkip = byteSource.getLength() - 769;
+ while (toSkip > 0)
+ toSkip -= stream.skip(toSkip);
+ return read256ColorPalette(stream);
+ }
+ finally
+ {
+ try
+ {
+ if (stream != null)
+ stream.close();
+ }
+ catch (IOException closeException)
+ {
+ }
+ }
+ }
+
+ private BufferedImage readImage(PcxHeader pcxHeader, InputStream is, ByteSource byteSource)
+ throws ImageReadException, IOException
+ {
+ int xSize = pcxHeader.xMax - pcxHeader.xMin + 1;
+ if (xSize < 0)
+ throw new ImageReadException("Image width is negative");
+ int ySize = pcxHeader.yMax - pcxHeader.yMin + 1;
+ if (ySize < 0)
+ throw new ImageReadException("Image height is negative");
+
+ int scanlineLength = pcxHeader.bytesPerLine * pcxHeader.nPlanes;
+ byte[] scanline = new byte[scanlineLength];
+ if ((pcxHeader.bitsPerPixel == 1 || pcxHeader.bitsPerPixel == 2
+ || pcxHeader.bitsPerPixel == 4 || pcxHeader.bitsPerPixel == 8) &&
+ pcxHeader.nPlanes == 1)
+ {
+ int bytesPerImageRow = (xSize * pcxHeader.bitsPerPixel + 7) / 8;
+ byte[] image = new byte[ySize * bytesPerImageRow];
+ for (int y = 0; y < ySize; y++)
+ {
+ readScanLine(pcxHeader, is, scanline);
+ System.arraycopy(scanline, 0, image, y*bytesPerImageRow, bytesPerImageRow);
+ }
+ DataBufferByte dataBuffer = new DataBufferByte(image, image.length);
+ int[] palette;
+ if (pcxHeader.bitsPerPixel == 1)
+ palette = new int[] { 0x000000, 0xffffff };
+ else if (pcxHeader.bitsPerPixel == 8)
+ {
+ // Normally the palette is read 769 bytes from the end of the file.
+ // However DCX files have multiple PCX images in one file, so
+ // there could be extra data before the end! So try look for the palette
+ // immediately after the image data first.
+ palette = read256ColorPalette(is);
+ if (palette == null)
+ palette = read256ColorPaletteFromEndOfFile(byteSource);
+ if (palette == null)
+ throw new ImageReadException(
+ "No 256 color palette found in image that needs it");
+ }
+ else
+ palette = pcxHeader.colormap;
+ WritableRaster raster;
+ if (pcxHeader.bitsPerPixel == 8)
+ {
+ raster = WritableRaster.createInterleavedRaster(dataBuffer,
+ xSize, ySize, bytesPerImageRow, 1, new int[]{0}, null);
+ }
+ else
+ {
+ raster = WritableRaster.createPackedRaster(dataBuffer,
+ xSize, ySize, pcxHeader.bitsPerPixel, null);
+ }
+ IndexColorModel colorModel = new IndexColorModel(pcxHeader.bitsPerPixel,
+ 1 << pcxHeader.bitsPerPixel, palette, 0, false, -1, DataBuffer.TYPE_BYTE);
+ return new BufferedImage(colorModel, raster,
+ colorModel.isAlphaPremultiplied(), new Properties());
+ }
+ else if (pcxHeader.bitsPerPixel == 1 && 2 <= pcxHeader.nPlanes
+ && pcxHeader.nPlanes <= 4)
+ {
+ IndexColorModel colorModel = new IndexColorModel(pcxHeader.nPlanes,
+ 1 << pcxHeader.nPlanes, pcxHeader.colormap, 0, false, -1, DataBuffer.TYPE_BYTE);
+ BufferedImage image = new BufferedImage(xSize, ySize, BufferedImage.TYPE_BYTE_BINARY, colorModel);
+ byte[] unpacked = new byte[xSize];
+ for (int y = 0; y < ySize; y++)
+ {
+ readScanLine(pcxHeader, is, scanline);
+ int nextByte = 0;
+ Arrays.fill(unpacked, (byte) 0);
+ for (int plane = 0; plane < pcxHeader.nPlanes; plane++)
+ {
+ for (int i = 0; i < pcxHeader.bytesPerLine; i++)
+ {
+ int b = 0xff & scanline[nextByte++];
+ for (int j = 0; j < 8 && 8*i + j < unpacked.length; j++)
+ unpacked[8*i + j] |= (byte) (((b >> (7 - j)) & 0x1) << plane);
+ }
+ }
+ image.getRaster().setDataElements(0, y, xSize, 1, unpacked);
+ }
+ return image;
+ }
+ else if (pcxHeader.bitsPerPixel == 8 && pcxHeader.nPlanes == 3)
+ {
+ byte[][] image = new byte[3][];
+ image[0] = new byte[xSize*ySize];
+ image[1] = new byte[xSize*ySize];
+ image[2] = new byte[xSize*ySize];
+ for (int y = 0; y < ySize; y++)
+ {
+ readScanLine(pcxHeader, is, scanline);
+ System.arraycopy(scanline, 0, image[0], y*xSize, xSize);
+ System.arraycopy(scanline, pcxHeader.bytesPerLine,
+ image[1], y*xSize, xSize);
+ System.arraycopy(scanline, 2*pcxHeader.bytesPerLine,
+ image[2], y*xSize, xSize);
+ }
+ DataBufferByte dataBuffer = new DataBufferByte(image, image[0].length);
+ WritableRaster raster = WritableRaster.createBandedRaster(dataBuffer,
+ xSize, ySize, xSize, new int[]{0,1,2},
+ new int[]{0,0,0}, null);
+ ColorModel colorModel = new ComponentColorModel(
+ ColorSpace.getInstance(ColorSpace.CS_sRGB), false, false,
+ Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
+ return new BufferedImage(colorModel, raster,
+ colorModel.isAlphaPremultiplied(), new Properties());
+ }
+ else if ((pcxHeader.bitsPerPixel == 24 && pcxHeader.nPlanes == 1) ||
+ (pcxHeader.bitsPerPixel == 32 && pcxHeader.nPlanes == 1))
+ {
+ int rowLength = 3 * xSize;
+ byte[] image = new byte[rowLength * ySize];
+ for (int y = 0; y < ySize; y++)
+ {
+ readScanLine(pcxHeader, is, scanline);
+ if (pcxHeader.bitsPerPixel == 24)
+ System.arraycopy(scanline, 0, image, y*rowLength, rowLength);
+ else
+ {
+ for (int x = 0; x < xSize; x++)
+ {
+ image[y*rowLength + 3*x] = scanline[4*x];
+ image[y*rowLength + 3*x + 1] = scanline[4*x + 1];
+ image[y*rowLength + 3*x + 2] = scanline[4*x + 2];
+ }
+ }
+ }
+ DataBufferByte dataBuffer = new DataBufferByte(image, image.length);
+ WritableRaster raster = WritableRaster.createInterleavedRaster(
+ dataBuffer, xSize, ySize, rowLength, 3,
+ new int[]{2,1,0}, null);
+ ColorModel colorModel = new ComponentColorModel(
+ ColorSpace.getInstance(ColorSpace.CS_sRGB), false, false,
+ Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
+ return new BufferedImage(colorModel, raster,
+ colorModel.isAlphaPremultiplied(), new Properties());
+ }
+ else
+ {
+ throw new ImageReadException("Invalid/unsupported image with bitsPerPixel "
+ + pcxHeader.bitsPerPixel + " and planes " + pcxHeader.nPlanes);
+ }
+ }
+
+ public final BufferedImage getBufferedImage(ByteSource byteSource,
+ Map params) throws ImageReadException, IOException
+ {
+ params = (params == null) ? new HashMap() : new HashMap(params);
+ boolean isStrict = false;
+ Object strictness = params.get(PARAM_KEY_STRICT);
+ if (strictness != null)
+ isStrict = ((Boolean) strictness).booleanValue();
+
+ InputStream is = null;
+ try
+ {
+ is = byteSource.getInputStream();
+ PcxHeader pcxHeader = readPcxHeader(is, isStrict);
+ return readImage(pcxHeader, is, byteSource);
+ }
+ finally
+ {
+ try
+ {
+ if (is != null)
+ is.close();
+ }
+ catch (IOException ignored)
+ {
+ }
+ }
+ }
+
+ public void writeImage(BufferedImage src, OutputStream os, Map params)
+ throws ImageWriteException, IOException
+ {
+ new PcxWriter(params).writeImage(src, os);
+ }
+
+ /**
+ * Extracts embedded XML metadata as XML string.
+ * <p>
+ *
+ * @param byteSource
+ * File containing image data.
+ * @param params
+ * Map of optional parameters, defined in SanselanConstants.
+ * @return Xmp Xml as String, if present. Otherwise, returns null.
+ */
+ public String getXmpXml(ByteSource byteSource, Map params)
+ throws ImageReadException, IOException
+ {
+ return null;
+ }
+}
Propchange: commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/pcx/PcxImageParser.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/pcx/PcxWriter.java
URL: http://svn.apache.org/viewvc/commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/pcx/PcxWriter.java?rev=1087970&view=auto
==============================================================================
--- commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/pcx/PcxWriter.java (added)
+++ commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/pcx/PcxWriter.java Sat Apr 2 08:22:30 2011
@@ -0,0 +1,401 @@
+/*
+ * Licensed 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.
+ * under the License.
+ */
+
+package org.apache.sanselan.formats.pcx;
+
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.sanselan.ImageWriteException;
+import org.apache.sanselan.common.BinaryOutputStream;
+import org.apache.sanselan.palette.PaletteFactory;
+import org.apache.sanselan.palette.SimplePalette;
+
+public class PcxWriter implements PcxConstants
+{
+ private int encoding;
+ private int bitDepth = -1;
+
+ public PcxWriter(Map params) throws ImageWriteException
+ {
+ // make copy of params; we'll clear keys as we consume them.
+ params = (params == null) ? new HashMap() : new HashMap(params);
+
+ // clear format key.
+ if (params.containsKey(PARAM_KEY_FORMAT))
+ params.remove(PARAM_KEY_FORMAT);
+
+ // uncompressed PCX files are not even documented in ZSoft's spec,
+ // let alone supported by most image viewers
+ encoding = PcxImageParser.PcxHeader.ENCODING_RLE;
+ if (params.containsKey(PARAM_KEY_PCX_COMPRESSION))
+ {
+ Object value = params.remove(PARAM_KEY_PCX_COMPRESSION);
+ if (value != null)
+ {
+ if (!(value instanceof Number))
+ throw new ImageWriteException(
+ "Invalid compression parameter: " + value);
+ int compression = ((Number) value).intValue();
+ if (compression == PCX_COMPRESSION_UNCOMPRESSED)
+ encoding = PcxImageParser.PcxHeader.ENCODING_UNCOMPRESSED;
+ }
+ }
+
+ if (params.containsKey(PARAM_KEY_PCX_BIT_DEPTH))
+ {
+ Object value = params.remove(PARAM_KEY_PCX_BIT_DEPTH);
+ if (value != null)
+ {
+ if (!(value instanceof Number))
+ throw new ImageWriteException(
+ "Invalid bit depth parameter: " + value);
+ bitDepth = ((Number)value).intValue();
+ }
+ }
+
+ if (params.size() > 0)
+ {
+ Object firstKey = params.keySet().iterator().next();
+ throw new ImageWriteException("Unknown parameter: " + firstKey);
+ }
+ }
+
+ private void writeScanLine(BinaryOutputStream bos, byte[] scanline)
+ throws IOException, ImageWriteException
+ {
+ if (encoding == PcxImageParser.PcxHeader.ENCODING_UNCOMPRESSED)
+ bos.writeByteArray(scanline);
+ else
+ {
+ if (encoding == PcxImageParser.PcxHeader.ENCODING_RLE)
+ {
+ int previousByte = -1;
+ int repeatCount = 0;
+ for (int i = 0; i < scanline.length; i++)
+ {
+ if ((scanline[i] & 0xff) == previousByte && repeatCount < 63)
+ ++repeatCount;
+ else
+ {
+ if (repeatCount > 0)
+ {
+ if (repeatCount == 1 && (previousByte & 0xc0) != 0xc0)
+ bos.write(previousByte);
+ else
+ {
+ bos.write(0xc0 | repeatCount);
+ bos.write(previousByte);
+ }
+ }
+ previousByte = 0xff & scanline[i];
+ repeatCount = 1;
+ }
+ }
+ if (repeatCount > 0)
+ {
+ if (repeatCount == 1 && (previousByte & 0xc0) != 0xc0)
+ bos.write(previousByte);
+ else
+ {
+ bos.write(0xc0 | repeatCount);
+ bos.write(previousByte);
+ }
+ }
+ }
+ else
+ throw new ImageWriteException("Invalid PCX encoding " + encoding);
+ }
+ }
+
+ public void writeImage(BufferedImage src, OutputStream os)
+ throws ImageWriteException, IOException
+ {
+ final PaletteFactory paletteFactory = new PaletteFactory();
+ final SimplePalette palette = paletteFactory.makePaletteSimple(src, 256);
+ BinaryOutputStream bos = new BinaryOutputStream(os, BinaryOutputStream.BYTE_ORDER_INTEL);
+ if (palette == null || bitDepth == 24 || bitDepth == 32)
+ {
+ if (bitDepth == 32)
+ write32BppPCX(src, bos);
+ else
+ write24BppPCX(src, bos);
+ }
+ else if (palette.length() > 16 || bitDepth == 8)
+ write256ColorPCX(src, palette, bos);
+ else if (palette.length() > 2 || bitDepth == 4)
+ write16ColorPCX(src, palette, bos);
+ else
+ {
+ boolean onlyBlackAndWhite = true;
+ if (palette.length() >= 1)
+ {
+ int rgb = palette.getEntry(0);
+ if (rgb != 0 && rgb != 0xffffff)
+ onlyBlackAndWhite = false;
+ }
+ if (palette.length() == 2)
+ {
+ int rgb = palette.getEntry(1);
+ if (rgb != 0 && rgb != 0xffffff)
+ onlyBlackAndWhite = false;
+ }
+ if (onlyBlackAndWhite)
+ writeBlackAndWhitePCX(src, palette, bos);
+ else
+ write16ColorPCX(src, palette, bos);
+ }
+ }
+
+ private void write32BppPCX(BufferedImage src, BinaryOutputStream bos)
+ throws ImageWriteException, IOException
+ {
+ final int bytesPerLine = src.getWidth() % 2 == 0 ?
+ src.getWidth() : src.getWidth() + 1;
+
+ // PCX header
+ bos.write(10); // manufacturer
+ bos.write(5); // version
+ bos.write(encoding); // encoding
+ bos.write(32); // bits per pixel
+ bos.write2Bytes(0); // xMin
+ bos.write2Bytes(0); // yMin
+ bos.write2Bytes(src.getWidth() - 1); // xMax
+ bos.write2Bytes(src.getHeight() - 1); // yMax
+ bos.write2Bytes(300); // hDpi
+ bos.write2Bytes(300); // vDpi
+ bos.writeByteArray(new byte[48]); // 16 color palette
+ bos.write(0); // reserved
+ bos.write(1); // planes
+ bos.write2Bytes(bytesPerLine); // bytes per line
+ bos.write2Bytes(1); // palette info
+ bos.write2Bytes(0); // hScreenSize
+ bos.write2Bytes(0); // vScreenSize
+ bos.writeByteArray(new byte[54]);
+
+ int rgbs[] = new int[src.getWidth()];
+ byte rgbBytes[] = new byte[4 * bytesPerLine];
+ for (int y = 0; y < src.getHeight(); y++)
+ {
+ src.getRGB(0, y, src.getWidth(), 1, rgbs, 0, src.getWidth());
+ for (int x = 0; x < rgbs.length; x++)
+ {
+ rgbBytes[4*x + 0] = (byte) (rgbs[x] & 0xff);
+ rgbBytes[4*x + 1] = (byte) ((rgbs[x] >> 8) & 0xff);
+ rgbBytes[4*x + 2] = (byte) ((rgbs[x] >> 16) & 0xff);
+ rgbBytes[4*x + 3] = 0;
+ }
+ writeScanLine(bos, rgbBytes);
+ }
+ }
+
+ private void write24BppPCX(BufferedImage src, BinaryOutputStream bos)
+ throws ImageWriteException, IOException
+ {
+ final int bytesPerLine = src.getWidth() % 2 == 0 ?
+ src.getWidth() : src.getWidth() + 1;
+
+ // PCX header
+ bos.write(10); // manufacturer
+ bos.write(5); // version
+ bos.write(encoding); // encoding
+ bos.write(8); // bits per pixel
+ bos.write2Bytes(0); // xMin
+ bos.write2Bytes(0); // yMin
+ bos.write2Bytes(src.getWidth() - 1); // xMax
+ bos.write2Bytes(src.getHeight() - 1); // yMax
+ bos.write2Bytes(300); // hDpi
+ bos.write2Bytes(300); // vDpi
+ bos.writeByteArray(new byte[48]); // 16 color palette
+ bos.write(0); // reserved
+ bos.write(3); // planes
+ bos.write2Bytes(bytesPerLine); // bytes per line
+ bos.write2Bytes(1); // palette info
+ bos.write2Bytes(0); // hScreenSize
+ bos.write2Bytes(0); // vScreenSize
+ bos.writeByteArray(new byte[54]);
+
+ int rgbs[] = new int[src.getWidth()];
+ byte rgbBytes[] = new byte[3 * bytesPerLine];
+ for (int y = 0; y < src.getHeight(); y++)
+ {
+ src.getRGB(0, y, src.getWidth(), 1, rgbs, 0, src.getWidth());
+ for (int x = 0; x < rgbs.length; x++)
+ {
+ rgbBytes[x] = (byte) ((rgbs[x] >> 16) & 0xff);
+ rgbBytes[bytesPerLine + x] = (byte) ((rgbs[x] >> 8) & 0xff);
+ rgbBytes[2 * bytesPerLine + x] = (byte) (rgbs[x] & 0xff);
+ }
+ writeScanLine(bos, rgbBytes);
+ }
+ }
+
+ private void writeBlackAndWhitePCX(BufferedImage src, SimplePalette palette, BinaryOutputStream bos)
+ throws ImageWriteException, IOException
+ {
+ int bytesPerLine = (src.getWidth() + 7) / 8;
+ if (bytesPerLine % 2 != 0)
+ ++bytesPerLine;
+
+ // PCX header
+ bos.write(10); // manufacturer
+ bos.write(3); // version - it seems some apps only open
+ // black and white files if the version is 3...
+ bos.write(encoding); // encoding
+ bos.write(1); // bits per pixel
+ bos.write2Bytes(0); // xMin
+ bos.write2Bytes(0); // yMin
+ bos.write2Bytes(src.getWidth() - 1); // xMax
+ bos.write2Bytes(src.getHeight() - 1); // yMax
+ bos.write2Bytes(300); // hDpi
+ bos.write2Bytes(300); // vDpi
+ bos.writeByteArray(new byte[48]); // 16 color palette
+ bos.write(0); // reserved
+ bos.write(1); // planes
+ bos.write2Bytes(bytesPerLine); // bytes per line
+ bos.write2Bytes(1); // palette info
+ bos.write2Bytes(0); // hScreenSize
+ bos.write2Bytes(0); // vScreenSize
+ bos.writeByteArray(new byte[54]);
+
+ byte[] row = new byte[bytesPerLine];
+ for (int y = 0; y < src.getHeight(); y++)
+ {
+ Arrays.fill(row, (byte)0);
+ for (int x = 0; x < src.getWidth(); x++)
+ {
+ int rgb = 0xffffff & src.getRGB(x, y);
+ int bit;
+ if (rgb == 0x000000)
+ bit = 0;
+ else if (rgb == 0xffffff)
+ bit = 1;
+ else
+ throw new ImageWriteException("Pixel neither black nor white");
+ row[x / 8] |= (bit << (7 - (x % 8)));
+ }
+ writeScanLine(bos, row);
+ }
+ }
+
+ private void write16ColorPCX(BufferedImage src, SimplePalette palette, BinaryOutputStream bos)
+ throws ImageWriteException, IOException
+ {
+ int bytesPerLine = (src.getWidth() + 1) / 2;
+ if (bytesPerLine % 2 != 0)
+ ++bytesPerLine;
+
+ byte[] palette16 = new byte[16*3];
+ for (int i = 0; i < 16; i++)
+ {
+ int rgb;
+ if (i < palette.length())
+ rgb = palette.getEntry(i);
+ else
+ rgb = 0;
+ palette16[3*i + 0] = (byte) (0xff & (rgb >> 16));
+ palette16[3*i + 1] = (byte) (0xff & (rgb >> 8));
+ palette16[3*i + 2] = (byte) (0xff & rgb);
+ }
+
+ // PCX header
+ bos.write(10); // manufacturer
+ bos.write(5); // version
+ bos.write(encoding); // encoding
+ bos.write(4); // bits per pixel
+ bos.write2Bytes(0); // xMin
+ bos.write2Bytes(0); // yMin
+ bos.write2Bytes(src.getWidth() - 1); // xMax
+ bos.write2Bytes(src.getHeight() - 1); // yMax
+ bos.write2Bytes(300); // hDpi
+ bos.write2Bytes(300); // vDpi
+ bos.writeByteArray(palette16); // 16 color palette
+ bos.write(0); // reserved
+ bos.write(1); // planes
+ bos.write2Bytes(bytesPerLine); // bytes per line
+ bos.write2Bytes(1); // palette info
+ bos.write2Bytes(0); // hScreenSize
+ bos.write2Bytes(0); // vScreenSize
+ bos.writeByteArray(new byte[54]);
+
+ byte[] indeces = new byte[bytesPerLine];
+ for (int y = 0; y < src.getHeight(); y++)
+ {
+ Arrays.fill(indeces, (byte)0);
+ for (int x = 0; x < src.getWidth(); x++)
+ {
+ int argb = src.getRGB(x, y);
+ int index = palette.getPaletteIndex(0xffffff & argb);
+ indeces[x / 2] |= (index << 4*(1 - (x % 2)));
+ }
+ writeScanLine(bos, indeces);
+ }
+ }
+
+ private void write256ColorPCX(BufferedImage src, SimplePalette palette, BinaryOutputStream bos)
+ throws ImageWriteException, IOException
+ {
+ final int bytesPerLine = src.getWidth() % 2 == 0 ?
+ src.getWidth() : src.getWidth() + 1;
+
+ // PCX header
+ bos.write(10); // manufacturer
+ bos.write(5); // version
+ bos.write(encoding); // encoding
+ bos.write(8); // bits per pixel
+ bos.write2Bytes(0); // xMin
+ bos.write2Bytes(0); // yMin
+ bos.write2Bytes(src.getWidth() - 1); // xMax
+ bos.write2Bytes(src.getHeight() - 1); // yMax
+ bos.write2Bytes(300); // hDpi
+ bos.write2Bytes(300); // vDpi
+ bos.writeByteArray(new byte[48]); // 16 color palette
+ bos.write(0); // reserved
+ bos.write(1); // planes
+ bos.write2Bytes(bytesPerLine); // bytes per line
+ bos.write2Bytes(1); // palette info
+ bos.write2Bytes(0); // hScreenSize
+ bos.write2Bytes(0); // vScreenSize
+ bos.writeByteArray(new byte[54]);
+
+ byte[] indeces = new byte[bytesPerLine];
+ for (int y = 0; y < src.getHeight(); y++)
+ {
+ for (int x = 0; x < src.getWidth(); x++)
+ {
+ int argb = src.getRGB(x, y);
+ int index = palette.getPaletteIndex(0xffffff & argb);
+ indeces[x] = (byte) index;
+ }
+ writeScanLine(bos, indeces);
+ }
+ // palette
+ bos.write(12);
+ for (int i = 0; i < 256; i++)
+ {
+ int rgb;
+ if (i < palette.length())
+ rgb = palette.getEntry(i);
+ else
+ rgb = 0;
+ bos.write((rgb >> 16) & 0xff);
+ bos.write((rgb >> 8) & 0xff);
+ bos.write(rgb & 0xff);
+ }
+ }
+}
Propchange: commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/pcx/PcxWriter.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: commons/proper/sanselan/trunk/src/test/data/images/pcx/1/1plane1bppCompressed.pcx
URL: http://svn.apache.org/viewvc/commons/proper/sanselan/trunk/src/test/data/images/pcx/1/1plane1bppCompressed.pcx?rev=1087970&view=auto
==============================================================================
Binary file - no diff available.
Propchange: commons/proper/sanselan/trunk/src/test/data/images/pcx/1/1plane1bppCompressed.pcx
------------------------------------------------------------------------------
svn:mime-type = application/octet-stream
Added: commons/proper/sanselan/trunk/src/test/data/images/pcx/1/1plane1bppUncompressed.pcx
URL: http://svn.apache.org/viewvc/commons/proper/sanselan/trunk/src/test/data/images/pcx/1/1plane1bppUncompressed.pcx?rev=1087970&view=auto
==============================================================================
Binary file - no diff available.
Propchange: commons/proper/sanselan/trunk/src/test/data/images/pcx/1/1plane1bppUncompressed.pcx
------------------------------------------------------------------------------
svn:mime-type = application/octet-stream
Added: commons/proper/sanselan/trunk/src/test/data/images/pcx/1/1plane24bppCompressed.pcx
URL: http://svn.apache.org/viewvc/commons/proper/sanselan/trunk/src/test/data/images/pcx/1/1plane24bppCompressed.pcx?rev=1087970&view=auto
==============================================================================
Binary file - no diff available.
Propchange: commons/proper/sanselan/trunk/src/test/data/images/pcx/1/1plane24bppCompressed.pcx
------------------------------------------------------------------------------
svn:mime-type = application/octet-stream
Added: commons/proper/sanselan/trunk/src/test/data/images/pcx/1/1plane2bppCompressed.pcx
URL: http://svn.apache.org/viewvc/commons/proper/sanselan/trunk/src/test/data/images/pcx/1/1plane2bppCompressed.pcx?rev=1087970&view=auto
==============================================================================
Binary file - no diff available.
Propchange: commons/proper/sanselan/trunk/src/test/data/images/pcx/1/1plane2bppCompressed.pcx
------------------------------------------------------------------------------
svn:mime-type = application/octet-stream
Added: commons/proper/sanselan/trunk/src/test/data/images/pcx/1/1plane32bppCompressed.pcx
URL: http://svn.apache.org/viewvc/commons/proper/sanselan/trunk/src/test/data/images/pcx/1/1plane32bppCompressed.pcx?rev=1087970&view=auto
==============================================================================
Binary file - no diff available.
Propchange: commons/proper/sanselan/trunk/src/test/data/images/pcx/1/1plane32bppCompressed.pcx
------------------------------------------------------------------------------
svn:mime-type = application/octet-stream
Added: commons/proper/sanselan/trunk/src/test/data/images/pcx/1/1plane4bppCompressed.pcx
URL: http://svn.apache.org/viewvc/commons/proper/sanselan/trunk/src/test/data/images/pcx/1/1plane4bppCompressed.pcx?rev=1087970&view=auto
==============================================================================
Binary file - no diff available.
Propchange: commons/proper/sanselan/trunk/src/test/data/images/pcx/1/1plane4bppCompressed.pcx
------------------------------------------------------------------------------
svn:mime-type = application/octet-stream
Added: commons/proper/sanselan/trunk/src/test/data/images/pcx/1/1plane8bppCompressed.pcx
URL: http://svn.apache.org/viewvc/commons/proper/sanselan/trunk/src/test/data/images/pcx/1/1plane8bppCompressed.pcx?rev=1087970&view=auto
==============================================================================
Binary file - no diff available.
Propchange: commons/proper/sanselan/trunk/src/test/data/images/pcx/1/1plane8bppCompressed.pcx
------------------------------------------------------------------------------
svn:mime-type = application/octet-stream
Added: commons/proper/sanselan/trunk/src/test/data/images/pcx/1/1plane8bppUncompressed.pcx
URL: http://svn.apache.org/viewvc/commons/proper/sanselan/trunk/src/test/data/images/pcx/1/1plane8bppUncompressed.pcx?rev=1087970&view=auto
==============================================================================
Binary file - no diff available.
Propchange: commons/proper/sanselan/trunk/src/test/data/images/pcx/1/1plane8bppUncompressed.pcx
------------------------------------------------------------------------------
svn:mime-type = application/octet-stream
Added: commons/proper/sanselan/trunk/src/test/data/images/pcx/1/2plane1bppCompressed.pcx
URL: http://svn.apache.org/viewvc/commons/proper/sanselan/trunk/src/test/data/images/pcx/1/2plane1bppCompressed.pcx?rev=1087970&view=auto
==============================================================================
Binary file - no diff available.
Propchange: commons/proper/sanselan/trunk/src/test/data/images/pcx/1/2plane1bppCompressed.pcx
------------------------------------------------------------------------------
svn:mime-type = application/octet-stream
Added: commons/proper/sanselan/trunk/src/test/data/images/pcx/1/3plane1bppCompressed.pcx
URL: http://svn.apache.org/viewvc/commons/proper/sanselan/trunk/src/test/data/images/pcx/1/3plane1bppCompressed.pcx?rev=1087970&view=auto
==============================================================================
Binary file - no diff available.
Propchange: commons/proper/sanselan/trunk/src/test/data/images/pcx/1/3plane1bppCompressed.pcx
------------------------------------------------------------------------------
svn:mime-type = application/octet-stream
Added: commons/proper/sanselan/trunk/src/test/data/images/pcx/1/3plane8bppCompressed.pcx
URL: http://svn.apache.org/viewvc/commons/proper/sanselan/trunk/src/test/data/images/pcx/1/3plane8bppCompressed.pcx?rev=1087970&view=auto
==============================================================================
Binary file - no diff available.
Propchange: commons/proper/sanselan/trunk/src/test/data/images/pcx/1/3plane8bppCompressed.pcx
------------------------------------------------------------------------------
svn:mime-type = application/octet-stream
Added: commons/proper/sanselan/trunk/src/test/data/images/pcx/1/3plane8bppUncompressed.pcx
URL: http://svn.apache.org/viewvc/commons/proper/sanselan/trunk/src/test/data/images/pcx/1/3plane8bppUncompressed.pcx?rev=1087970&view=auto
==============================================================================
Binary file - no diff available.
Propchange: commons/proper/sanselan/trunk/src/test/data/images/pcx/1/3plane8bppUncompressed.pcx
------------------------------------------------------------------------------
svn:mime-type = application/octet-stream
Added: commons/proper/sanselan/trunk/src/test/data/images/pcx/1/4plane1bppCompressed.pcx
URL: http://svn.apache.org/viewvc/commons/proper/sanselan/trunk/src/test/data/images/pcx/1/4plane1bppCompressed.pcx?rev=1087970&view=auto
==============================================================================
Binary file - no diff available.
Propchange: commons/proper/sanselan/trunk/src/test/data/images/pcx/1/4plane1bppCompressed.pcx
------------------------------------------------------------------------------
svn:mime-type = application/octet-stream
Added: commons/proper/sanselan/trunk/src/test/data/images/pcx/1/Oregon Scientific DS6639 - DSC_0307 - small.pcx
URL: http://svn.apache.org/viewvc/commons/proper/sanselan/trunk/src/test/data/images/pcx/1/Oregon%20Scientific%20DS6639%20-%20DSC_0307%20-%20small.pcx?rev=1087970&view=auto
==============================================================================
Binary file - no diff available.
Propchange: commons/proper/sanselan/trunk/src/test/data/images/pcx/1/Oregon Scientific DS6639 - DSC_0307 - small.pcx
------------------------------------------------------------------------------
svn:mime-type = application/octet-stream
Added: commons/proper/sanselan/trunk/src/test/data/images/pcx/1/info.txt
URL: http://svn.apache.org/viewvc/commons/proper/sanselan/trunk/src/test/data/images/pcx/1/info.txt?rev=1087970&view=auto
==============================================================================
--- commons/proper/sanselan/trunk/src/test/data/images/pcx/1/info.txt (added)
+++ commons/proper/sanselan/trunk/src/test/data/images/pcx/1/info.txt Sat Apr 2 08:22:30 2011
@@ -0,0 +1,3 @@
+Contributed to the project by Damjan Jovanovic.
+Oregon Scientific DS6639 - DSC_0307 - small.pcx was converted from ../../bmp/1/Oregon Scientific DS6639 - DSC_0307 - small.bmp
+The XplaneYbpp[Un]compressed.pcx files were programmatically generated.
Propchange: commons/proper/sanselan/trunk/src/test/data/images/pcx/1/info.txt
------------------------------------------------------------------------------
svn:eol-style = native
Modified: commons/proper/sanselan/trunk/src/test/java/org/apache/sanselan/common/byteSources/ByteSourceImageTest.java
URL: http://svn.apache.org/viewvc/commons/proper/sanselan/trunk/src/test/java/org/apache/sanselan/common/byteSources/ByteSourceImageTest.java?rev=1087970&r1=1087969&r2=1087970&view=diff
==============================================================================
--- commons/proper/sanselan/trunk/src/test/java/org/apache/sanselan/common/byteSources/ByteSourceImageTest.java (original)
+++ commons/proper/sanselan/trunk/src/test/java/org/apache/sanselan/common/byteSources/ByteSourceImageTest.java Sat Apr 2 08:22:30 2011
@@ -59,6 +59,7 @@ public class ByteSourceImageTest extends
if (imageFile.getName().toLowerCase().endsWith(".ico")
|| imageFile.getName().toLowerCase().endsWith(".tga")
|| imageFile.getName().toLowerCase().endsWith(".jb2")
+ || imageFile.getName().toLowerCase().endsWith(".pcx")
|| imageFile.getName().toLowerCase().endsWith(".psd")
|| imageFile.getName().toLowerCase().endsWith(".wbmp"))
{
Added: commons/proper/sanselan/trunk/src/test/java/org/apache/sanselan/formats/pcx/PcxBaseTest.java
URL: http://svn.apache.org/viewvc/commons/proper/sanselan/trunk/src/test/java/org/apache/sanselan/formats/pcx/PcxBaseTest.java?rev=1087970&view=auto
==============================================================================
--- commons/proper/sanselan/trunk/src/test/java/org/apache/sanselan/formats/pcx/PcxBaseTest.java (added)
+++ commons/proper/sanselan/trunk/src/test/java/org/apache/sanselan/formats/pcx/PcxBaseTest.java Sat Apr 2 08:22:30 2011
@@ -0,0 +1,46 @@
+/*
+ * 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.pcx;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+
+import org.apache.sanselan.ImageReadException;
+import org.apache.sanselan.SanselanTest;
+
+public abstract class PcxBaseTest extends SanselanTest
+{
+
+ private static boolean isPcx(File file) throws IOException, ImageReadException
+ {
+ return file.getName().toLowerCase().endsWith(".pcx");
+ }
+
+ private static final ImageFilter IMAGE_FILTER = new ImageFilter() {
+ public boolean accept(File file) throws IOException, ImageReadException
+ {
+ return isPcx(file);
+ }
+ };
+
+ protected List getPcxImages() throws IOException, ImageReadException
+ {
+ return getTestImages(IMAGE_FILTER);
+ }
+}
Propchange: commons/proper/sanselan/trunk/src/test/java/org/apache/sanselan/formats/pcx/PcxBaseTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: commons/proper/sanselan/trunk/src/test/java/org/apache/sanselan/formats/pcx/PcxReadTest.java
URL: http://svn.apache.org/viewvc/commons/proper/sanselan/trunk/src/test/java/org/apache/sanselan/formats/pcx/PcxReadTest.java?rev=1087970&view=auto
==============================================================================
--- commons/proper/sanselan/trunk/src/test/java/org/apache/sanselan/formats/pcx/PcxReadTest.java (added)
+++ commons/proper/sanselan/trunk/src/test/java/org/apache/sanselan/formats/pcx/PcxReadTest.java Sat Apr 2 08:22:30 2011
@@ -0,0 +1,61 @@
+/*
+ * 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.pcx;
+
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.sanselan.ImageInfo;
+import org.apache.sanselan.ImageReadException;
+import org.apache.sanselan.Sanselan;
+import org.apache.sanselan.common.IImageMetadata;
+import org.apache.sanselan.util.Debug;
+
+public class PcxReadTest extends PcxBaseTest
+{
+
+ public void test() throws IOException, ImageReadException
+ {
+ Debug.debug("start");
+
+ List images = getPcxImages();
+ for (int i = 0; i < images.size(); i++)
+ {
+ if (i % 10 == 0)
+ Debug.purgeMemory();
+
+ File imageFile = (File) images.get(i);
+ Debug.debug("imageFile", imageFile);
+
+ IImageMetadata metadata = Sanselan.getMetadata(imageFile);
+ // assertNotNull(metadata);
+
+ Map params = new HashMap();
+ ImageInfo imageInfo = Sanselan.getImageInfo(imageFile, params);
+ assertNotNull(imageInfo);
+
+ BufferedImage image = Sanselan.getBufferedImage(imageFile);
+ assertNotNull(image);
+ }
+ }
+
+}
Propchange: commons/proper/sanselan/trunk/src/test/java/org/apache/sanselan/formats/pcx/PcxReadTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified: commons/proper/sanselan/trunk/src/test/java/org/apache/sanselan/roundtrip/RoundtripTest.java
URL: http://svn.apache.org/viewvc/commons/proper/sanselan/trunk/src/test/java/org/apache/sanselan/roundtrip/RoundtripTest.java?rev=1087970&r1=1087969&r2=1087970&view=diff
==============================================================================
--- commons/proper/sanselan/trunk/src/test/java/org/apache/sanselan/roundtrip/RoundtripTest.java (original)
+++ commons/proper/sanselan/trunk/src/test/java/org/apache/sanselan/roundtrip/RoundtripTest.java Sat Apr 2 08:22:30 2011
@@ -88,6 +88,8 @@ public class RoundtripTest extends Sanse
COLOR_FULL_RGB, true), //
new FormatInfo(ImageFormat.IMAGE_FORMAT_WBMP, true, true,
COLOR_BITMAP, true), //
+ new FormatInfo(ImageFormat.IMAGE_FORMAT_PCX, true, true,
+ COLOR_FULL_RGB, true), //
};
private BufferedImage createArgbBitmapImage(int width, int height)