commons-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From cmc...@apache.org
Subject svn commit: r1000270 - in /commons/proper/sanselan/trunk/src: main/java/org/apache/sanselan/ main/java/org/apache/sanselan/formats/icns/ test/data/images/icns/ test/data/images/icns/1/ test/java/org/apache/sanselan/ test/java/org/apache/sanselan/format...
Date Wed, 22 Sep 2010 23:09:22 GMT
Author: cmchen
Date: Wed Sep 22 23:09:22 2010
New Revision: 1000270

URL: http://svn.apache.org/viewvc?rev=1000270&view=rev
Log:
Submitting an ICNS-related patch from Damjan Jovanovic.
JIRA Key: SANSELAN-45

Added:
    commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/icns/
    commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/icns/IcnsDecoder.java   (with props)
    commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/icns/IcnsImageParser.java   (with props)
    commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/icns/IcnsType.java   (with props)
    commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/icns/Rle24Compression.java   (with props)
    commons/proper/sanselan/trunk/src/test/data/images/icns/
    commons/proper/sanselan/trunk/src/test/data/images/icns/1/
    commons/proper/sanselan/trunk/src/test/data/images/icns/1/info.txt   (with props)
    commons/proper/sanselan/trunk/src/test/data/images/icns/1/poedit16x16.icns   (with props)
    commons/proper/sanselan/trunk/src/test/data/images/icns/1/poedit32x32.icns   (with props)
    commons/proper/sanselan/trunk/src/test/data/images/icns/1/poedit48x48.icns   (with props)
    commons/proper/sanselan/trunk/src/test/java/org/apache/sanselan/formats/icns/
    commons/proper/sanselan/trunk/src/test/java/org/apache/sanselan/formats/icns/IcnsBaseTest.java   (with props)
    commons/proper/sanselan/trunk/src/test/java/org/apache/sanselan/formats/icns/IcnsReadTest.java   (with props)
    commons/proper/sanselan/trunk/src/test/java/org/apache/sanselan/formats/icns/IcnsRoundTripTest.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/main/java/org/apache/sanselan/Sanselan.java
    commons/proper/sanselan/trunk/src/test/java/org/apache/sanselan/SanselanGuessFormatTest.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=1000270&r1=1000269&r2=1000270&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 Wed Sep 22 23:09:22 2010
@@ -72,6 +72,7 @@ public class ImageFormat
     public static final ImageFormat IMAGE_FORMAT_PNM = new ImageFormat("PNM");
     public static final ImageFormat IMAGE_FORMAT_TGA = new ImageFormat("TGA");
     public static final ImageFormat IMAGE_FORMAT_JBIG2 = new ImageFormat("JBig2");
+    public static final ImageFormat IMAGE_FORMAT_ICNS = new ImageFormat("ICNS");
 
     public static final ImageFormat[] getAllFormats()
     {
@@ -80,7 +81,7 @@ public class ImageFormat
                 IMAGE_FORMAT_TIFF, IMAGE_FORMAT_JPEG, IMAGE_FORMAT_BMP,
                 IMAGE_FORMAT_PSD, IMAGE_FORMAT_PBM, IMAGE_FORMAT_PGM,
                 IMAGE_FORMAT_PPM, IMAGE_FORMAT_PNM, IMAGE_FORMAT_TGA,
-                IMAGE_FORMAT_JBIG2,
+                IMAGE_FORMAT_JBIG2, IMAGE_FORMAT_ICNS,
         };
 
         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=1000270&r1=1000269&r2=1000270&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 Wed Sep 22 23:09:22 2010
@@ -35,6 +35,7 @@ import org.apache.sanselan.common.byteSo
 import org.apache.sanselan.common.byteSources.ByteSourceFile;
 import org.apache.sanselan.formats.bmp.BmpImageParser;
 import org.apache.sanselan.formats.gif.GifImageParser;
+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.png.PngImageParser;
@@ -53,6 +54,7 @@ public abstract class ImageParser extend
                 new PngImageParser(), new BmpImageParser(),
                 new GifImageParser(), new PsdImageParser(),
                 new PNMImageParser(), new IcoImageParser(),
+                new IcnsImageParser(),
         // new JBig2ImageParser(),
         // new TgaImageParser(),
         };

Modified: commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/Sanselan.java
URL: http://svn.apache.org/viewvc/commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/Sanselan.java?rev=1000270&r1=1000269&r2=1000270&view=diff
==============================================================================
--- commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/Sanselan.java (original)
+++ commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/Sanselan.java Wed Sep 22 23:09:22 2010
@@ -149,6 +149,7 @@ public abstract class Sanselan implement
     private static final int[] MAGIC_NUMBERS_PPM_B = { 0x50, 0x36, };
     private static final int[] MAGIC_NUMBERS_JBIG2_1 = { 0x97, 0x4A, };
     private static final int[] MAGIC_NUMBERS_JBIG2_2 = { 0x42, 0x32, };
+	private static final int[] MAGIC_NUMBERS_ICNS = { 0x69, 0x63, };
 
     private static boolean compareBytePair(int[] a, int b[]) {
         if (a.length != 2 && b.length != 2) {
@@ -218,6 +219,8 @@ public abstract class Sanselan implement
                 if (compareBytePair(MAGIC_NUMBERS_JBIG2_2, bytePair2)) {
                     return ImageFormat.IMAGE_FORMAT_JBIG2;
                 }
+			} else if (compareBytePair(MAGIC_NUMBERS_ICNS, bytePair)) {
+				return ImageFormat.IMAGE_FORMAT_ICNS;
             }
 
             return ImageFormat.IMAGE_FORMAT_UNKNOWN;
@@ -1405,4 +1408,4 @@ public abstract class Sanselan implement
         throw new ImageWriteException("Unknown Format: " + format);
     }
 
-}
\ No newline at end of file
+}

