incubator-sanselan-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From cmc...@apache.org
Subject svn commit: r682066 - in /incubator/sanselan/trunk: RELEASE_NOTES src/main/java/org/apache/sanselan/palette/PaletteFactory.java src/test/java/org/apache/sanselan/roundtrip/ src/test/java/org/apache/sanselan/roundtrip/RoundtripTest.java
Date Sat, 02 Aug 2008 21:56:26 GMT
Author: cmchen
Date: Sat Aug  2 14:56:25 2008
New Revision: 682066

URL: http://svn.apache.org/viewvc?rev=682066&view=rev
Log:
* Added a unit test around reading and writing images in every format.

Added:
    incubator/sanselan/trunk/src/test/java/org/apache/sanselan/roundtrip/
    incubator/sanselan/trunk/src/test/java/org/apache/sanselan/roundtrip/RoundtripTest.java
  (with props)
Modified:
    incubator/sanselan/trunk/RELEASE_NOTES
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/palette/PaletteFactory.java

Modified: incubator/sanselan/trunk/RELEASE_NOTES
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/RELEASE_NOTES?rev=682066&r1=682065&r2=682066&view=diff
==============================================================================
--- incubator/sanselan/trunk/RELEASE_NOTES (original)
+++ incubator/sanselan/trunk/RELEASE_NOTES Sat Aug  2 14:56:25 2008
@@ -15,6 +15,14 @@
 Release 0.95
 ------------
 
+ * Added a wide-reaching unit test around reading and writing images.
+ * We now sort some (but not all) GIF color tables.
+ * Applied the BMP "buffer flushing" bug to the PBM reading and writing code.
+ * Fixed a regression around flushing the bit buffer when writing BMPs with very small palettes.
+ * Removed assumption about DataBuffer type when reading BMPs.
+ * When writing a GIF, we now always include a Graphic Control Extension block, even if its
not necessary.
+ * We are more defensive about missing GCEs.
+ * Lastly, we now set a minimum bound on initial code sizes for LZW-compressed Gif image
data.
  * Found a regression in writing TIFFs around strip offsets being properly updated.  Not
a LZW issue after all.
 	Added a few unit tests around this issue.	
 	see: https://issues.apache.org/jira/browse/SANSELAN-6

Modified: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/palette/PaletteFactory.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/apache/sanselan/palette/PaletteFactory.java?rev=682066&r1=682065&r2=682066&view=diff
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/apache/sanselan/palette/PaletteFactory.java
(original)
+++ incubator/sanselan/trunk/src/main/java/org/apache/sanselan/palette/PaletteFactory.java
Sat Aug  2 14:56:25 2008
@@ -20,6 +20,7 @@
 import java.awt.image.BufferedImage;
 import java.awt.image.ColorModel;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
@@ -467,6 +468,7 @@
 
 		int result[] = new int[rgb_count];
 		System.arraycopy(rgbs, 0, result, 0, rgb_count);
+		Arrays.sort(result);
 
 		//		return result;
 		return new SimplePalette(result);