Added: commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/icns/IcnsDecoder.java
URL: http://svn.apache.org/viewvc/commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/icns/IcnsDecoder.java?rev=1000270&view=auto
==============================================================================
--- commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/icns/IcnsDecoder.java (added)
+++ commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/icns/IcnsDecoder.java Wed Sep 22 23:09:22 2010
@@ -0,0 +1,538 @@
+/*
+ * 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.icns;
+
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.util.ArrayList;
+
+import org.apache.sanselan.ImageReadException;
+
+public class IcnsDecoder
+{
+	private static final int[] palette_4bpp =
+	{
+		0xffffffff,
+		0xfffcf305,
+		0xffff6402,
+		0xffdd0806,
+		0xfff20884,
+		0xff4600a5,
+		0xff0000d4,
+		0xff02abea,
+		0xff1fb714,
+		0xff006411,
+		0xff562c05,
+		0xff90713a,
+		0xffc0c0c0,
+		0xff808080,
+		0xff404040,
+		0xff000000
+	};
+
+	private static final int[] palette_8bpp =
+	{
+		0xFFFFFFFF,
+		0xFFFFFFCC,
+		0xFFFFFF99,
+		0xFFFFFF66,
+		0xFFFFFF33,
+		0xFFFFFF00,
+		0xFFFFCCFF,
+		0xFFFFCCCC,
+		0xFFFFCC99,
+		0xFFFFCC66,
+		0xFFFFCC33,
+		0xFFFFCC00,
+		0xFFFF99FF,
+		0xFFFF99CC,
+		0xFFFF9999,
+		0xFFFF9966,
+		0xFFFF9933,
+		0xFFFF9900,
+		0xFFFF66FF,
+		0xFFFF66CC,
+		0xFFFF6699,
+		0xFFFF6666,
+		0xFFFF6633,
+		0xFFFF6600,
+		0xFFFF33FF,
+		0xFFFF33CC,
+		0xFFFF3399,
+		0xFFFF3366,
+		0xFFFF3333,
+		0xFFFF3300,
+		0xFFFF00FF,
+		0xFFFF00CC,
+		0xFFFF0099,
+		0xFFFF0066,
+		0xFFFF0033,
+		0xFFFF0000,
+		0xFFCCFFFF,
+		0xFFCCFFCC,
+		0xFFCCFF99,
+		0xFFCCFF66,
+		0xFFCCFF33,
+		0xFFCCFF00,
+		0xFFCCCCFF,
+		0xFFCCCCCC,
+		0xFFCCCC99,
+		0xFFCCCC66,
+		0xFFCCCC33,
+		0xFFCCCC00,
+		0xFFCC99FF,
+		0xFFCC99CC,
+		0xFFCC9999,
+		0xFFCC9966,
+		0xFFCC9933,
+		0xFFCC9900,
+		0xFFCC66FF,
+		0xFFCC66CC,
+		0xFFCC6699,
+		0xFFCC6666,
+		0xFFCC6633,
+		0xFFCC6600,
+		0xFFCC33FF,
+		0xFFCC33CC,
+		0xFFCC3399,
+		0xFFCC3366,
+		0xFFCC3333,
+		0xFFCC3300,
+		0xFFCC00FF,
+		0xFFCC00CC,
+		0xFFCC0099,
+		0xFFCC0066,
+		0xFFCC0033,
+		0xFFCC0000,
+		0xFF99FFFF,
+		0xFF99FFCC,
+		0xFF99FF99,
+		0xFF99FF66,
+		0xFF99FF33,
+		0xFF99FF00,
+		0xFF99CCFF,
+		0xFF99CCCC,
+		0xFF99CC99,
+		0xFF99CC66,
+		0xFF99CC33,
+		0xFF99CC00,
+		0xFF9999FF,
+		0xFF9999CC,
+		0xFF999999,
+		0xFF999966,
+		0xFF999933,
+		0xFF999900,
+		0xFF9966FF,
+		0xFF9966CC,
+		0xFF996699,
+		0xFF996666,
+		0xFF996633,
+		0xFF996600,
+		0xFF9933FF,
+		0xFF9933CC,
+		0xFF993399,
+		0xFF993366,
+		0xFF993333,
+		0xFF993300,
+		0xFF9900FF,
+		0xFF9900CC,
+		0xFF990099,
+		0xFF990066,
+		0xFF990033,
+		0xFF990000,
+		0xFF66FFFF,
+		0xFF66FFCC,
+		0xFF66FF99,
+		0xFF66FF66,
+		0xFF66FF33,
+		0xFF66FF00,
+		0xFF66CCFF,
+		0xFF66CCCC,
+		0xFF66CC99,
+		0xFF66CC66,
+		0xFF66CC33,
+		0xFF66CC00,
+		0xFF6699FF,
+		0xFF6699CC,
+		0xFF669999,
+		0xFF669966,
+		0xFF669933,
+		0xFF669900,
+		0xFF6666FF,
+		0xFF6666CC,
+		0xFF666699,
+		0xFF666666,
+		0xFF666633,
+		0xFF666600,
+		0xFF6633FF,
+		0xFF6633CC,
+		0xFF663399,
+		0xFF663366,
+		0xFF663333,
+		0xFF663300,
+		0xFF6600FF,
+		0xFF6600CC,
+		0xFF660099,
+		0xFF660066,
+		0xFF660033,
+		0xFF660000,
+		0xFF33FFFF,
+		0xFF33FFCC,
+		0xFF33FF99,
+		0xFF33FF66,
+		0xFF33FF33,
+		0xFF33FF00,
+		0xFF33CCFF,
+		0xFF33CCCC,
+		0xFF33CC99,
+		0xFF33CC66,
+		0xFF33CC33,
+		0xFF33CC00,
+		0xFF3399FF,
+		0xFF3399CC,
+		0xFF339999,
+		0xFF339966,
+		0xFF339933,
+		0xFF339900,
+		0xFF3366FF,
+		0xFF3366CC,
+		0xFF336699,
+		0xFF336666,
+		0xFF336633,
+		0xFF336600,
+		0xFF3333FF,
+		0xFF3333CC,
+		0xFF333399,
+		0xFF333366,
+		0xFF333333,
+		0xFF333300,
+		0xFF3300FF,
+		0xFF3300CC,
+		0xFF330099,
+		0xFF330066,
+		0xFF330033,
+		0xFF330000,
+		0xFF00FFFF,
+		0xFF00FFCC,
+		0xFF00FF99,
+		0xFF00FF66,
+		0xFF00FF33,
+		0xFF00FF00,
+		0xFF00CCFF,
+		0xFF00CCCC,
+		0xFF00CC99,
+		0xFF00CC66,
+		0xFF00CC33,
+		0xFF00CC00,
+		0xFF0099FF,
+		0xFF0099CC,
+		0xFF009999,
+		0xFF009966,
+		0xFF009933,
+		0xFF009900,
+		0xFF0066FF,
+		0xFF0066CC,
+		0xFF006699,
+		0xFF006666,
+		0xFF006633,
+		0xFF006600,
+		0xFF0033FF,
+		0xFF0033CC,
+		0xFF003399,
+		0xFF003366,
+		0xFF003333,
+		0xFF003300,
+		0xFF0000FF,
+		0xFF0000CC,
+		0xFF000099,
+		0xFF000066,
+		0xFF000033,
+		0xFFEE0000,
+		0xFFDD0000,
+		0xFFBB0000,
+		0xFFAA0000,
+		0xFF880000,
+		0xFF770000,
+		0xFF550000,
+		0xFF440000,
+		0xFF220000,
+		0xFF110000,
+		0xFF00EE00,
+		0xFF00DD00,
+		0xFF00BB00,
+		0xFF00AA00,
+		0xFF008800,
+		0xFF007700,
+		0xFF005500,
+		0xFF004400,
+		0xFF002200,
+		0xFF001100,
+		0xFF0000EE,
+		0xFF0000DD,
+		0xFF0000BB,
+		0xFF0000AA,
+		0xFF000088,
+		0xFF000077,
+		0xFF000055,
+		0xFF000044,
+		0xFF000022,
+		0xFF000011,
+		0xFFEEEEEE,
+		0xFFDDDDDD,
+		0xFFBBBBBB,
+		0xFFAAAAAA,
+		0xFF888888,
+		0xFF777777,
+		0xFF555555,
+		0xFF444444,
+		0xFF222222,
+		0xFF111111,
+		0xFF000000
+	};
+
+	private static void decode1BPPImage(IcnsType imageType, byte[] imageData,
+			BufferedImage bufferedImage)
+	{
+		int position = 0;
+		int bitsLeft = 0;
+		int value = 0;
+		for (int y = 0; y < imageType.getHeight(); y++)
+		{
+			for (int x = 0; x < imageType.getWidth(); x++)
+			{
+				if (bitsLeft == 0)
+				{
+					value = 0xff & imageData[position++];
+					bitsLeft = 8;
+				}
+				int argb;
+				if ((value & 0x80) != 0)
+					argb = 0xff000000;
+				else
+					argb = 0xffffffff;
+				value <<= 1;
+				bitsLeft--;
+				bufferedImage.setRGB(x, y, argb);
+			}
+		}
+	}
+
+	private static void decode4BPPImage(IcnsType imageType, byte[] imageData,
+			BufferedImage bufferedImage)
+	{
+		int i = 0;
+		boolean visited = false;
+		for (int y = 0; y < imageType.getHeight(); y++)
+		{
+			for (int x = 0; x < imageType.getWidth(); x++)
+			{
+				int index;
+				if (!visited)
+					index = 0xf & (imageData[i] >> 4);
+				else
+					index = 0xf & imageData[i++];
+				visited = !visited;
+				bufferedImage.setRGB(x, y, palette_4bpp[index]);
+			}
+		}
+	}
+
+	private static void decode8BPPImage(IcnsType imageType, byte[] imageData,
+			BufferedImage bufferedImage)
+	{
+		for (int y = 0; y < imageType.getHeight(); y++)
+		{
+			for (int x = 0; x < imageType.getWidth(); x++)
+			{
+				int index = 0xff & imageData[y*imageType.getWidth() + x];
+				bufferedImage.setRGB(x, y, palette_8bpp[index]);
+			}
+		}
+	}
+
+	private static void decode32BPPImage(IcnsType imageType, byte[] imageData,
+			BufferedImage bufferedImage)
+	{
+		for (int y = 0; y < imageType.getHeight(); y++)
+		{
+			for (int x = 0; x < imageType.getWidth(); x++)
+			{
+				int argb = 0xff000000 /* the "alpha" is ignored */ |
+						((0xff & imageData[4*(y*imageType.getWidth() + x) + 1]) << 16) |
+						((0xff & imageData[4*(y*imageType.getWidth() + x) + 2]) << 8) |
+						(0xff & imageData[4*(y*imageType.getWidth() + x) + 3]);
+				bufferedImage.setRGB(x, y, argb);
+			}
+		}
+	}
+
+	private static void apply1BPPMask(byte[] maskData, BufferedImage bufferedImage) throws ImageReadException
+	{
+		int position = 0;
+		int bitsLeft = 0;
+		int value = 0;
+
+		// 1 bit icon types have image data followed by mask data in the same entry
+		int totalBytes = (bufferedImage.getWidth() * bufferedImage.getHeight() + 7) / 8;
+		if (maskData.length >= 2*totalBytes)
+			position = totalBytes;
+		else
+			throw new ImageReadException("1 BPP mask underrun parsing ICNS file");
+
+		for (int y = 0; y < bufferedImage.getHeight(); y++)
+		{
+			for (int x = 0; x < bufferedImage.getWidth(); x++)
+			{
+				if (bitsLeft == 0)
+				{
+					value = 0xff & maskData[position++];
+					bitsLeft = 8;
+				}
+				int alpha;
+				if ((value & 0x80) != 0)
+					alpha = 0xff;
+				else
+					alpha = 0x00;
+				value <<= 1;
+				bitsLeft--;
+				bufferedImage.setRGB(x, y, (alpha << 24) |
+					(0xffffff & bufferedImage.getRGB(x, y)));
+			}
+		}
+	}
+
+	private static void apply8BPPMask(byte[] maskData, BufferedImage bufferedImage)
+	{
+		for (int y = 0; y < bufferedImage.getHeight(); y++)
+		{
+			for (int x = 0; x < bufferedImage.getWidth(); x++)
+			{
+				int alpha = 0xff & maskData[y*bufferedImage.getWidth() + x];
+				bufferedImage.setRGB(x, y, (alpha << 24) |
+					(0xffffff & bufferedImage.getRGB(x, y)));
+			}
+		}
+	}
+
+	public static ArrayList decodeAllImages(IcnsImageParser.IcnsElement[] icnsElements)
+			throws ImageReadException, IOException
+	{
+		ArrayList result = new ArrayList();
+		for (int i = 0; i < icnsElements.length; i++)
+		{
+			IcnsImageParser.IcnsElement imageElement = icnsElements[i];
+			IcnsType imageType = IcnsType.findImageType(imageElement.type);
+			if (imageType == null)
+				continue;
+
+			IcnsType maskType = null;
+			IcnsImageParser.IcnsElement maskElement = null;
+			if (imageType.hasMask())
+			{
+				maskType = imageType;
+				maskElement = imageElement;
+			}
+			else
+			{
+				maskType = IcnsType.find8BPPMaskType(imageType);
+				if (maskType != null)
+				{
+					for (int j = 0; j < icnsElements.length; j++)
+					{
+						if (icnsElements[j].type == maskType.getType())
+						{
+							maskElement = icnsElements[j];
+							break;
+						}
+					}
+				}
+				if (maskElement == null)
+				{
+					maskType = IcnsType.find1BPPMaskType(imageType);
+					if (maskType != null)
+					{
+						for (int j = 0; j < icnsElements.length; j++)
+						{
+							if (icnsElements[j].type == maskType.getType())
+							{
+								maskElement = icnsElements[j];
+								break;
+							}
+						}
+					}
+				}
+			}
+
+			// FIXME: don't skip these when JPEG 2000 support is added:
+			if (imageType == IcnsType.ICNS_256x256_32BIT_ARGB_IMAGE ||
+				imageType == IcnsType.ICNS_512x512_32BIT_ARGB_IMAGE)
+				continue;
+
+			int expectedSize = (imageType.getWidth()*imageType.getHeight()*
+					imageType.getBitsPerPixel() + 7) / 8;
+			byte[] imageData;
+			if (imageElement.data.length < expectedSize)
+			{
+				if (imageType.getBitsPerPixel() == 32)
+				{
+					imageData = Rle24Compression.decompress(imageType.getWidth(),
+							imageType.getHeight(), imageElement.data);
+				}
+				else
+					throw new ImageReadException(
+							"Short image data but not a 32 bit compressed type");
+			}
+			else
+				imageData = imageElement.data;
+
+			BufferedImage bufferedImage = new BufferedImage(imageType.getWidth(),
+					imageType.getHeight(), BufferedImage.TYPE_INT_ARGB);
+			switch (imageType.getBitsPerPixel())
+			{
+				case 1:
+					decode1BPPImage(imageType, imageData, bufferedImage);
+					break;
+				case 4:
+					decode4BPPImage(imageType, imageData, bufferedImage);
+					break;
+				case 8:
+					decode8BPPImage(imageType, imageData, bufferedImage);
+					break;
+				case 32:
+					decode32BPPImage(imageType, imageData, bufferedImage);
+					break;
+				default:
+					throw new ImageReadException(
+						"Unsupported bit depth " + imageType.getBitsPerPixel());
+			}
+
+			if (maskElement != null)
+			{
+				if (maskType.getBitsPerPixel() == 1)
+					apply1BPPMask(maskElement.data, bufferedImage);
+				else if (maskType.getBitsPerPixel() == 8)
+					apply8BPPMask(maskElement.data, bufferedImage);
+				else
+					throw new ImageReadException("Unsupport mask bit depth " +
+							maskType.getBitsPerPixel());
+			}
+
+			result.add(bufferedImage);
+		}
+		return result;
+	}
+}
\ No newline at end of file