Added: incubator/sanselan/trunk/src/test/java/org/apache/sanselan/roundtrip/RoundtripTest.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/test/java/org/apache/sanselan/roundtrip/RoundtripTest.java?rev=682066&view=auto
==============================================================================
--- incubator/sanselan/trunk/src/test/java/org/apache/sanselan/roundtrip/RoundtripTest.java
(added)
+++ incubator/sanselan/trunk/src/test/java/org/apache/sanselan/roundtrip/RoundtripTest.java
Sat Aug  2 14:56:25 2008
@@ -0,0 +1,448 @@
+/*
+ * 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.roundtrip;
+
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.sanselan.ImageFormat;
+import org.apache.sanselan.ImageReadException;
+import org.apache.sanselan.ImageWriteException;
+import org.apache.sanselan.Sanselan;
+import org.apache.sanselan.SanselanConstants;
+import org.apache.sanselan.SanselanTest;
+import org.apache.sanselan.common.RgbBufferedImageFactory;
+import org.apache.sanselan.common.byteSources.ByteSourceFile;
+import org.apache.sanselan.formats.png.PngImageParser;
+import org.apache.sanselan.util.Debug;
+import org.apache.sanselan.util.IOUtils;
+
+public class RoundtripTest extends SanselanTest
+{
+	private static final int COLOR_FULL_RGB = 0;
+	private static final int COLOR_LIMITED_INDEX = 1;
+	private static final int COLOR_GRAYSCALE = 2;
+	private static final int COLOR_BITMAP = 3;
+
+	private static class FormatInfo
+	{
+
+		public final ImageFormat format;
+		public final boolean canRead;
+		public final boolean canWrite;
+		public final int colorSupport;
+		public final boolean identicalSecondWrite;
+
+		public FormatInfo(ImageFormat format, boolean canRead,
+				boolean canWrite, int colorSupport,
+				final boolean identicalSecondWrite)
+		{
+			this.canRead = canRead;
+			this.canWrite = canWrite;
+			this.colorSupport = colorSupport;
+			this.format = format;
+			this.identicalSecondWrite = identicalSecondWrite;
+		}
+	}
+
+	private static final FormatInfo FORMAT_INFOS[] = { //
+			new FormatInfo(ImageFormat.IMAGE_FORMAT_PNG, true, true,
+					COLOR_FULL_RGB, true), //
+			new FormatInfo(ImageFormat.IMAGE_FORMAT_GIF, true, true,
+					COLOR_LIMITED_INDEX, true), //
+			new FormatInfo(ImageFormat.IMAGE_FORMAT_ICO, true, false,
+					COLOR_FULL_RGB, true), //
+			new FormatInfo(ImageFormat.IMAGE_FORMAT_TIFF, true, true,
+					COLOR_FULL_RGB, true), //
+			new FormatInfo(ImageFormat.IMAGE_FORMAT_JPEG, false, false,
+					COLOR_FULL_RGB, true), //
+			new FormatInfo(ImageFormat.IMAGE_FORMAT_BMP, true, true,
+					COLOR_FULL_RGB, true), //
+			new FormatInfo(ImageFormat.IMAGE_FORMAT_PSD, true, false,
+					COLOR_FULL_RGB, true), //
+			new FormatInfo(ImageFormat.IMAGE_FORMAT_PBM, true, true,
+					COLOR_BITMAP, true), //
+			new FormatInfo(ImageFormat.IMAGE_FORMAT_PGM, true, true,
+					COLOR_GRAYSCALE, true), //
+			new FormatInfo(ImageFormat.IMAGE_FORMAT_PPM, true, true,
+					COLOR_FULL_RGB, true), //
+			// new FormatInfo(ImageFormat.IMAGE_FORMAT_PNM, true, true,
+			// COLOR_FULL_RGB, true), //
+			new FormatInfo(ImageFormat.IMAGE_FORMAT_TGA, false, false,
+					COLOR_FULL_RGB, true), //
+	};
+
+	private BufferedImage createArgbBitmapImage(int width, int height)
+	{
+		BufferedImage result = new BufferedImage(width, height,
+				BufferedImage.TYPE_INT_ARGB);
+		for (int x = 0; x < width; x++)
+			for (int y = 0; y < height; y++)
+			{
+				// alternating black and white.
+				int modulator = y + 2; // make sure lines vary.
+				int argb = (x + y) % modulator == 0 ? 0xff000000 : 0xffffffff;
+				result.setRGB(x, y, argb);
+			}
+		return result;
+	}
+
+	private BufferedImage createBitmapBitmapImage(int width, int height)
+	{
+		BufferedImage result = new BufferedImage(width, height,
+				BufferedImage.TYPE_BYTE_BINARY);
+		for (int x = 0; x < width; x++)
+			for (int y = 0; y < height; y++)
+			{
+				// alternating black and white.
+				int modulator = y + 2; // make sure lines vary.
+				int argb = (x + y) % modulator == 0 ? 0xff000000 : 0xffffffff;
+				result.setRGB(x, y, argb);
+			}
+		return result;
+	}
+
+	private BufferedImage createArgbGrayscaleImage(int width, int height)
+	{
+		BufferedImage result = new BufferedImage(width, height,
+				BufferedImage.TYPE_INT_ARGB);
+		for (int x = 0; x < width; x++)
+			for (int y = 0; y < height; y++)
+			{
+				int value = (256 * (x + y)) / (width + height);
+				int argb = (0xff << 24) | (value << 16) | (value << 8)
+						| (value << 0);
+
+				result.setRGB(x, y, argb);
+			}
+		return result;
+	}
+
+	private BufferedImage createGrayscaleGrayscaleImage(int width, int height)
+	{
+		BufferedImage result = new BufferedImage(width, height,
+				BufferedImage.TYPE_BYTE_GRAY);
+		for (int x = 0; x < width; x++)
+			for (int y = 0; y < height; y++)
+			{
+				int value = (256 * (x + y)) / (width + height);
+				int argb = (0xff << 24) | (value << 16) | (value << 8)
+						| (value << 0);
+
+				result.setRGB(x, y, argb);
+			}
+		return result;
+	}
+
+	private BufferedImage createLimitedColorImage(int width, int height)
+	{
+		int colors[] = { 0xffffffff, 0xff000000, 0xfff00000, 0xff0000ff,
+				0xff123456, 0xfffefeff, 0xff7f817f, };
+
+		BufferedImage result = new BufferedImage(width, height,
+				BufferedImage.TYPE_INT_ARGB);
+		for (int x = 0; x < width; x++)
+			for (int y = 0; y < height; y++)
+			{
+				int argb = colors[(x + y) % colors.length];
+				result.setRGB(x, y, argb);
+			}
+		return result;
+	}
+
+	private BufferedImage createFullColorImage(int width, int height)
+	{
+		BufferedImage result = new BufferedImage(width, height,
+				BufferedImage.TYPE_INT_ARGB);
+		for (int x = 0; x < width; x++)
+			for (int y = 0; y < height; y++)
+			{
+				int red = (x * 255) / width;
+				int green = (y * 255) / height;
+				int blue = ((x + y) * 255) / (width + height);
+				int argb = (0xff << 24) | (red << 16) | (green << 8)
+						| (blue << 0);
+				result.setRGB(x, y, argb);
+			}
+		return result;
+	}
+
+	private void compareImagesExact(BufferedImage a, BufferedImage b)
+	{
+		compareImages(a, b, 0);
+	}
+
+	// private void compareImagesOffByOne(BufferedImage a, BufferedImage b)
+	// {
+	// compareImages(a, b, 3); // one bit of rounding error for each channel
+	// }
+
+	private void compareImages(BufferedImage a, BufferedImage b, int tolerance)
+	{
+		assertEquals(a.getWidth(), b.getWidth());
+		assertEquals(a.getHeight(), b.getHeight());
+
+		for (int x = 0; x < a.getWidth(); x++)
+			for (int y = 0; y < a.getHeight(); y++)
+			{
+				int a_argb = a.getRGB(x, y);
+				int b_argb = b.getRGB(x, y);
+				if (a_argb != b_argb)
+				{
+					if (calculateARGBDistance(a_argb, b_argb) <= tolerance)
+						continue; // ignore.
+				}
+				if (a_argb != b_argb)
+				{
+					Debug.debug("width", a.getWidth());
+					Debug.debug("height", a.getHeight());
+					Debug.debug("distance", calculateARGBDistance(a_argb,
+							b_argb));
+					Debug.debug("x", x);
+					Debug.debug("y", y);
+					Debug.debug("a_argb", a_argb + " (0x"
+							+ Integer.toHexString(a_argb) + ")");
+					Debug.debug("b_argb", b_argb + " (0x"
+							+ Integer.toHexString(b_argb) + ")");
+				}
+				assertEquals(a_argb, b_argb);
+			}
+	}
+
+	private int calculateARGBDistance(int a, int b)
+	{
+		int aAlpha = 0xff & (a >> 24);
+		int aRed = 0xff & (a >> 16);
+		int aGreen = 0xff & (a >> 8);
+		int aBlue = 0xff & (a >> 0);
+		int bAlpha = 0xff & (b >> 24);
+		int bRed = 0xff & (b >> 16);
+		int bGreen = 0xff & (b >> 8);
+		int bBlue = 0xff & (b >> 0);
+		int diff = Math.abs(aAlpha - bAlpha) + Math.abs(aRed - bRed)
+				+ Math.abs(aGreen - bGreen) + Math.abs(aBlue - bBlue);
+		return diff;
+
+	}
+
+	private void compareFilesExact(File a, File b) throws IOException
+	{
+		assertTrue(a.exists() && a.isFile());
+		assertTrue(b.exists() && b.isFile());
+		assertEquals(a.length(), b.length());
+
+		byte aData[] = IOUtils.getFileBytes(a);
+		byte bData[] = IOUtils.getFileBytes(b);
+
+		for (int i = 0; i < a.length(); i++)
+		{
+			int aByte = 0xff & aData[i];
+			int bByte = 0xff & bData[i];
+
+			if (aByte != bByte)
+			{
+				Debug.debug("a", a);
+				Debug.debug("b", b);
+				Debug.debug("i", i);
+				Debug.debug("aByte", aByte + " (0x"
+						+ Integer.toHexString(aByte) + ")");
+				Debug.debug("bByte", bByte + " (0x"
+						+ Integer.toHexString(bByte) + ")");
+			}
+			assertEquals(aByte, bByte);
+		}
+	}
+
+	 public void testBitmapRoundtrip() throws IOException, ImageReadException,
+	 ImageWriteException
+	 {
+	 BufferedImage testImages[] = { //
+	
+	 createArgbBitmapImage(1, 1), // minimal
+	 createArgbBitmapImage(2, 2), //
+	 createArgbBitmapImage(10, 10), // larger than 8
+	 createArgbBitmapImage(300, 300), // larger than 256
+	
+	 createBitmapBitmapImage(1, 1), // minimal
+	 createBitmapBitmapImage(2, 2), //
+	 createBitmapBitmapImage(10, 10), // larger than 8
+	 createBitmapBitmapImage(300, 300), // larger than 256
+	 };
+	
+	 for (int j = 0; j < testImages.length; j++)
+	 {
+	 BufferedImage testImage = testImages[j];
+	
+	 for (int i = 0; i < FORMAT_INFOS.length; i++)
+	 {
+	 FormatInfo formatInfo = FORMAT_INFOS[i];
+	 if ((!formatInfo.canRead) || (!formatInfo.canWrite))
+	 continue;
+	
+	 Debug.debug("bitmap test: " + formatInfo.format.name);
+	
+	 testRoundtrip(formatInfo, testImage, "bitmap", true);
+	 }
+	 }
+	 }
+
+	 public void testGrayscaleRoundtrip() throws IOException,
+	 ImageReadException, ImageWriteException
+	 {
+	 BufferedImage testImages[] = { //
+	
+	 createArgbBitmapImage(1, 1), // minimal
+	 createArgbGrayscaleImage(2, 2), //
+	 createArgbGrayscaleImage(10, 10), // larger than 8
+	 createArgbGrayscaleImage(300, 300), // larger than 256
+	
+	 createGrayscaleGrayscaleImage(1, 1), // minimal
+	 createGrayscaleGrayscaleImage(2, 2), //
+	 createGrayscaleGrayscaleImage(10, 10), // larger than 8
+	 createGrayscaleGrayscaleImage(300, 300), // larger than 256
+	 };
+	
+	 for (int j = 0; j < testImages.length; j++)
+	 {
+	 BufferedImage testImage = testImages[j];
+	
+	 for (int i = 0; i < FORMAT_INFOS.length; i++)
+	 {
+	 FormatInfo formatInfo = FORMAT_INFOS[i];
+	 if ((!formatInfo.canRead) || (!formatInfo.canWrite))
+	 continue;
+	
+	 Debug.debug("grayscale test: " + formatInfo.format.name);
+	
+	 boolean imageExact = true;
+	 if (formatInfo.colorSupport == COLOR_BITMAP)
+	 imageExact = false;
+	
+	 testRoundtrip(formatInfo, testImage, "gray", imageExact);
+	 }
+	 }
+	 }
+
+	public void testLimitedColorRoundtrip() throws IOException,
+			ImageReadException, ImageWriteException
+	{
+		BufferedImage testImages[] = { //
+
+		createLimitedColorImage(1, 1), // minimal
+				createLimitedColorImage(2, 2), //
+				createLimitedColorImage(10, 10), // larger than 8
+				createLimitedColorImage(300, 300), // larger than 256
+		};
+
+		for (int j = 0; j < testImages.length; j++)
+		{
+			BufferedImage testImage = testImages[j];
+
+			for (int i = 0; i < FORMAT_INFOS.length; i++)
+			{
+				FormatInfo formatInfo = FORMAT_INFOS[i];
+				if ((!formatInfo.canRead) || (!formatInfo.canWrite))
+					continue;
+
+				Debug.debug("indexable test: " + formatInfo.format.name);
+
+				boolean imageExact = true;
+				if (formatInfo.colorSupport == COLOR_BITMAP)
+					imageExact = false;
+				if (formatInfo.colorSupport == COLOR_GRAYSCALE)
+					imageExact = false;
+
+				testRoundtrip(formatInfo, testImage, "indexable", imageExact);
+			}
+		}
+	}
+
+
+	public void testFullColorRoundtrip() throws IOException,
+			ImageReadException, ImageWriteException
+	{
+		BufferedImage testImages[] = { //
+
+		createFullColorImage(1, 1), // minimal
+				createFullColorImage(2, 2), //
+				createFullColorImage(10, 10), // larger than 8
+				createFullColorImage(300, 300), // larger than 256
+		};
+
+		for (int j = 0; j < testImages.length; j++)
+		{
+			BufferedImage testImage = testImages[j];
+
+			for (int i = 0; i < FORMAT_INFOS.length; i++)
+			{
+				FormatInfo formatInfo = FORMAT_INFOS[i];
+				if ((!formatInfo.canRead) || (!formatInfo.canWrite))
+					continue;
+
+				Debug.debug("fullColor test: " + formatInfo.format.name);
+
+				boolean imageExact = true;
+				if (formatInfo.colorSupport == COLOR_BITMAP)
+					imageExact = false;
+				if (formatInfo.colorSupport == COLOR_GRAYSCALE)
+					imageExact = false;
+				if (formatInfo.colorSupport == COLOR_LIMITED_INDEX)
+					imageExact = false;
+				
+				testRoundtrip(formatInfo, testImage, "fullColor", imageExact);
+			}
+		}
+	}
+
+	private void testRoundtrip(FormatInfo formatInfo, BufferedImage testImage,
+			String tempPrefix, boolean imageExact) throws IOException,
+			ImageReadException, ImageWriteException
+	{
+		File temp1 = createTempFile(tempPrefix + ".", "."
+				+ formatInfo.format.extension);
+		// Debug.debug("tempFile: " + tempFile.getName());
+
+		Map params = new HashMap();
+		Sanselan.writeImage(testImage, temp1, formatInfo.format, params);
+
+		Map readParams = new HashMap();
+		readParams.put(SanselanConstants.BUFFERED_IMAGE_FACTORY,
+				new RgbBufferedImageFactory());
+		BufferedImage image2 = Sanselan.getBufferedImage(temp1, readParams);
+		assertNotNull(image2);
+
+		if (imageExact)
+		{
+			// note tolerance when comparing grayscale images
+			// BufferedImages of
+			compareImagesExact(testImage, image2);
+		}
+
+		File temp2 = createTempFile(tempPrefix + ".", "."
+				+ formatInfo.format.extension);
+		// Debug.debug("tempFile: " + tempFile.getName());
+		Sanselan.writeImage(image2, temp2, formatInfo.format, params);
+
+		compareFilesExact(temp1, temp2);
+	}
+
+}

Propchange: incubator/sanselan/trunk/src/test/java/org/apache/sanselan/roundtrip/RoundtripTest.java
------------------------------------------------------------------------------
    svn:eol-style = native



Mime
View raw message