Propchange: commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/icns/IcnsDecoder.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/icns/IcnsImageParser.java
URL: http://svn.apache.org/viewvc/commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/icns/IcnsImageParser.java?rev=1000270&view=auto
==============================================================================
--- commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/icns/IcnsImageParser.java (added)
+++ commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/icns/IcnsImageParser.java Wed Sep 22 23:09:22 2010
@@ -0,0 +1,381 @@
+/*
+ * 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.icns;
+
+import java.awt.Dimension;
+import java.awt.image.BufferedImage;
+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.HashMap;
+import java.util.Map;
+
+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.BinaryOutputStream;
+import org.apache.sanselan.common.IImageMetadata;
+import org.apache.sanselan.common.byteSources.ByteSource;
+import org.apache.sanselan.util.Debug;
+import org.apache.sanselan.util.ParamMap;
+
+public class IcnsImageParser extends ImageParser
+{
+	public static final int ICNS_MAGIC = IcnsType.typeAsInt("icns");
+
+	public IcnsImageParser()
+	{
+		super.setByteOrder(BYTE_ORDER_MSB);
+	}
+
+	public String getName()
+	{
+		return "icns-Custom";
+	}
+
+	public String getDefaultExtension()
+	{
+		return DEFAULT_EXTENSION;
+	}
+
+	private static final String DEFAULT_EXTENSION = ".icns";
+
+	private static final String ACCEPTED_EXTENSIONS[] = {
+			".icns",
+	};
+
+	protected String[] getAcceptedExtensions()
+	{
+		return ACCEPTED_EXTENSIONS;
+	}
+
+	protected ImageFormat[] getAcceptedTypes()
+	{
+		return new ImageFormat[]{
+			ImageFormat.IMAGE_FORMAT_ICNS
+		};
+	}
+
+	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
+	{
+		// make copy of params; we'll clear keys as we consume them.
+		params = (params == null) ? new HashMap() : new HashMap(params);
+		
+		boolean verbose = ParamMap.getParamBoolean(params, PARAM_KEY_VERBOSE,
+				false);
+
+		if (params.containsKey(PARAM_KEY_VERBOSE))
+			params.remove(PARAM_KEY_VERBOSE);
+
+		if (params.size() > 0)
+		{
+			Object firstKey = params.keySet().iterator().next();
+			throw new ImageReadException("Unknown parameter: " + firstKey);
+		}
+
+		IcnsContents contents = readImage(byteSource);
+		ArrayList images = IcnsDecoder.decodeAllImages(contents.icnsElements);
+		if (images.isEmpty())
+			throw new ImageReadException("No icons in ICNS file");
+		BufferedImage image0 = (BufferedImage) images.get(0);
+		return new ImageInfo("Icns", 32, new ArrayList(), ImageFormat.IMAGE_FORMAT_ICNS,
+				"ICNS Apple Icon Image", image0.getHeight(), "image/x-icns", images.size(),
+				0, 0, 0, 0, image0.getWidth(), false, true, false, ImageInfo.COLOR_TYPE_RGB,
+				ImageInfo.COMPRESSION_ALGORITHM_UNKNOWN);
+	}
+
+	public Dimension getImageSize(ByteSource byteSource,
+			Map params)
+			throws ImageReadException, IOException
+	{
+		// make copy of params; we'll clear keys as we consume them.
+		params = (params == null) ? new HashMap() : new HashMap(params);
+
+		boolean verbose = ParamMap.getParamBoolean(params, PARAM_KEY_VERBOSE,
+				false);
+
+		if (params.containsKey(PARAM_KEY_VERBOSE))
+			params.remove(PARAM_KEY_VERBOSE);
+
+		if (params.size() > 0)
+		{
+			Object firstKey = params.keySet().iterator().next();
+			throw new ImageReadException("Unknown parameter: " + firstKey);
+		}
+
+		IcnsContents contents = readImage(byteSource);
+		ArrayList images = IcnsDecoder.decodeAllImages(contents.icnsElements);
+		if (images.isEmpty())
+			throw new ImageReadException("No icons in ICNS file");
+		BufferedImage image0 = (BufferedImage) images.get(0);
+		return new Dimension(image0.getWidth(), image0.getHeight());
+	}
+
+	public byte[] getICCProfileBytes(ByteSource byteSource,
+			Map params)
+			throws ImageReadException, IOException
+	{
+		return null;
+	}
+
+	private static class IcnsHeader
+	{
+		public final int magic; // Magic literal (4 bytes), always "icns"
+		public final int fileSize; // Length of file (4 bytes), in bytes.
+
+		public IcnsHeader(final int magic, final int fileSize)
+		{
+			this.magic = magic;
+			this.fileSize = fileSize;
+		}
+
+		public void dump(PrintWriter pw)
+		{
+			pw.println("IcnsHeader");
+			pw.println("Magic: 0x" + Integer.toHexString(magic) +
+					" (" + IcnsType.describeType(magic) + ")");
+			pw.println("FileSize: " + fileSize);
+			pw.println("");
+		}
+	}
+
+	private IcnsHeader readIcnsHeader(InputStream is)
+			throws ImageReadException, IOException
+	{
+		int Magic = read4Bytes("Magic", is, "Not a Valid ICNS File");
+		int FileSize = read4Bytes("FileSize", is, "Not a Valid ICNS File");
+
+		if (Magic != ICNS_MAGIC)
+			throw new ImageReadException("Not a Valid ICNS File: " +
+					"magic is 0x" + Integer.toHexString(Magic));
+
+		return new IcnsHeader(Magic, FileSize);
+	}
+
+	public static class IcnsElement
+	{
+		public final int type;
+		public final int elementSize;
+		public final byte[] data;
+
+		public IcnsElement(final int type, final int elementSize, byte[] data)
+		{
+			this.type = type;
+			this.elementSize = elementSize;
+			this.data = data;
+		}
+
+		public void dump(PrintWriter pw)
+		{
+			pw.println("IcnsElement");
+			IcnsType icnsType = IcnsType.findAnyType(type);
+			String typeDescription;
+			if (icnsType == null)
+				typeDescription = "";
+			else
+				typeDescription = " " + icnsType.toString();
+			pw.println("Type: 0x" + Integer.toHexString(type) +
+					" (" + IcnsType.describeType(type) + ")" +
+					typeDescription);
+			pw.println("ElementSize: " + elementSize);
+			pw.println("");
+		}
+	}
+
+	private IcnsElement readIcnsElement(InputStream is) throws ImageReadException,
+			IOException
+	{
+		int type = read4Bytes("Type", is, "Not a Valid ICNS File"); // Icon type (4 bytes)
+		int elementSize = read4Bytes("ElementSize", is, "Not a Valid ICNS File"); // Length of data (4 bytes), in bytes, including this header
+		byte[] data = readByteArray("Data", elementSize - 8, is, "Not a Valid ICNS File");
+
+		return new IcnsElement(type, elementSize, data);
+	}
+
+	private static class IcnsContents
+	{
+		public final IcnsHeader icnsHeader;
+		public final IcnsElement icnsElements[];
+
+		public IcnsContents(final IcnsHeader icnsHeader,
+				final IcnsElement[] icnsElements)
+		{
+			super();
+			this.icnsHeader = icnsHeader;
+			this.icnsElements = icnsElements;
+		}
+	}
+
+	private IcnsContents readImage(ByteSource byteSource)
+			throws ImageReadException, IOException
+	{
+		InputStream is = null;
+		try
+		{
+			is = byteSource.getInputStream();
+			IcnsHeader icnsHeader = readIcnsHeader(is);
+
+			ArrayList icnsElementList = new ArrayList();
+			for (int remainingSize = icnsHeader.fileSize - 8;
+				remainingSize > 0; )
+			{
+				IcnsElement icnsElement = readIcnsElement(is);
+				icnsElementList.add(icnsElement);
+				remainingSize -= icnsElement.elementSize;
+			}
+
+			IcnsElement[] icnsElements = new IcnsElement[icnsElementList.size()];
+			for (int i = 0; i < icnsElements.length; i++)
+				icnsElements[i] = (IcnsElement) icnsElementList.get(i);
+
+			return new IcnsContents(icnsHeader, icnsElements);
+		}
+		finally
+		{
+			try
+			{
+				is.close();
+			}
+			catch (Exception e)
+			{
+				Debug.debug(e);
+			}
+		}
+	}
+
+	public boolean dumpImageFile(PrintWriter pw, ByteSource byteSource)
+			throws ImageReadException, IOException
+	{
+		IcnsContents icnsContents = readImage(byteSource);
+		icnsContents.icnsHeader.dump(pw);
+		for (int i = 0; i < icnsContents.icnsElements.length; i++)
+			icnsContents.icnsElements[i].dump(pw);
+		return true;
+	}
+
+	public final BufferedImage getBufferedImage(ByteSource byteSource,
+			Map params) throws ImageReadException, IOException
+	{
+		IcnsContents icnsContents = readImage(byteSource);
+		ArrayList result = IcnsDecoder.decodeAllImages(icnsContents.icnsElements);
+		if (result.size() > 0)
+			return (BufferedImage) result.get(0);
+		else
+			throw new ImageReadException("No icons in ICNS file");
+	}
+
+	public ArrayList getAllBufferedImages(ByteSource byteSource)
+			throws ImageReadException, IOException
+	{
+		IcnsContents icnsContents = readImage(byteSource);
+		return IcnsDecoder.decodeAllImages(icnsContents.icnsElements);
+	}
+
+	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 = (params == null) ? new HashMap() : new HashMap(params);
+
+		// clear format key.
+		if (params.containsKey(PARAM_KEY_FORMAT))
+			params.remove(PARAM_KEY_FORMAT);
+
+		if (params.size() > 0)
+		{
+			Object firstKey = params.keySet().iterator().next();
+			throw new ImageWriteException("Unknown parameter: " + firstKey);
+		}
+
+		IcnsType imageType;
+		if (src.getWidth() == 16 && src.getHeight() == 16)
+			imageType = IcnsType.ICNS_16x16_32BIT_IMAGE;
+		else if (src.getWidth() == 32 && src.getHeight() == 32)
+			imageType = IcnsType.ICNS_32x32_32BIT_IMAGE;
+		else if (src.getWidth() == 48 && src.getHeight() == 48)
+			imageType = IcnsType.ICNS_48x48_32BIT_IMAGE;
+		else if (src.getWidth() == 128 && src.getHeight() == 128)
+			imageType = IcnsType.ICNS_128x128_32BIT_IMAGE;
+		else
+			throw new ImageWriteException("Invalid/unsupported source width " +
+					src.getWidth() + " and height " + src.getHeight());
+
+		BinaryOutputStream bos = new BinaryOutputStream(os, BYTE_ORDER_BIG_ENDIAN);
+		bos.write4Bytes(ICNS_MAGIC);
+		bos.write4Bytes(4 + 4 + 4 + 4 + 4*imageType.getWidth()*imageType.getHeight() +
+				4 + 4 + imageType.getWidth()*imageType.getHeight());
+
+		bos.write4Bytes(imageType.getType());
+		bos.write4Bytes(4 + 4 + 4*imageType.getWidth()*imageType.getHeight());
+		for (int y = 0; y < src.getHeight(); y++)
+		{
+			for (int x = 0; x < src.getWidth(); x++)
+			{
+				int argb = src.getRGB(x, y);
+				bos.write(0);
+				bos.write(argb >> 16);
+				bos.write(argb >> 8);
+				bos.write(argb);
+			}
+		}
+
+		IcnsType maskType = IcnsType.find8BPPMaskType(imageType);
+		bos.write4Bytes(maskType.getType());
+		bos.write4Bytes(4 + 4 + imageType.getWidth()*imageType.getWidth());
+		for (int y = 0; y < src.getHeight(); y++)
+		{
+			for (int x = 0; x < src.getWidth(); x++)
+			{
+				int argb = src.getRGB(x, y);
+				bos.write(argb >> 24);
+			}
+		}
+	}
+
+	/**
+	 * 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/icns/IcnsImageParser.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/icns/IcnsType.java
URL: http://svn.apache.org/viewvc/commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/icns/IcnsType.java?rev=1000270&view=auto
==============================================================================
--- commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/icns/IcnsType.java (added)
+++ commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/icns/IcnsType.java Wed Sep 22 23:09:22 2010
@@ -0,0 +1,231 @@
+/*
+ * 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.icns;
+
+import java.io.UnsupportedEncodingException;
+
+
+public class IcnsType
+{
+	private final int type;
+	private final int width;
+	private final int height;
+	private final int bitsPerPixel;
+	private final boolean hasMask;
+
+	public static final IcnsType ICNS_16x12_1BIT_IMAGE_AND_MASK =
+		new IcnsType("icm#", 16, 12, 1, true);
+	public static final IcnsType ICNS_16x12_4BIT_IMAGE =
+		new IcnsType("icm4", 16, 12, 4, false);
+	public static final IcnsType ICNS_16x12_8BIT_IMAGE =
+		new IcnsType("icm8", 16, 12, 8, false);
+
+	public static final IcnsType ICNS_16x16_8BIT_MASK =
+		new IcnsType("s8mk", 16, 16, 8, true);
+	public static final IcnsType ICNS_16x16_1BIT_IMAGE_AND_MASK =
+		new IcnsType("ics#", 16, 16, 1, true);
+	public static final IcnsType ICNS_16x16_4BIT_IMAGE =
+		new IcnsType("ics4", 16, 16, 4, false);
+	public static final IcnsType ICNS_16x16_8BIT_IMAGE =
+		new IcnsType("ics8", 16, 16, 8, false);
+	public static final IcnsType ICNS_16x16_32BIT_IMAGE =
+		new IcnsType("is32", 16, 16, 32, false);
+
+	public static final IcnsType ICNS_32x32_8BIT_MASK =
+		new IcnsType("l8mk", 32, 32, 8, true);
+	public static final IcnsType ICNS_32x32_1BIT_IMAGE_AND_MASK =
+		new IcnsType("ICN#", 32, 32, 1, true);
+	public static final IcnsType ICNS_32x32_4BIT_IMAGE =
+		new IcnsType("icl4", 32, 32, 4, false);
+	public static final IcnsType ICNS_32x32_8BIT_IMAGE =
+		new IcnsType("icl8", 32, 32, 8, false);
+	public static final IcnsType ICNS_32x32_32BIT_IMAGE =
+		new IcnsType("il32", 32, 32, 32, false);
+
+	public static final IcnsType ICNS_48x48_8BIT_MASK =
+		new IcnsType("h8mk", 48, 48, 8, true);
+	public static final IcnsType ICNS_48x48_1BIT_IMAGE_AND_MASK =
+		new IcnsType("ich#", 48, 48, 1, true);
+	public static final IcnsType ICNS_48x48_4BIT_IMAGE =
+		new IcnsType("ich4", 48, 48, 4, false);
+	public static final IcnsType ICNS_48x48_8BIT_IMAGE =
+		new IcnsType("ich8", 48, 48, 8, false);
+	public static final IcnsType ICNS_48x48_32BIT_IMAGE =
+		new IcnsType("ih32", 48, 48, 32, false);
+
+	public static final IcnsType ICNS_128x128_8BIT_MASK =
+		new IcnsType("t8mk", 128, 128, 8, true);
+	public static final IcnsType ICNS_128x128_32BIT_IMAGE =
+		new IcnsType("it32", 128, 128, 32, false);
+
+	public static final IcnsType ICNS_256x256_32BIT_ARGB_IMAGE =
+		new IcnsType("ic08", 256, 256, 32, false);
+
+	public static final IcnsType ICNS_512x512_32BIT_ARGB_IMAGE =
+		new IcnsType("ic09", 512, 512, 32, false);
+
+	private static final IcnsType[] allImageTypes =
+	{
+		ICNS_16x12_1BIT_IMAGE_AND_MASK, ICNS_16x12_4BIT_IMAGE, ICNS_16x12_8BIT_IMAGE,
+		ICNS_16x16_1BIT_IMAGE_AND_MASK, ICNS_16x16_4BIT_IMAGE, ICNS_16x16_8BIT_IMAGE, ICNS_16x16_32BIT_IMAGE,
+		ICNS_32x32_1BIT_IMAGE_AND_MASK, ICNS_32x32_4BIT_IMAGE, ICNS_32x32_8BIT_IMAGE, ICNS_32x32_32BIT_IMAGE,
+		ICNS_48x48_1BIT_IMAGE_AND_MASK, ICNS_48x48_4BIT_IMAGE, ICNS_48x48_8BIT_IMAGE, ICNS_48x48_32BIT_IMAGE,
+		ICNS_128x128_32BIT_IMAGE,
+		ICNS_256x256_32BIT_ARGB_IMAGE,
+		ICNS_512x512_32BIT_ARGB_IMAGE
+	};
+
+	private static final IcnsType[] allMaskTypes =
+	{
+		ICNS_16x12_1BIT_IMAGE_AND_MASK,
+		ICNS_16x16_1BIT_IMAGE_AND_MASK, ICNS_16x16_8BIT_MASK,
+		ICNS_32x32_1BIT_IMAGE_AND_MASK, ICNS_32x32_8BIT_MASK,
+		ICNS_48x48_1BIT_IMAGE_AND_MASK, ICNS_48x48_8BIT_MASK,
+		ICNS_128x128_8BIT_MASK
+	};
+
+	private IcnsType(String type, int width, int height, int bitsPerPixel, boolean hasMask)
+	{
+		this.type = typeAsInt(type);
+		this.width = width;
+		this.height = height;
+		this.bitsPerPixel = bitsPerPixel;
+		this.hasMask = hasMask;
+	}
+
+	public int getType()
+	{
+		return type;
+	}
+
+	public int getWidth()
+	{
+		return width;
+	}
+
+	public int getHeight()
+	{
+		return height;
+	}
+
+	public int getBitsPerPixel()
+	{
+		return bitsPerPixel;
+	}
+
+	public boolean hasMask()
+	{
+		return hasMask;
+	}
+
+	public String toString()
+	{
+		return getClass().getName() + "[" +
+				"width=" + width + "," +
+				"height=" + height + "," +
+				"bpp=" + bitsPerPixel + "," +
+				"hasMask=" + hasMask + "]";
+	}
+
+	public static IcnsType findAnyType(int type)
+	{
+		for (int i = 0; i < allImageTypes.length; i++)
+		{
+			if (allImageTypes[i].getType() == type)
+				return allImageTypes[i];
+		}
+		for (int i = 0; i < allMaskTypes.length; i++)
+		{
+			if (allMaskTypes[i].getType() == type)
+				return allMaskTypes[i];
+		}
+		return null;
+	}
+
+	public static IcnsType findImageType(int type)
+	{
+		for (int i = 0; i < allImageTypes.length; i++)
+		{
+			if (allImageTypes[i].getType() == type)
+				return allImageTypes[i];
+		}
+		return null;
+	}
+
+	public static IcnsType find8BPPMaskType(IcnsType imageType)
+	{
+		for (int i = 0; i < allMaskTypes.length; i++)
+		{
+			if (allMaskTypes[i].getBitsPerPixel() == 8 &&
+				allMaskTypes[i].getWidth() == imageType.getWidth() &&
+				allMaskTypes[i].getHeight() == imageType.getHeight())
+			{
+				return allMaskTypes[i];
+			}
+		}
+		return null;
+	}
+
+	public static IcnsType find1BPPMaskType(IcnsType imageType)
+	{
+		for (int i = 0; i < allMaskTypes.length; i++)
+		{
+			if (allMaskTypes[i].getBitsPerPixel() == 1 &&
+				allMaskTypes[i].getWidth() == imageType.getWidth() &&
+				allMaskTypes[i].getHeight() == imageType.getHeight())
+			{
+				return allMaskTypes[i];
+			}
+		}
+		return null;
+	}
+
+	public static int typeAsInt(String type)
+	{
+		byte[] bytes = null;
+		try
+		{
+			bytes = type.getBytes("US-ASCII");
+		}
+		catch (UnsupportedEncodingException cannotHappen)
+		{
+		}
+		if (bytes.length != 4)
+			throw new IllegalArgumentException("Invalid ICNS type");
+		return ((0xff & bytes[0]) << 24) |
+			((0xff & bytes[1]) << 16) |
+			((0xff & bytes[2]) << 8) |
+			(0xff & bytes[3]);
+	}
+
+	public static String describeType(int type)
+	{
+		byte[] bytes = new byte[4];
+		bytes[0] = (byte)(0xff & (type >> 24));
+		bytes[1] = (byte)(0xff & (type >> 16));
+		bytes[2] = (byte)(0xff & (type >> 8));
+		bytes[3] = (byte)(0xff & type);
+		try
+		{
+			return new String(bytes, "US-ASCII");
+		}
+		catch (UnsupportedEncodingException cannotHappen)
+		{
+		}
+		return null;
+	}
+}

Propchange: commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/icns/IcnsType.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/icns/Rle24Compression.java
URL: http://svn.apache.org/viewvc/commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/icns/Rle24Compression.java?rev=1000270&view=auto
==============================================================================
--- commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/icns/Rle24Compression.java (added)
+++ commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/icns/Rle24Compression.java Wed Sep 22 23:09:22 2010
@@ -0,0 +1,68 @@
+/*
+ * 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.icns;
+
+class Rle24Compression
+{
+	public static byte[] decompress(int width, int height, byte[] data)
+	{
+		final int pixelCount = width * height;
+		final byte[] result = new byte[4 * pixelCount];
+
+		// Several ICNS parsers advance by 4 bytes here:
+		// http://code.google.com/p/icns2png/ - when the width is >= 128
+		// http://icns.sourceforge.net/ - when those 4 bytes are all zero
+		//
+		// A scan of all .icns files on MacOS shows that
+		// all 128x128 images indeed start with 4 zeroes,
+		// while all smaller images don't.
+		// However it is dangerous to assume
+		// that 4 initial zeroes always need to be skipped,
+		// because they could encode valid pixels on smaller images.
+		// So always skip on 128x128, and never skip on anything else.
+		int dataPos = 0;
+		if (width >= 128 && height >= 128)
+			dataPos = 4;
+
+		// argb, band by band in 3 passes, with no alpha
+		for (int band = 1; band <= 3; band++)
+		{
+			int remaining = pixelCount;
+			int resultPos = 0;
+			while (remaining > 0)
+			{
+				if ((data[dataPos] & 0x80) != 0)
+				{
+					int count = (0xff & data[dataPos]) - 125;
+					for (int i = 0; i < count; i++)
+						result[band + 4*(resultPos++)] = data[dataPos + 1];
+					dataPos += 2;
+					remaining -= count;
+				}
+				else
+				{
+					int count = (0xff & data[dataPos]) + 1;
+					dataPos++;
+					for (int i = 0; i < count; i++)
+						result[band + 4*(resultPos++)] = data[dataPos++];
+					remaining -= count;
+				}
+			}
+		}
+		return result;
+	}
+}

Propchange: commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/icns/Rle24Compression.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: commons/proper/sanselan/trunk/src/test/data/images/icns/1/info.txt
URL: http://svn.apache.org/viewvc/commons/proper/sanselan/trunk/src/test/data/images/icns/1/info.txt?rev=1000270&view=auto
==============================================================================
--- commons/proper/sanselan/trunk/src/test/data/images/icns/1/info.txt (added)
+++ commons/proper/sanselan/trunk/src/test/data/images/icns/1/info.txt Wed Sep 22 23:09:22 2010
@@ -0,0 +1,3 @@
+These icons are taken from http://openiconlibrary.sourceforge.net, the Mac icon package,
+file poedit.icns, which is listed as public domain.
+Contributed to the project by Damjan Jovanovic.

Propchange: commons/proper/sanselan/trunk/src/test/data/images/icns/1/info.txt
------------------------------------------------------------------------------
    svn:eol-style = native

Added: commons/proper/sanselan/trunk/src/test/data/images/icns/1/poedit16x16.icns
URL: http://svn.apache.org/viewvc/commons/proper/sanselan/trunk/src/test/data/images/icns/1/poedit16x16.icns?rev=1000270&view=auto
==============================================================================
Binary file - no diff available.

Propchange: commons/proper/sanselan/trunk/src/test/data/images/icns/1/poedit16x16.icns
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Added: commons/proper/sanselan/trunk/src/test/data/images/icns/1/poedit32x32.icns
URL: http://svn.apache.org/viewvc/commons/proper/sanselan/trunk/src/test/data/images/icns/1/poedit32x32.icns?rev=1000270&view=auto
==============================================================================
Binary file - no diff available.

Propchange: commons/proper/sanselan/trunk/src/test/data/images/icns/1/poedit32x32.icns
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Added: commons/proper/sanselan/trunk/src/test/data/images/icns/1/poedit48x48.icns
URL: http://svn.apache.org/viewvc/commons/proper/sanselan/trunk/src/test/data/images/icns/1/poedit48x48.icns?rev=1000270&view=auto
==============================================================================
Binary file - no diff available.

Propchange: commons/proper/sanselan/trunk/src/test/data/images/icns/1/poedit48x48.icns
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Modified: commons/proper/sanselan/trunk/src/test/java/org/apache/sanselan/SanselanGuessFormatTest.java
URL: http://svn.apache.org/viewvc/commons/proper/sanselan/trunk/src/test/java/org/apache/sanselan/SanselanGuessFormatTest.java?rev=1000270&r1=1000269&r2=1000270&view=diff
==============================================================================
--- commons/proper/sanselan/trunk/src/test/java/org/apache/sanselan/SanselanGuessFormatTest.java (original)
+++ commons/proper/sanselan/trunk/src/test/java/org/apache/sanselan/SanselanGuessFormatTest.java Wed Sep 22 23:09:22 2010
@@ -26,6 +26,7 @@ public class SanselanGuessFormatTest ext
     public static final String BMP_IMAGE_FILE = "bmp\\1\\Oregon Scientific DS6639 - DSC_0307 - small.bmp";
     public static final String PNG_IMAGE_FILE = "png\\1\\Oregon Scientific DS6639 - DSC_0307 - small.png";
     public static final String GIF_IMAGE_FILE = "gif\\1\\Oregon Scientific DS6639 - DSC_0307 - small.gif";
+    public static final String ICNS_IMAGE_FILE = "icns\\1\\poedit48x48.icns";
     public static final String ICO_IMAGE_FILE = "ico\\1\\Oregon Scientific DS6639 - DSC_0307 - small.ico";
     public static final String TIFF_IMAGE_FILE = "tiff\\1\\Oregon Scientific DS6639 - DSC_0307 - small.tif";
     public static final String JPEG_IMAGE_FILE = "jpg\\1\\Oregon Scientific DS6639 - DSC_0307 - small.jpg";
@@ -38,6 +39,7 @@ public class SanselanGuessFormatTest ext
     public void testGuess_all() throws IOException, ImageReadException {
         testGuess(ImageFormat.IMAGE_FORMAT_PNG, PNG_IMAGE_FILE);
         testGuess(ImageFormat.IMAGE_FORMAT_GIF, GIF_IMAGE_FILE);
+        testGuess(ImageFormat.IMAGE_FORMAT_ICNS, ICNS_IMAGE_FILE);
         // TODO(cmchen): add ability to sniff ICOs if possible.
         // testGuess(ImageFormat.IMAGE_FORMAT_ICO, ICO_IMAGE_FILE);
         testGuess(ImageFormat.IMAGE_FORMAT_TIFF, TIFF_IMAGE_FILE);

Added: commons/proper/sanselan/trunk/src/test/java/org/apache/sanselan/formats/icns/IcnsBaseTest.java
URL: http://svn.apache.org/viewvc/commons/proper/sanselan/trunk/src/test/java/org/apache/sanselan/formats/icns/IcnsBaseTest.java?rev=1000270&view=auto
==============================================================================
--- commons/proper/sanselan/trunk/src/test/java/org/apache/sanselan/formats/icns/IcnsBaseTest.java (added)
+++ commons/proper/sanselan/trunk/src/test/java/org/apache/sanselan/formats/icns/IcnsBaseTest.java Wed Sep 22 23:09:22 2010
@@ -0,0 +1,49 @@
+/*
+ * 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.icns;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+
+import org.apache.sanselan.ImageFormat;
+import org.apache.sanselan.ImageReadException;
+import org.apache.sanselan.Sanselan;
+import org.apache.sanselan.SanselanTest;
+
+public abstract class IcnsBaseTest extends SanselanTest
+{
+
+    private static boolean isIcns(File file) throws IOException, ImageReadException
+    {
+        ImageFormat format = Sanselan.guessFormat(file);
+        return format == ImageFormat.IMAGE_FORMAT_ICNS;
+    }
+
+    private static final ImageFilter IMAGE_FILTER = new ImageFilter() {
+        public boolean accept(File file) throws IOException, ImageReadException
+        {
+            return isIcns(file);
+        }
+    };
+
+    protected List getIcnsImages() throws IOException, ImageReadException
+    {
+        return getTestImages(IMAGE_FILTER);
+    }
+}

Propchange: commons/proper/sanselan/trunk/src/test/java/org/apache/sanselan/formats/icns/IcnsBaseTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: commons/proper/sanselan/trunk/src/test/java/org/apache/sanselan/formats/icns/IcnsReadTest.java
URL: http://svn.apache.org/viewvc/commons/proper/sanselan/trunk/src/test/java/org/apache/sanselan/formats/icns/IcnsReadTest.java?rev=1000270&view=auto
==============================================================================
--- commons/proper/sanselan/trunk/src/test/java/org/apache/sanselan/formats/icns/IcnsReadTest.java (added)
+++ commons/proper/sanselan/trunk/src/test/java/org/apache/sanselan/formats/icns/IcnsReadTest.java Wed Sep 22 23:09:22 2010
@@ -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.icns;
+
+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 IcnsReadTest extends IcnsBaseTest
+{
+
+    public void test() throws IOException, ImageReadException
+    {
+        Debug.debug("start");
+
+        List images = getIcnsImages();
+        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/icns/IcnsReadTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: commons/proper/sanselan/trunk/src/test/java/org/apache/sanselan/formats/icns/IcnsRoundTripTest.java
URL: http://svn.apache.org/viewvc/commons/proper/sanselan/trunk/src/test/java/org/apache/sanselan/formats/icns/IcnsRoundTripTest.java?rev=1000270&view=auto
==============================================================================
--- commons/proper/sanselan/trunk/src/test/java/org/apache/sanselan/formats/icns/IcnsRoundTripTest.java (added)
+++ commons/proper/sanselan/trunk/src/test/java/org/apache/sanselan/formats/icns/IcnsRoundTripTest.java Wed Sep 22 23:09:22 2010
@@ -0,0 +1,481 @@
+/*
+ * 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.icns;
+
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import org.apache.sanselan.ImageReadException;
+import org.apache.sanselan.ImageWriteException;
+import org.apache.sanselan.Sanselan;
+import org.apache.sanselan.common.BinaryOutputStream;
+import org.apache.sanselan.util.Debug;
+import org.apache.sanselan.util.IOUtils;
+
+public class IcnsRoundTripTest extends IcnsBaseTest
+{
+    // 16x16 test image
+    private static final int[][] image = {
+        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+        {0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0},
+        {0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0},
+        {0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0},
+        {0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0},
+        {0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0},
+        {0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0},
+        {0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0},
+        {0,0,0,0,0,1,1,1,0,1,0,0,0,0,0,0},
+        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
+    };
+
+	public void test1BPPIconMaskVersus8BPPMask() throws ImageReadException, ImageWriteException, IOException
+	{
+		final int foreground = 0xff000000;
+		final int background = 0xff000000;
+		ByteArrayOutputStream baos = new ByteArrayOutputStream();
+		BinaryOutputStream bos = new BinaryOutputStream(baos,
+				BinaryOutputStream.BYTE_ORDER_BIG_ENDIAN);
+		bos.write4Bytes(IcnsImageParser.ICNS_MAGIC);
+		bos.write4Bytes(4 + 4 + 4 + 4 + 2*16*16/8 + 4 + 4 + 16*16);
+		bos.write4Bytes(IcnsType.ICNS_16x16_1BIT_IMAGE_AND_MASK.getType());
+		bos.write4Bytes(4 + 4 + 2*16*16/8);
+		// 1 BPP image - all black
+		for (int y = 0; y < 16; y++)
+		{
+			bos.write(0xff);
+			bos.write(0xff);
+		}
+		// 1 BPP mask - all opaque
+		for (int y = 0; y < 16; y++)
+		{
+			bos.write(0xff);
+			bos.write(0xff);
+		}
+		// 8 BPP alpha mask - partially transparent
+		bos.write4Bytes(IcnsType.ICNS_16x16_8BIT_MASK.getType());
+		bos.write4Bytes(4 + 4 + 16*16);
+		for (int y = 0; y < 16; y++)
+		{
+			for (int x = 0; x < 16; x++)
+			{
+				if (image[y][x] != 0)
+					bos.write(0xff);
+				else
+					bos.write(0x00);
+			}
+		}
+		bos.flush();
+		writeAndReadImageData("1bpp-image-mask-versus-8bpp-mask",
+				baos.toByteArray(), foreground, background);
+	}
+
+	public void test8BPPIcon8BPPMask() throws ImageReadException, ImageWriteException, IOException
+	{
+		final int foreground = 0xff000000;
+		final int background = 0x00cccccc;
+		ByteArrayOutputStream baos = new ByteArrayOutputStream();
+		BinaryOutputStream bos = new BinaryOutputStream(baos,
+				BinaryOutputStream.BYTE_ORDER_BIG_ENDIAN);
+		bos.write4Bytes(IcnsImageParser.ICNS_MAGIC);
+		bos.write4Bytes(4 + 4 + 4 + 4 + 16*16 + 4 + 4 + 16*16);
+		bos.write4Bytes(IcnsType.ICNS_16x16_8BIT_IMAGE.getType());
+		bos.write4Bytes(4 + 4 + 16*16);
+		// 8 BPP image
+		for (int y = 0; y < 16; y++)
+		{
+			for (int x = 0; x < 16; x++)
+			{
+				if (image[y][x] != 0)
+					bos.write(0xff);
+				else
+					bos.write(43);
+			}
+		}
+		// 8 BPP alpha mask
+		bos.write4Bytes(IcnsType.ICNS_16x16_8BIT_MASK.getType());
+		bos.write4Bytes(4 + 4 + 16*16);
+		for (int y = 0; y < 16; y++)
+		{
+			for (int x = 0; x < 16; x++)
+			{
+				if (image[y][x] != 0)
+					bos.write(0xff);
+				else
+					bos.write(0x00);
+			}
+		}
+		bos.flush();
+		writeAndReadImageData("8bpp-image-8bpp-mask", baos.toByteArray(), foreground, background);
+	}
+
+	public void test8BPPIcon8BPPMaskVersus1BPPMask()
+			throws ImageReadException, ImageWriteException, IOException
+	{
+		final int foreground = 0xff000000;
+		final int background = 0x00cccccc;
+		ByteArrayOutputStream baos = new ByteArrayOutputStream();
+		BinaryOutputStream bos = new BinaryOutputStream(baos,
+				BinaryOutputStream.BYTE_ORDER_BIG_ENDIAN);
+		bos.write4Bytes(IcnsImageParser.ICNS_MAGIC);
+		bos.write4Bytes(4 + 4 + 4 + 4 + 16*16 + 4 + 4 + 16*16 + 4 + 4 + 2*16*16/8);
+		bos.write4Bytes(IcnsType.ICNS_16x16_8BIT_IMAGE.getType());
+		bos.write4Bytes(4 + 4 + 16*16);
+		// 8 BPP image
+		for (int y = 0; y < 16; y++)
+		{
+			for (int x = 0; x < 16; x++)
+			{
+				if (image[y][x] != 0)
+					bos.write(0xff);
+				else
+					bos.write(43);
+			}
+		}
+		// 8 BPP alpha mask, some transparent
+		bos.write4Bytes(IcnsType.ICNS_16x16_8BIT_MASK.getType());
+		bos.write4Bytes(4 + 4 + 16*16);
+		for (int y = 0; y < 16; y++)
+		{
+			for (int x = 0; x < 16; x++)
+			{
+				if (image[y][x] != 0)
+					bos.write(0xff);
+				else
+					bos.write(0x00);
+			}
+		}
+		// 1 BPP mask
+		bos.write4Bytes(IcnsType.ICNS_16x16_1BIT_IMAGE_AND_MASK.getType());
+		bos.write4Bytes(4 + 4 + 2*16*16/8);
+		// 1 bit image
+		for (int y = 0; y < 16; y++)
+		{
+			for (int x = 0; x < 16; x += 8)
+			{
+				int eightBits = 0;
+				for (int pos = 0; pos < 8; pos++)
+				{
+					if (image[y][x+pos] != 0)
+						eightBits |= (1 << (7 - pos));
+				}
+				bos.write(eightBits);
+			}
+		}
+		// 1 bit mask, all opaque
+		for (int y = 0; y < 16; y++)
+		{
+			bos.write(0xff);
+			bos.write(0xff);
+		}
+		bos.flush();
+		writeAndReadImageData("8bpp-image-8bpp-mask-vs-1bpp-mask",
+				baos.toByteArray(), foreground, background);
+	}
+
+	public void test8BPPIcon1BPPMaskVersus8BPPMask()
+			throws ImageReadException, ImageWriteException, IOException
+	{
+		final int foreground = 0xff000000;
+		final int background = 0x00cccccc;
+		ByteArrayOutputStream baos = new ByteArrayOutputStream();
+		BinaryOutputStream bos = new BinaryOutputStream(baos,
+				BinaryOutputStream.BYTE_ORDER_BIG_ENDIAN);
+		bos.write4Bytes(IcnsImageParser.ICNS_MAGIC);
+		bos.write4Bytes(4 + 4 + 4 + 4 + 16*16 + 4 + 4 + 16*16 + 4 + 4 + 2*16*16/8);
+		bos.write4Bytes(IcnsType.ICNS_16x16_8BIT_IMAGE.getType());
+		bos.write4Bytes(4 + 4 + 16*16);
+		// 8 BPP image
+		for (int y = 0; y < 16; y++)
+		{
+			for (int x = 0; x < 16; x++)
+			{
+				if (image[y][x] != 0)
+					bos.write(0xff);
+				else
+					bos.write(43);
+			}
+		}
+		// 1 BPP mask
+		bos.write4Bytes(IcnsType.ICNS_16x16_1BIT_IMAGE_AND_MASK.getType());
+		bos.write4Bytes(4 + 4 + 2*16*16/8);
+		// 1 bit image
+		for (int y = 0; y < 16; y++)
+		{
+			for (int x = 0; x < 16; x += 8)
+			{
+				int eightBits = 0;
+				for (int pos = 0; pos < 8; pos++)
+				{
+					if (image[y][x+pos] != 0)
+						eightBits |= (1 << (7 - pos));
+				}
+				bos.write(eightBits);
+			}
+		}
+		// 1 bit mask, all opaque
+		for (int y = 0; y < 16; y++)
+		{
+			bos.write(0xff);
+			bos.write(0xff);
+		}
+		// 8 BPP alpha mask, some transparent
+		bos.write4Bytes(IcnsType.ICNS_16x16_8BIT_MASK.getType());
+		bos.write4Bytes(4 + 4 + 16*16);
+		for (int y = 0; y < 16; y++)
+		{
+			for (int x = 0; x < 16; x++)
+			{
+				if (image[y][x] != 0)
+					bos.write(0xff);
+				else
+					bos.write(0x00);
+			}
+		}
+		bos.flush();
+		writeAndReadImageData("8bpp-image-1bpp-mask-vs-8bpp-mask",
+				baos.toByteArray(), foreground, background);
+	}
+
+	public void test8BPPIconNoMask() throws ImageReadException, ImageWriteException, IOException
+	{
+		final int foreground = 0xff000000;
+		final int background = 0xffcccccc;
+		ByteArrayOutputStream baos = new ByteArrayOutputStream();
+		BinaryOutputStream bos = new BinaryOutputStream(baos,
+				BinaryOutputStream.BYTE_ORDER_BIG_ENDIAN);
+		bos.write4Bytes(IcnsImageParser.ICNS_MAGIC);
+		bos.write4Bytes(4 + 4 + 4 + 4 + 16*16);
+		bos.write4Bytes(IcnsType.ICNS_16x16_8BIT_IMAGE.getType());
+		bos.write4Bytes(4 + 4 + 16*16);
+		// 8 BPP image
+		for (int y = 0; y < 16; y++)
+		{
+			for (int x = 0; x < 16; x++)
+			{
+				if (image[y][x] != 0)
+					bos.write(0xff);
+				else
+					bos.write(43);
+			}
+		}
+		bos.flush();
+		writeAndReadImageData("8bpp-image-no-mask", baos.toByteArray(), foreground, background);
+	}
+
+	public void test32BPPMaskedIcon() throws ImageReadException, ImageWriteException, IOException
+	{
+		final int foreground = 0xff000000;
+		final int background = 0x000000ff;
+		ByteArrayOutputStream baos = new ByteArrayOutputStream();
+		BinaryOutputStream bos = new BinaryOutputStream(baos,
+				BinaryOutputStream.BYTE_ORDER_BIG_ENDIAN);
+		bos.write4Bytes(IcnsImageParser.ICNS_MAGIC);
+		bos.write4Bytes(4 + 4 + 4 + 4 + 4*16*16 + 4 + 4 + 2*16*16/8);
+		bos.write4Bytes(IcnsType.ICNS_16x16_32BIT_IMAGE.getType());
+		bos.write4Bytes(4 + 4 + 4*16*16);
+		for (int y = 0; y < 16; y++)
+		{
+			for (int x = 0; x < 16; x++)
+			{
+				// argb, a ignored
+				bos.write(0);
+				final int pixel;
+				if (image[y][x] != 0)
+					pixel = foreground;
+				else
+					pixel = background;
+				bos.write(0xff & (pixel >> 16));
+				bos.write(0xff & (pixel >> 8));
+				bos.write(0xff & pixel);
+			}
+		}
+		bos.write4Bytes(IcnsType.ICNS_16x16_1BIT_IMAGE_AND_MASK.getType());
+		bos.write4Bytes(4 + 4 + 2*16*16/8);
+		// 1 bit image
+		for (int y = 0; y < 16; y++)
+		{
+			for (int x = 0; x < 16; x += 8)
+			{
+				int eightBits = 0;
+				for (int pos = 0; pos < 8; pos++)
+				{
+					if (image[y][x+pos] != 0)
+						eightBits |= (1 << (7 - pos));
+				}
+				bos.write(eightBits);
+			}
+		}
+		// 1 bit mask
+		for (int y = 0; y < 16; y++)
+		{
+			for (int x = 0; x < 16; x += 8)
+			{
+				int eightBits = 0;
+				for (int pos = 0; pos < 8; pos++)
+				{
+					if (image[y][x+pos] != 0)
+						eightBits |= (1 << (7 - pos));
+				}
+				bos.write(eightBits);
+			}
+		}
+		bos.flush();
+		writeAndReadImageData("32bpp-image-1bpp-mask", baos.toByteArray(), foreground, background);
+	}
+
+	public void test32BPPHalfMaskedIcon() throws ImageReadException, ImageWriteException, IOException
+	{
+		final int foreground = 0xff000000;
+		final int background = 0xff0000ff;
+		ByteArrayOutputStream baos = new ByteArrayOutputStream();
+		BinaryOutputStream bos = new BinaryOutputStream(baos,
+				BinaryOutputStream.BYTE_ORDER_BIG_ENDIAN);
+		bos.write4Bytes(IcnsImageParser.ICNS_MAGIC);
+		bos.write4Bytes(4 + 4 + 4 + 4 + 4*16*16 + 4 + 4 + 16*16/8);
+		bos.write4Bytes(IcnsType.ICNS_16x16_32BIT_IMAGE.getType());
+		bos.write4Bytes(4 + 4 + 4*16*16);
+		for (int y = 0; y < 16; y++)
+		{
+			for (int x = 0; x < 16; x++)
+			{
+				// argb, a ignored
+				bos.write(0);
+				final int pixel;
+				if (image[y][x] != 0)
+					pixel = foreground;
+				else
+					pixel = background;
+				bos.write(0xff & (pixel >> 16));
+				bos.write(0xff & (pixel >> 8));
+				bos.write(0xff & pixel);
+			}
+		}
+		bos.write4Bytes(IcnsType.ICNS_16x16_1BIT_IMAGE_AND_MASK.getType());
+		bos.write4Bytes(4 + 4 + 16*16/8);
+		// 1 bit image
+		for (int y = 0; y < 16; y++)
+		{
+			for (int x = 0; x < 16; x += 8)
+			{
+				int eightBits = 0;
+				for (int pos = 0; pos < 8; pos++)
+				{
+					if (image[y][x+pos] != 0)
+						eightBits |= (1 << (7 - pos));
+				}
+				bos.write(eightBits);
+			}
+		}
+		// Missing 1 bit mask!!!
+		bos.flush();
+		
+		boolean threw = false;
+		try
+		{
+			writeAndReadImageData("32bpp-half-masked-CORRUPT", baos.toByteArray(), foreground, background);
+		}
+		catch (ImageReadException imageReadException)
+		{
+			threw = true;
+		}
+		assertTrue("ICNS file with corrupt mask didn't fail to parse", threw);
+	}
+
+	public void test32BPPMaskMissingIcon() throws ImageReadException, ImageWriteException, IOException
+	{
+		final int foreground = 0xff000000;
+		final int background = 0xff0000ff;
+		ByteArrayOutputStream baos = new ByteArrayOutputStream();
+		BinaryOutputStream bos = new BinaryOutputStream(baos,
+				BinaryOutputStream.BYTE_ORDER_BIG_ENDIAN);
+		bos.write4Bytes(IcnsImageParser.ICNS_MAGIC);
+		bos.write4Bytes(4 + 4 + 4 + 4 + 4*16*16);
+		bos.write4Bytes(IcnsType.ICNS_16x16_32BIT_IMAGE.getType());
+		bos.write4Bytes(4 + 4 + 4*16*16);
+		for (int y = 0; y < 16; y++)
+		{
+			for (int x = 0; x < 16; x++)
+			{
+				// argb, a ignored
+				bos.write(0);
+				final int pixel;
+				if (image[y][x] != 0)
+					pixel = foreground;
+				else
+					pixel = background;
+				bos.write(0xff & (pixel >> 16));
+				bos.write(0xff & (pixel >> 8));
+				bos.write(0xff & pixel);
+			}
+		}
+		bos.flush();
+		writeAndReadImageData("32bpp-mask-missing", baos.toByteArray(), foreground, background);
+	}
+
+    private void writeAndReadImageData(String description, byte[] rawData,
+            int foreground, int background) throws IOException,
+            ImageReadException
+    {
+        // Uncomment to generate ICNS files that can be tested with MacOS:
+        File exportFile = new File("/tmp/" + description + ".icns");
+        IOUtils.writeToFile(rawData, exportFile);
+
+        File tempFile = createTempFile("temp", ".icns");
+        IOUtils.writeToFile(rawData, tempFile);
+
+        BufferedImage dstImage = Sanselan.getBufferedImage(tempFile);
+
+        assertNotNull(dstImage);
+        assertTrue(dstImage.getWidth() == image[0].length);
+        assertTrue(dstImage.getHeight() == image.length);
+
+        verify(dstImage, foreground, background);
+    }
+
+    private void verify(BufferedImage data, int foreground, int background)
+    {
+        assertNotNull(data);
+        assertTrue(data.getHeight() == image.length);
+
+        for (int y = 0; y < data.getHeight(); y++)
+        {
+            assertTrue(data.getWidth() == image[y].length);
+            for (int x = 0; x < data.getWidth(); x++)
+            {
+                int imageARGB = (image[y][x] == 1) ? foreground : background;
+                int dataARGB = data.getRGB(x, y);
+
+                if (imageARGB != dataARGB)
+                {
+                    Debug.debug("x: " + x + ", y: " + y + ", image: " + imageARGB
+                            + " (0x" + Integer.toHexString(imageARGB) + ")"
+                            + ", data: " + dataARGB + " (0x"
+                            + Integer.toHexString(dataARGB) + ")");
+                }
+                assertTrue(imageARGB == dataARGB);
+            }
+        }
+    }
+}

Propchange: commons/proper/sanselan/trunk/src/test/java/org/apache/sanselan/formats/icns/IcnsRoundTripTest.java
------------------------------------------------------------------------------
    svn:eol-style = native



Mime
View raw message