You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by se...@apache.org on 2010/09/10 18:33:42 UTC

svn commit: r995859 [9/30] - in /commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan: ./ color/ common/ common/byteSources/ common/mylzw/ formats/bmp/ formats/bmp/pixelparsers/ formats/bmp/writers/ formats/gif/ formats/ico/ formats/jpeg/ fo...

Modified: commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/gif/GIFHeaderInfo.java
URL: http://svn.apache.org/viewvc/commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/gif/GIFHeaderInfo.java?rev=995859&r1=995858&r2=995859&view=diff
==============================================================================
--- commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/gif/GIFHeaderInfo.java (original)
+++ commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/gif/GIFHeaderInfo.java Fri Sep 10 16:33:35 2010
@@ -18,44 +18,44 @@ package org.apache.sanselan.formats.gif;
 
 class GIFHeaderInfo
 {
-	public final byte identifier1;
-	public final byte identifier2;
-	public final byte identifier3;
-	public final byte version1;
-	public final byte version2;
-	public final byte version3;
+    public final byte identifier1;
+    public final byte identifier2;
+    public final byte identifier3;
+    public final byte version1;
+    public final byte version2;
+    public final byte version3;
 
-	public final int logicalScreenWidth;
-	public final int logicalScreenHeight;
-	public final byte packedFields;
-	public final byte backgroundColorIndex;
-	public final byte pixelAspectRatio;
-	public final boolean globalColorTableFlag;
-	public final byte colorResolution;
-	public final boolean sortFlag;
-	public final byte sizeOfGlobalColorTable;
+    public final int logicalScreenWidth;
+    public final int logicalScreenHeight;
+    public final byte packedFields;
+    public final byte backgroundColorIndex;
+    public final byte pixelAspectRatio;
+    public final boolean globalColorTableFlag;
+    public final byte colorResolution;
+    public final boolean sortFlag;
+    public final byte sizeOfGlobalColorTable;
 
-	public GIFHeaderInfo(byte Identifier1, byte Identifier2, byte Identifier3,
-			byte Version1, byte Version2, byte Version3,
-			int LogicalScreenWidth, int LogicalScreenHeight, byte PackedFields,
-			byte BackgroundColorIndex, byte PixelAspectRatio,
-			boolean GlobalColorTableFlag, byte ColorResolution,
-			boolean SortFlag, byte SizeofGlobalColorTable)
-	{
-		this.identifier1 = Identifier1;
-		this.identifier2 = Identifier2;
-		this.identifier3 = Identifier3;
-		this.version1 = Version1;
-		this.version2 = Version2;
-		this.version3 = Version3;
-		this.logicalScreenWidth = LogicalScreenWidth;
-		this.logicalScreenHeight = LogicalScreenHeight;
-		this.packedFields = PackedFields;
-		this.backgroundColorIndex = BackgroundColorIndex;
-		this.pixelAspectRatio = PixelAspectRatio;
-		this.globalColorTableFlag = GlobalColorTableFlag;
-		this.colorResolution = ColorResolution;
-		this.sortFlag = SortFlag;
-		this.sizeOfGlobalColorTable = SizeofGlobalColorTable;
-	}
+    public GIFHeaderInfo(byte Identifier1, byte Identifier2, byte Identifier3,
+            byte Version1, byte Version2, byte Version3,
+            int LogicalScreenWidth, int LogicalScreenHeight, byte PackedFields,
+            byte BackgroundColorIndex, byte PixelAspectRatio,
+            boolean GlobalColorTableFlag, byte ColorResolution,
+            boolean SortFlag, byte SizeofGlobalColorTable)
+    {
+        this.identifier1 = Identifier1;
+        this.identifier2 = Identifier2;
+        this.identifier3 = Identifier3;
+        this.version1 = Version1;
+        this.version2 = Version2;
+        this.version3 = Version3;
+        this.logicalScreenWidth = LogicalScreenWidth;
+        this.logicalScreenHeight = LogicalScreenHeight;
+        this.packedFields = PackedFields;
+        this.backgroundColorIndex = BackgroundColorIndex;
+        this.pixelAspectRatio = PixelAspectRatio;
+        this.globalColorTableFlag = GlobalColorTableFlag;
+        this.colorResolution = ColorResolution;
+        this.sortFlag = SortFlag;
+        this.sizeOfGlobalColorTable = SizeofGlobalColorTable;
+    }
 }
\ No newline at end of file

Modified: commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/gif/GenericGIFBlock.java
URL: http://svn.apache.org/viewvc/commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/gif/GenericGIFBlock.java?rev=995859&r1=995858&r2=995859&view=diff
==============================================================================
--- commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/gif/GenericGIFBlock.java (original)
+++ commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/gif/GenericGIFBlock.java Fri Sep 10 16:33:35 2010
@@ -22,34 +22,34 @@ import java.util.ArrayList;
 
 class GenericGIFBlock extends GIFBlock
 {
-	public final ArrayList subblocks;
+    public final ArrayList subblocks;
 
-	public GenericGIFBlock(int blockCode, ArrayList subblocks)
-	{
-		super(blockCode);
-
-		this.subblocks = subblocks;
-
-	}
-
-	public byte[] appendSubBlocks() throws IOException
-	{
-		return appendSubBlocks(false);
-	}
-
-	public byte[] appendSubBlocks(boolean includeLengths) throws IOException
-	{
-		ByteArrayOutputStream out = new ByteArrayOutputStream();
-
-		for (int i = 0; i < subblocks.size(); i++)
-		{
-			byte subblock[] = (byte[]) subblocks.get(i);
-			if(includeLengths && i>0)
-				out.write(subblock.length);
-			out.write(subblock);
-		}
+    public GenericGIFBlock(int blockCode, ArrayList subblocks)
+    {
+        super(blockCode);
+
+        this.subblocks = subblocks;
+
+    }
+
+    public byte[] appendSubBlocks() throws IOException
+    {
+        return appendSubBlocks(false);
+    }
+
+    public byte[] appendSubBlocks(boolean includeLengths) throws IOException
+    {
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+        for (int i = 0; i < subblocks.size(); i++)
+        {
+            byte subblock[] = (byte[]) subblocks.get(i);
+            if(includeLengths && i>0)
+                out.write(subblock.length);
+            out.write(subblock);
+        }
 
-		return out.toByteArray();
-	}
+        return out.toByteArray();
+    }
 
 }
\ No newline at end of file

Modified: commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/gif/GifImageParser.java
URL: http://svn.apache.org/viewvc/commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/gif/GifImageParser.java?rev=995859&r1=995858&r2=995859&view=diff
==============================================================================
--- commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/gif/GifImageParser.java (original)
+++ commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/gif/GifImageParser.java Fri Sep 10 16:33:35 2010
@@ -50,1140 +50,1140 @@ import org.apache.sanselan.util.ParamMap
 public class GifImageParser extends ImageParser
 {
 
-	public GifImageParser()
-	{
-		super.setByteOrder(BYTE_ORDER_LSB);
-	}
-
-	public String getName()
-	{
-		return "Gif-Custom";
-	}
-
-	public String getDefaultExtension()
-	{
-		return DEFAULT_EXTENSION;
-	}
-
-	private static final String DEFAULT_EXTENSION = ".gif";
-
-	private static final String ACCEPTED_EXTENSIONS[] = { DEFAULT_EXTENSION, };
-
-	protected String[] getAcceptedExtensions()
-	{
-		return ACCEPTED_EXTENSIONS;
-	}
-
-	protected ImageFormat[] getAcceptedTypes()
-	{
-		return new ImageFormat[] { ImageFormat.IMAGE_FORMAT_GIF, //
-		};
-	}
-
-	private static final byte GIF_HEADER_SIGNATURE[] = { 71, 73, 70 };
-
-	private GIFHeaderInfo readHeader(InputStream is,
-			FormatCompliance formatCompliance) throws ImageReadException,
-			IOException
-	{
-		byte identifier1 = readByte("identifier1", is, "Not a Valid GIF File");
-		byte identifier2 = readByte("identifier2", is, "Not a Valid GIF File");
-		byte identifier3 = readByte("identifier3", is, "Not a Valid GIF File");
-
-		byte version1 = readByte("version1", is, "Not a Valid GIF File");
-		byte version2 = readByte("version2", is, "Not a Valid GIF File");
-		byte version3 = readByte("version3", is, "Not a Valid GIF File");
-
-		if (formatCompliance != null)
-		{
-			formatCompliance.compare_bytes("Signature", GIF_HEADER_SIGNATURE,
-					new byte[] { identifier1, identifier2, identifier3, });
-			formatCompliance.compare("version", 56, version1);
-			formatCompliance
-					.compare("version", new int[] { 55, 57, }, version2);
-			formatCompliance.compare("version", 97, version3);
-		}
-
-		if (debug)
-			printCharQuad("identifier: ", ((identifier1 << 16)
-					| (identifier2 << 8) | (identifier3 << 0)));
-		if (debug)
-			printCharQuad("version: ",
-					((version1 << 16) | (version2 << 8) | (version3 << 0)));
-
-		int logicalScreenWidth = read2Bytes("Logical Screen Width", is,
-				"Not a Valid GIF File");
-		int logicalScreenHeight = read2Bytes("Logical Screen Height", is,
-				"Not a Valid GIF File");
-
-		if (formatCompliance != null)
-		{
-			formatCompliance.checkBounds("Width", 1, Integer.MAX_VALUE,
-					logicalScreenWidth);
-			formatCompliance.checkBounds("Height", 1, Integer.MAX_VALUE,
-					logicalScreenHeight);
-		}
-
-		byte packedFields = readByte("Packed Fields", is,
-				"Not a Valid GIF File");
-		byte backgroundColorIndex = readByte("Background Color Index", is,
-				"Not a Valid GIF File");
-		byte pixelAspectRatio = readByte("Pixel Aspect Ratio", is,
-				"Not a Valid GIF File");
-
-		if (debug)
-			printByteBits("PackedFields bits", packedFields);
-
-		boolean globalColorTableFlag = ((packedFields & 128) > 0);
-		if (debug)
-			System.out.println("GlobalColorTableFlag: " + globalColorTableFlag);
-		byte colorResolution = (byte) ((packedFields >> 4) & 7);
-		if (debug)
-			System.out.println("ColorResolution: " + colorResolution);
-		boolean sortFlag = ((packedFields & 8) > 0);
-		if (debug)
-			System.out.println("SortFlag: " + sortFlag);
-		byte sizeofGlobalColorTable = (byte) (packedFields & 7);
-		if (debug)
-			System.out.println("SizeofGlobalColorTable: "
-					+ sizeofGlobalColorTable);
-
-		if (formatCompliance != null)
-		{
-			if (globalColorTableFlag && backgroundColorIndex != -1)
-				formatCompliance.checkBounds("Background Color Index", 0,
-						convertColorTableSize(sizeofGlobalColorTable),
-						backgroundColorIndex);
-		}
-
-		return new GIFHeaderInfo(identifier1, identifier2, identifier3,
-				version1, version2, version3, logicalScreenWidth,
-				logicalScreenHeight, packedFields, backgroundColorIndex,
-				pixelAspectRatio, globalColorTableFlag, colorResolution,
-				sortFlag, sizeofGlobalColorTable);
-	}
-
-	private GraphicControlExtension readGraphicControlExtension(int code,
-			InputStream is) throws ImageReadException, IOException
-	{
-		readByte("block_size", is, "GIF: corrupt GraphicControlExt");
-		int packed = readByte("packed fields", is,
-				"GIF: corrupt GraphicControlExt");
-
-		int dispose = (packed & 0x1c) >> 2; // disposal method
-		boolean transparency = (packed & 1) != 0;
-
-		int delay = read2Bytes("delay in milliseconds", is,
-				"GIF: corrupt GraphicControlExt");
-		int transparentColorIndex = 0xff & readByte("transparent color index",
-				is, "GIF: corrupt GraphicControlExt");
-		readByte("block terminator", is, "GIF: corrupt GraphicControlExt");
-
-		return new GraphicControlExtension(code, packed, dispose, transparency,
-				delay, transparentColorIndex);
-	}
-
-	private byte[] readSubBlock(InputStream is) throws ImageReadException,
-			IOException
-	{
-		int block_size = 0xff & readByte("block_size", is, "GIF: corrupt block");
-
-		byte bytes[] = readByteArray("block", block_size, is,
-				"GIF: corrupt block");
-
-		return bytes;
-	}
-
-	protected GenericGIFBlock readGenericGIFBlock(InputStream is, int code)
-			throws ImageReadException, IOException
-	{
-		return readGenericGIFBlock(is, code, null);
-	}
-
-	protected GenericGIFBlock readGenericGIFBlock(InputStream is, int code,
-			byte first[]) throws ImageReadException, IOException
-	{
-		ArrayList subblocks = new ArrayList();
-
-		if (first != null)
-			subblocks.add(first);
-
-		while (true)
-		{
-			byte bytes[] = readSubBlock(is);
-			if (bytes.length < 1)
-				break;
-			subblocks.add(bytes);
-		}
-
-		return new GenericGIFBlock(code, subblocks);
-	}
-
-	private final static int EXTENSION_CODE = 0x21;
-	private final static int IMAGE_SEPARATOR = 0x2C;
-	private final static int GRAPHIC_CONTROL_EXTENSION = (EXTENSION_CODE << 8) | 0xf9;
-	private final static int COMMENT_EXTENSION = 0xfe;
-	private final static int PLAIN_TEXT_EXTENSION = 0x01;
-	private final static int XMP_EXTENSION = 0xff;
-	private final static int TERMINATOR_BYTE = 0x3b;
-	private final static int APPLICATION_EXTENSION_LABEL = 0xff;
-	private final static int XMP_COMPLETE_CODE = (EXTENSION_CODE << 8)
-			| XMP_EXTENSION;
-
-	private ArrayList readBlocks(GIFHeaderInfo ghi, InputStream is,
-			boolean stopBeforeImageData, FormatCompliance formatCompliance)
-			throws ImageReadException, IOException
-	{
-		ArrayList result = new ArrayList();
-
-		while (true)
-		{
-			int code = is.read();
-
-			switch (code)
-			{
-			case -1:
-				throw new ImageReadException("GIF: unexpected end of data");
-
-			case IMAGE_SEPARATOR:
-				ImageDescriptor id = readImageDescriptor(ghi, code, is,
-						stopBeforeImageData, formatCompliance);
-				result.add(id);
-				// if(stopBeforeImageData)
-				// return result;
-
-				break;
-
-			case EXTENSION_CODE: // extension
-			{
-				int extensionCode = is.read();
-				int completeCode = ((0xff & code) << 8)
-						| (0xff & extensionCode);
-
-				switch (extensionCode)
-				{
-				case 0xf9:
-					GraphicControlExtension gce = readGraphicControlExtension(
-							completeCode, is);
-					result.add(gce);
-					break;
-
-				case COMMENT_EXTENSION:
-				case PLAIN_TEXT_EXTENSION: {
-					GenericGIFBlock block = readGenericGIFBlock(is,
-							completeCode);
-					result.add(block);
-					break;
-				}
-
-				case APPLICATION_EXTENSION_LABEL: // 255 (hex 0xFF) Application
-					// Extension Label
-				{
-					byte label[] = readSubBlock(is);
-
-					if (formatCompliance != null)
-						formatCompliance
-								.addComment("Unknown Application Extension ("
-										+ new String(label) + ")", completeCode);
-
-					// if (label == new String("ICCRGBG1"))
-					{
-						// GIF's can have embedded ICC Profiles - who knew?
-					}
-
-					if ((label != null) && (label.length > 0))
-					{
-						GenericGIFBlock block = readGenericGIFBlock(is,
-								completeCode, label);
-						result.add(block);
-					}
-					break;
-				}
-
-				default: {
-
-					if (formatCompliance != null)
-						formatCompliance.addComment("Unknown block",
-								completeCode);
-
-					GenericGIFBlock block = readGenericGIFBlock(is,
-							completeCode);
-					result.add(block);
-					break;
-				}
-				}
-			}
-				break;
-
-			case TERMINATOR_BYTE:
-				return result;
-
-			case 0x00: // bad byte, but keep going and see what happens
-				break;
-
-			default:
-				throw new ImageReadException("GIF: unknown code: " + code);
-			}
-		}
-	}
-
-	private ImageDescriptor readImageDescriptor(GIFHeaderInfo ghi,
-			int blockCode, InputStream is, boolean stopBeforeImageData,
-			FormatCompliance formatCompliance) throws ImageReadException,
-			IOException
-	{
-		int ImageLeftPosition = read2Bytes("Image Left Position", is,
-				"Not a Valid GIF File");
-		int ImageTopPosition = read2Bytes("Image Top Position", is,
-				"Not a Valid GIF File");
-		int imageWidth = read2Bytes("Image Width", is, "Not a Valid GIF File");
-		int imageHeight = read2Bytes("Image Height", is, "Not a Valid GIF File");
-		byte PackedFields = readByte("Packed Fields", is,
-				"Not a Valid GIF File");
-
-		if (formatCompliance != null)
-		{
-			formatCompliance.checkBounds("Width", 1, ghi.logicalScreenWidth,
-					imageWidth);
-			formatCompliance.checkBounds("Height", 1, ghi.logicalScreenHeight,
-					imageHeight);
-			formatCompliance.checkBounds("Left Position", 0,
-					ghi.logicalScreenWidth - imageWidth, ImageLeftPosition);
-			formatCompliance.checkBounds("Top Position", 0,
-					ghi.logicalScreenHeight - imageHeight, ImageTopPosition);
-		}
-
-		if (debug)
-			printByteBits("PackedFields bits", PackedFields);
-
-		boolean LocalColorTableFlag = (((PackedFields >> 7) & 1) > 0);
-		if (debug)
-			System.out.println("LocalColorTableFlag: " + LocalColorTableFlag);
-		boolean InterlaceFlag = (((PackedFields >> 6) & 1) > 0);
-		if (debug)
-			System.out.println("Interlace Flag: " + InterlaceFlag);
-		boolean SortFlag = (((PackedFields >> 5) & 1) > 0);
-		if (debug)
-			System.out.println("Sort  Flag: " + SortFlag);
-
-		byte SizeofLocalColorTable = (byte) (PackedFields & 7);
-		if (debug)
-			System.out.println("SizeofLocalColorTable: "
-					+ SizeofLocalColorTable);
-
-		byte LocalColorTable[] = null;
-		if (LocalColorTableFlag)
-			LocalColorTable = readColorTable(is, SizeofLocalColorTable,
-					formatCompliance);
-
-		byte imageData[] = null;
-		if (!stopBeforeImageData)
-		{
-			int LZWMinimumCodeSize = is.read();
-
-			GenericGIFBlock block = readGenericGIFBlock(is, -1);
-			byte bytes[] = block.appendSubBlocks();
-			InputStream bais = new ByteArrayInputStream(bytes);
-
-			int size = imageWidth * imageHeight;
-			MyLZWDecompressor myLzwDecompressor = new MyLZWDecompressor(
-					LZWMinimumCodeSize, BYTE_ORDER_LSB);
-			imageData = myLzwDecompressor.decompress(bais, size);
-		} else
-		{
-			int LZWMinimumCodeSize = is.read();
-			if (debug)
-				System.out.println("LZWMinimumCodeSize: " + LZWMinimumCodeSize);
-
-			readGenericGIFBlock(is, -1);
-		}
-
-		ImageDescriptor result = new ImageDescriptor(blockCode,
-				ImageLeftPosition, ImageTopPosition, imageWidth, imageHeight,
-				PackedFields, LocalColorTableFlag, InterlaceFlag, SortFlag,
-				SizeofLocalColorTable, LocalColorTable, imageData);
-
-		return result;
-	}
-
-	private int simple_pow(int base, int power)
-	{
-		int result = 1;
-
-		for (int i = 0; i < power; i++)
-			result *= base;
-
-		return result;
-	}
-
-	private int convertColorTableSize(int ct_size)
-	{
-		return 3 * simple_pow(2, ct_size + 1);
-	}
-
-	private byte[] readColorTable(InputStream is, int ct_size,
-			FormatCompliance formatCompliance) throws IOException
-	{
-		int actual_size = convertColorTableSize(ct_size);
-
-		byte bytes[] = readByteArray("block", actual_size, is,
-				"GIF: corrupt Color Table");
-
-		return bytes;
-	}
-
-	// TODO - unused
-	private GIFHeaderInfo readHeader(ByteSource byteSource)
-			throws ImageReadException, IOException
-	{
-		InputStream is = null;
-		try
-		{
-			is = byteSource.getInputStream();
-
-			return readHeader(is, FormatCompliance.getDefault());
-		} finally
-		{
-			try
-			{
-			    if (is != null) {
-			        is.close();
-			    }
-			} catch (Exception e)
-			{
-				Debug.debug(e);
-			}
-
-		}
-	}
-
-	private GIFBlock findBlock(ArrayList v, int code)
-	{
-		for (int i = 0; i < v.size(); i++)
-		{
-			GIFBlock gifBlock = (GIFBlock) v.get(i);
-			if (gifBlock.blockCode == code)
-				return gifBlock;
-		}
-		return null;
-	}
-
-	private ImageContents readFile(ByteSource byteSource,
-			boolean stopBeforeImageData) throws ImageReadException, IOException
-	{
-		return readFile(byteSource, stopBeforeImageData, FormatCompliance
-				.getDefault());
-	}
-
-	private ImageContents readFile(ByteSource byteSource,
-			boolean stopBeforeImageData, FormatCompliance formatCompliance)
-			throws ImageReadException, IOException
-	{
-		InputStream is = null;
-		try
-		{
-			is = byteSource.getInputStream();
-
-			GIFHeaderInfo ghi = readHeader(is, formatCompliance);
-
-			byte globalColorTable[] = null;
-			if (ghi.globalColorTableFlag)
-				globalColorTable = readColorTable(is,
-						ghi.sizeOfGlobalColorTable, formatCompliance);
-
-			ArrayList blocks = readBlocks(ghi, is, stopBeforeImageData,
-					formatCompliance);
-
-			ImageContents result = new ImageContents(ghi, globalColorTable,
-					blocks);
-
-			return result;
-		} finally
-		{
-			try
-			{
-			    if (is != null) {
-			        is.close();
-			    }
-			} catch (Exception e)
-			{
-				Debug.debug(e);
-			}
-
-		}
-	}
-
-	public byte[] getICCProfileBytes(ByteSource byteSource, Map params)
-			throws ImageReadException, IOException
-	{
-		return null;
-	}
-
-	public Dimension getImageSize(ByteSource byteSource, Map params)
-			throws ImageReadException, IOException
-	{
-		ImageContents blocks = readFile(byteSource, false);
-
-		if (blocks == null)
-			throw new ImageReadException("GIF: Couldn't read blocks");
-
-		GIFHeaderInfo bhi = blocks.gifHeaderInfo;
-		if (bhi == null)
-			throw new ImageReadException("GIF: Couldn't read Header");
-
-		ImageDescriptor id = (ImageDescriptor) findBlock(blocks.blocks,
-				IMAGE_SEPARATOR);
-		if (id == null)
-			throw new ImageReadException("GIF: Couldn't read ImageDescriptor");
-
-		// Prefer the size information in the ImageDescriptor; it is more reliable
-		// than the size information in the header.
-		return new Dimension(id.imageWidth, id.imageHeight);
-	}
-
-	public byte[] embedICCProfile(byte image[], byte profile[])
-	{
-		return null;
-	}
-
-	public boolean embedICCProfile(File src, File dst, byte profile[])
-	{
-		return false;
-	}
-
-	public IImageMetadata getMetadata(ByteSource byteSource, Map params)
-			throws ImageReadException, IOException
-	{
-		return null;
-	}
-
-	private ArrayList getComments(ArrayList v) throws IOException
-	{
-		ArrayList result = new ArrayList();
-		int code = 0x21fe;
-
-		for (int i = 0; i < v.size(); i++)
-		{
-			GIFBlock block = (GIFBlock) v.get(i);
-			if (block.blockCode == code)
-			{
-				byte bytes[] = ((GenericGIFBlock) block).appendSubBlocks();
-				result.add(new String(bytes));
-			}
-		}
-
-		return result;
-	}
-
-	public ImageInfo getImageInfo(ByteSource byteSource, Map params)
-			throws ImageReadException, IOException
-	{
-		ImageContents blocks = readFile(byteSource, false);
-
-		if (blocks == null)
-			throw new ImageReadException("GIF: Couldn't read blocks");
-
-		GIFHeaderInfo bhi = blocks.gifHeaderInfo;
-		if (bhi == null)
-			throw new ImageReadException("GIF: Couldn't read Header");
-
-		ImageDescriptor id = (ImageDescriptor) findBlock(blocks.blocks,
-				IMAGE_SEPARATOR);
-		if (id == null)
-			throw new ImageReadException("GIF: Couldn't read ImageDescriptor");
-
-		GraphicControlExtension gce = (GraphicControlExtension) findBlock(
-				blocks.blocks, GRAPHIC_CONTROL_EXTENSION);
-
-		// Prefer the size information in the ImageDescriptor; it is more reliable
-		// than the size information in the header.
-		int height = id.imageWidth;
-		int width = id.imageHeight;
-
-		ArrayList Comments;
-
-		Comments = getComments(blocks.blocks);
-
-		int BitsPerPixel = (bhi.colorResolution + 1) * 3;
-		ImageFormat Format = ImageFormat.IMAGE_FORMAT_GIF;
-		String FormatName = "GIF Graphics Interchange Format";
-		String MimeType = "image/gif";
-		// we ought to count images, but don't yet.
-		int NumberOfImages = -1;
-
-		boolean isProgressive = id.interlaceFlag;
-
-		int PhysicalWidthDpi = 72;
-		float PhysicalWidthInch = (float) ((double) width / (double) PhysicalWidthDpi);
-		int PhysicalHeightDpi = 72;
-		float PhysicalHeightInch = (float) ((double) height / (double) PhysicalHeightDpi);
-
-		String FormatDetails = "Gif " + ((char) blocks.gifHeaderInfo.version1)
-				+ ((char) blocks.gifHeaderInfo.version2)
-				+ ((char) blocks.gifHeaderInfo.version3);
-
-		boolean isTransparent = false;
-		if (gce != null && gce.transparency)
-			isTransparent = true;
-
-		boolean usesPalette = true;
-		int colorType = ImageInfo.COLOR_TYPE_RGB;
-		String compressionAlgorithm = ImageInfo.COMPRESSION_ALGORITHM_LZW;
-
-		ImageInfo result = new ImageInfo(FormatDetails, BitsPerPixel, Comments,
-				Format, FormatName, height, MimeType, NumberOfImages,
-				PhysicalHeightDpi, PhysicalHeightInch, PhysicalWidthDpi,
-				PhysicalWidthInch, width, isProgressive, isTransparent,
-				usesPalette, colorType, compressionAlgorithm);
-
-		return result;
-	}
-
-	public boolean dumpImageFile(PrintWriter pw, ByteSource byteSource)
-			throws ImageReadException, IOException
-	{
-		pw.println("gif.dumpImageFile");
-
-		{
-			ImageInfo imageData = getImageInfo(byteSource);
-			if (imageData == null)
-				return false;
-
-			imageData.toString(pw, "");
-		}
-		{
-			ImageContents blocks = readFile(byteSource, false);
-
-			if (blocks == null)
-				return false;
-
-			pw.println("gif.blocks: " + blocks.blocks.size());
-			for (int i = 0; i < blocks.blocks.size(); i++)
-			{
-				GIFBlock gifBlock = (GIFBlock) blocks.blocks.get(i);
-				this.debugNumber(pw, "\t" + i + " ("
-						+ gifBlock.getClass().getName() + ")",
-						gifBlock.blockCode, 4);
-			}
-
-		}
-
-		pw.println("");
-
-		return true;
-	}
-
-	private int[] getColorTable(byte bytes[]) throws ImageReadException
-	{
-		if ((bytes.length % 3) != 0)
-			throw new ImageReadException("Bad Color Table Length: "
-					+ bytes.length);
-		int length = bytes.length / 3;
-
-		int result[] = new int[length];
-
-		for (int i = 0; i < length; i++)
-		{
-			int red = 0xff & bytes[(i * 3) + 0];
-			int green = 0xff & bytes[(i * 3) + 1];
-			int blue = 0xff & bytes[(i * 3) + 2];
-
-			int alpha = 0xff;
-
-			int rgb = (alpha << 24) | (red << 16) | (green << 8) | (blue << 0);
-			result[i] = rgb;
-		}
-
-		return result;
-	}
-
-	public FormatCompliance getFormatCompliance(ByteSource byteSource)
-			throws ImageReadException, IOException
-	{
-		FormatCompliance result = new FormatCompliance(byteSource
-				.getDescription());
-
-		readFile(byteSource, false, result);
-
-		return result;
-	}
-
-	public BufferedImage getBufferedImage(ByteSource byteSource, Map params)
-			throws ImageReadException, IOException
-	{
-		ImageContents imageContents = readFile(byteSource, false);
-
-		if (imageContents == null)
-			throw new ImageReadException("GIF: Couldn't read blocks");
-
-		GIFHeaderInfo ghi = imageContents.gifHeaderInfo;
-		if (ghi == null)
-			throw new ImageReadException("GIF: Couldn't read Header");
-
-		ImageDescriptor id = (ImageDescriptor) findBlock(imageContents.blocks,
-				IMAGE_SEPARATOR);
-		if (id == null)
-			throw new ImageReadException("GIF: Couldn't read Image Descriptor");
-		GraphicControlExtension gce = (GraphicControlExtension) findBlock(
-				imageContents.blocks, GRAPHIC_CONTROL_EXTENSION);
-
-		// Prefer the size information in the ImageDescriptor; it is more reliable
-		// than the size information in the header.
-		int width = id.imageWidth;
-		int height = id.imageHeight;
-
-		boolean hasAlpha = false;
-		if (gce != null && gce.transparency)
-			hasAlpha = true;
-
-		BufferedImage result = getBufferedImageFactory(params)
-				.getColorBufferedImage(width, height, hasAlpha);
-
-		{
-			int colorTable[];
-			if (id.localColorTable != null)
-				colorTable = getColorTable(id.localColorTable);
-			else if (imageContents.globalColorTable != null)
-				colorTable = getColorTable(imageContents.globalColorTable);
-			else
-				throw new ImageReadException("Gif: No Color Table");
-
-			int transparentIndex = -1;
-			if (hasAlpha)
-				transparentIndex = gce.transparentColorIndex;
-
-			int counter = 0;
-
-			int rowsInPass1 = (height + 7) / 8;
-			int rowsInPass2 = (height + 3) / 8;
-			int rowsInPass3 = (height + 1) / 4;
-			int rowsInPass4 = (height) / 2;
-
-			DataBuffer db = result.getRaster().getDataBuffer();
-
-			for (int row = 0; row < height; row++)
-			{
-				int y;
-				if (id.interlaceFlag)
-				{
-					int the_row = row;
-					if (the_row < rowsInPass1)
-						y = the_row * 8;
-					else
-					{
-						the_row -= rowsInPass1;
-						if (the_row < (rowsInPass2))
-							y = 4 + (the_row * 8);
-						else
-						{
-							the_row -= rowsInPass2;
-							if (the_row < (rowsInPass3))
-								y = 2 + (the_row * 4);
-							else
-							{
-								the_row -= rowsInPass3;
-								if (the_row < (rowsInPass4))
-									y = 1 + (the_row * 2);
-								else
-									throw new ImageReadException(
-											"Gif: Strange Row");
-							}
-						}
-					}
-				} else
-					y = row;
-
-				for (int x = 0; x < width; x++)
-				{
-					int index = 0xff & id.imageData[counter++];
-					int rgb = colorTable[index];
-
-					if (transparentIndex == index)
-						rgb = 0x00;
-
-					db.setElem(y * width + x, rgb);
-				}
-
-			}
-		}
-
-		return result;
-
-	}
-
-	private void writeAsSubBlocks(OutputStream os, byte bytes[])
-			throws IOException
-	{
-		int index = 0;
-
-		while (index < bytes.length)
-		{
-			int block_size = Math.min(bytes.length - index, 255);
-			os.write(block_size);
-			os.write(bytes, index, block_size);
-			index += block_size;
-		}
-		os.write(0); // last block
-	}
-
-	private static final int LOCAL_COLOR_TABLE_FLAG_MASK = 1 << 7;
-	private static final int INTERLACE_FLAG_MASK = 1 << 6;
-	private static final int SORT_FLAG_MASK = 1 << 5;
-
-	public void writeImage(BufferedImage src, OutputStream os, Map params)
-			throws ImageWriteException, IOException
-	{
-		// make copy of params; we'll clear keys as we consume them.
-		params = new HashMap(params);
-
-		boolean verbose = ParamMap.getParamBoolean(params, PARAM_KEY_VERBOSE,
-				false);
-
-		// clear format key.
-		if (params.containsKey(PARAM_KEY_FORMAT))
-			params.remove(PARAM_KEY_FORMAT);
-		if (params.containsKey(PARAM_KEY_VERBOSE))
-			params.remove(PARAM_KEY_VERBOSE);
-
-		String xmpXml = null;
-		if (params.containsKey(PARAM_KEY_XMP_XML))
-		{
-			xmpXml = (String) params.get(PARAM_KEY_XMP_XML);
-			params.remove(PARAM_KEY_XMP_XML);
-		}
-
-		if (params.size() > 0)
-		{
-			Object firstKey = params.keySet().iterator().next();
-			throw new ImageWriteException("Unknown parameter: " + firstKey);
-		}
-
-		int width = src.getWidth();
-		int height = src.getHeight();
-
-		boolean hasAlpha = new PaletteFactory().hasTransparency(src);
-
-		int max_colors = hasAlpha ? 255 : 256;
-
-		Palette palette2 = new PaletteFactory().makePaletteSimple(src,
-				max_colors);
-		// int palette[] = new PaletteFactory().makePaletteSimple(src, 256);
-		// Map palette_map = paletteToMap(palette);
-
-		if (palette2 == null)
-		{
-			palette2 = new PaletteFactory().makePaletteQuantized(src,
-					max_colors);
-			if (verbose)
-				System.out.println("quantizing");
-		} else if (verbose)
-			System.out.println("exact palette");
-
-		if (palette2 == null)
-			throw new ImageWriteException(
-					"Gif: can't write images with more than 256 colors");
-		int palette_size = palette2.length() + (hasAlpha ? 1 : 0);
-
-		BinaryOutputStream bos = new BinaryOutputStream(os, BYTE_ORDER_LSB);
-
-		{
-			// write Header
-			os.write(0x47); // G magic numbers
-			os.write(0x49); // I
-			os.write(0x46); // F
-
-			os.write(0x38); // 8 version magic numbers
-			os.write(0x39); // 9
-			os.write(0x61); // a
-
-			// Logical Screen Descriptor.
-
-			bos.write2Bytes(width);
-			bos.write2Bytes(height);
-
-			int colorTableScaleLessOne = (palette_size > 128) ? 7
-					: (palette_size > 64) ? 6 : (palette_size > 32) ? 5
-							: (palette_size > 16) ? 4 : (palette_size > 8) ? 3
-									: (palette_size > 4) ? 2
-											: (palette_size > 2) ? 1 : 0;
-
-			int colorTableSizeInFormat = 1 << (colorTableScaleLessOne + 1);
-			int actual_size = 3 * simple_pow(2, colorTableScaleLessOne + 1);
-			{
-				byte colorResolution = (byte) colorTableScaleLessOne; // TODO:
-
-				boolean globalColorTableFlag = false;
-				boolean sortFlag = false;
-				int globalColorTableFlagMask = 1 << 7;
-				int sortFlagMask = 8;
-				int sizeOfGlobalColorTable = 0;
-
-				int packedFields = ((globalColorTableFlag ? globalColorTableFlagMask
-						: 0)
-						| (sortFlag ? sortFlagMask : 0)
-						| ((7 & colorResolution) << 4) | (7 & sizeOfGlobalColorTable));
-				bos.write(packedFields); // one byte
-			}
-			{
-				byte BackgroundColorIndex = 0;
-				bos.write(BackgroundColorIndex);
-			}
-			{
-				byte PixelAspectRatio = 0;
-				bos.write(PixelAspectRatio);
-			}
-
-			{ // write Global Color Table.
-
-			}
-
-			{ // ALWAYS write GraphicControlExtension
-				bos.write(EXTENSION_CODE);
-				bos.write((byte) 0xf9);
-				// bos.write(0xff & (kGraphicControlExtension >> 8));
-				// bos.write(0xff & (kGraphicControlExtension >> 0));
-
-				bos.write((byte) 4); // block size;
-				int packedFields = hasAlpha ? 1 : 0; // transparency flag
-				bos.write((byte) packedFields);
-				bos.write((byte) 0); // Delay Time
-				bos.write((byte) 0); // Delay Time
-				bos.write((byte) (hasAlpha ? palette2.length() : 0)); // Transparent
-				// Color
-				// Index
-				bos.write((byte) 0); // terminator
-			}
-
-			if (null != xmpXml)
-			{
-				bos.write(EXTENSION_CODE);
-				bos.write(APPLICATION_EXTENSION_LABEL);
-
-				bos.write(XMP_APPLICATION_ID_AND_AUTH_CODE.length); // 0x0B
-				bos.write(XMP_APPLICATION_ID_AND_AUTH_CODE);
-
-				byte xmpXmlBytes[] = xmpXml.getBytes("utf-8");
-				bos.write(xmpXmlBytes);
-
-				// write "magic trailer"
-				for (int magic = 0; magic <= 0xff; magic++)
-					bos.write(0xff - magic);
-
-				bos.write((byte) 0); // terminator
-
-			}
-
-			{ // Image Descriptor.
-				bos.write(IMAGE_SEPARATOR);
-				bos.write2Bytes(0); // Image Left Position
-				bos.write2Bytes(0); // Image Top Position
-				bos.write2Bytes(width); // Image Width
-				bos.write2Bytes(height); // Image Height
-
-				{
-					boolean LocalColorTableFlag = true;
-					// boolean LocalColorTableFlag = false;
-					boolean InterlaceFlag = false;
-					boolean SortFlag = false;
-					int SizeOfLocalColorTable = colorTableScaleLessOne;
-
-					// int SizeOfLocalColorTable = 0;
-
-					int PackedFields = ((LocalColorTableFlag ? LOCAL_COLOR_TABLE_FLAG_MASK
-							: 0)
-							| (InterlaceFlag ? INTERLACE_FLAG_MASK : 0)
-							| (SortFlag ? SORT_FLAG_MASK : 0) | (7 & SizeOfLocalColorTable));
-					bos.write(PackedFields); // one byte
-				}
-			}
-
-			{ // write Local Color Table.
-				for (int i = 0; i < colorTableSizeInFormat; i++)
-				{
-					if (i < palette2.length())
-					{
-						int rgb = palette2.getEntry(i);
-
-						int red = 0xff & (rgb >> 16);
-						int green = 0xff & (rgb >> 8);
-						int blue = 0xff & (rgb >> 0);
-
-						bos.write(red);
-						bos.write(green);
-						bos.write(blue);
-					} else
-					{
-						bos.write(0);
-						bos.write(0);
-						bos.write(0);
-					}
-				}
-			}
-
-			{ // get Image Data.
-				int image_data_total = 0;
-
-				int LZWMinimumCodeSize = colorTableScaleLessOne + 1;
-//				LZWMinimumCodeSize = Math.max(8, LZWMinimumCodeSize);
-				if (LZWMinimumCodeSize < 2)
-					LZWMinimumCodeSize = 2;
-
-				// TODO:
-				// make
-				// better
-				// choice
-				// here.
-				bos.write(LZWMinimumCodeSize);
-
-				MyLZWCompressor compressor = new MyLZWCompressor(
-						LZWMinimumCodeSize, BYTE_ORDER_LSB, false); // GIF
-				// Mode);
-
-				byte imagedata[] = new byte[width * height];
-				for (int y = 0; y < height; y++)
-				{
-					for (int x = 0; x < width; x++)
-					{
-						int argb = src.getRGB(x, y);
-						int rgb = 0xffffff & argb;
-						int index;
-
-						if (hasAlpha)
-						{
-							int alpha = 0xff & (argb >> 24);
-							final int alphaThreshold = 255;
-							if (alpha < alphaThreshold)
-								index = palette2.length(); // is transparent
-							else
-								index = palette2.getPaletteIndex(rgb);
-						} else
-						{
-							index = palette2.getPaletteIndex(rgb);
-						}
-
-						imagedata[y * width + x] = (byte) index;
-					}
-				}
-
-				byte compressed[] = compressor.compress(imagedata);
-				writeAsSubBlocks(bos, compressed);
-				image_data_total += compressed.length;
-			}
-
-			// palette2.dump();
-
-			bos.write(TERMINATOR_BYTE);
-		}
-
-		bos.close();
-		os.close();
-	}
-
-	private static final byte XMP_APPLICATION_ID_AND_AUTH_CODE[] = { 0x58, // X
-			0x4D, // M
-			0x50, // P
-			0x20, //
-			0x44, // D
-			0x61, // a
-			0x74, // t
-			0x61, // a
-			0x58, // X
-			0x4D, // M
-			0x50, // P
-	};
-
-	/**
-	 * 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
-	{
-
-		InputStream is = null;
-		try
-		{
-			is = byteSource.getInputStream();
-
-			FormatCompliance formatCompliance = null;
-			GIFHeaderInfo ghi = readHeader(is, formatCompliance);
-
-			if (ghi.globalColorTableFlag)
-				readColorTable(is, ghi.sizeOfGlobalColorTable, formatCompliance);
-
-			ArrayList blocks = readBlocks(ghi, is, true, formatCompliance);
-
-			List result = new ArrayList();
-			for (int i = 0; i < blocks.size(); i++)
-			{
-				GIFBlock block = (GIFBlock) blocks.get(i);
-				if (block.blockCode != XMP_COMPLETE_CODE)
-					continue;
-
-				GenericGIFBlock genericBlock = (GenericGIFBlock) block;
-
-				byte blockBytes[] = genericBlock.appendSubBlocks(true);
-				if (blockBytes.length < XMP_APPLICATION_ID_AND_AUTH_CODE.length)
-					continue;
-
-				if (!compareByteArrays(blockBytes, 0,
-						XMP_APPLICATION_ID_AND_AUTH_CODE, 0,
-						XMP_APPLICATION_ID_AND_AUTH_CODE.length))
-					continue;
-
-				byte GIF_MAGIC_TRAILER[] = new byte[256];
-				for (int magic = 0; magic <= 0xff; magic++)
-					GIF_MAGIC_TRAILER[magic] = (byte) (0xff - magic);
-
-				if (blockBytes.length < XMP_APPLICATION_ID_AND_AUTH_CODE.length
-						+ GIF_MAGIC_TRAILER.length)
-					continue;
-				if (!compareByteArrays(blockBytes, blockBytes.length
-						- GIF_MAGIC_TRAILER.length, GIF_MAGIC_TRAILER, 0,
-						GIF_MAGIC_TRAILER.length))
-					throw new ImageReadException(
-							"XMP block in GIF missing magic trailer.");
-
-				try
-				{
-					// XMP is UTF-8 encoded xml.
-					String xml = new String(
-							blockBytes,
-							XMP_APPLICATION_ID_AND_AUTH_CODE.length,
-							blockBytes.length
-									- (XMP_APPLICATION_ID_AND_AUTH_CODE.length + GIF_MAGIC_TRAILER.length),
-							"utf-8");
-					result.add(xml);
-				} catch (UnsupportedEncodingException e)
-				{
-					throw new ImageReadException("Invalid XMP Block in GIF.");
-				}
-			}
-
-			if (result.size() < 1)
-				return null;
-			if (result.size() > 1)
-				throw new ImageReadException("More than one XMP Block in GIF.");
-			return (String) result.get(0);
-
-		} finally
-		{
-			try
-			{
-			    if (is != null) {
-			        is.close();
-			    }
-			} catch (Exception e)
-			{
-				Debug.debug(e);
-			}
+    public GifImageParser()
+    {
+        super.setByteOrder(BYTE_ORDER_LSB);
+    }
+
+    public String getName()
+    {
+        return "Gif-Custom";
+    }
+
+    public String getDefaultExtension()
+    {
+        return DEFAULT_EXTENSION;
+    }
+
+    private static final String DEFAULT_EXTENSION = ".gif";
+
+    private static final String ACCEPTED_EXTENSIONS[] = { DEFAULT_EXTENSION, };
+
+    protected String[] getAcceptedExtensions()
+    {
+        return ACCEPTED_EXTENSIONS;
+    }
+
+    protected ImageFormat[] getAcceptedTypes()
+    {
+        return new ImageFormat[] { ImageFormat.IMAGE_FORMAT_GIF, //
+        };
+    }
+
+    private static final byte GIF_HEADER_SIGNATURE[] = { 71, 73, 70 };
+
+    private GIFHeaderInfo readHeader(InputStream is,
+            FormatCompliance formatCompliance) throws ImageReadException,
+            IOException
+    {
+        byte identifier1 = readByte("identifier1", is, "Not a Valid GIF File");
+        byte identifier2 = readByte("identifier2", is, "Not a Valid GIF File");
+        byte identifier3 = readByte("identifier3", is, "Not a Valid GIF File");
+
+        byte version1 = readByte("version1", is, "Not a Valid GIF File");
+        byte version2 = readByte("version2", is, "Not a Valid GIF File");
+        byte version3 = readByte("version3", is, "Not a Valid GIF File");
+
+        if (formatCompliance != null)
+        {
+            formatCompliance.compare_bytes("Signature", GIF_HEADER_SIGNATURE,
+                    new byte[] { identifier1, identifier2, identifier3, });
+            formatCompliance.compare("version", 56, version1);
+            formatCompliance
+                    .compare("version", new int[] { 55, 57, }, version2);
+            formatCompliance.compare("version", 97, version3);
+        }
+
+        if (debug)
+            printCharQuad("identifier: ", ((identifier1 << 16)
+                    | (identifier2 << 8) | (identifier3 << 0)));
+        if (debug)
+            printCharQuad("version: ",
+                    ((version1 << 16) | (version2 << 8) | (version3 << 0)));
+
+        int logicalScreenWidth = read2Bytes("Logical Screen Width", is,
+                "Not a Valid GIF File");
+        int logicalScreenHeight = read2Bytes("Logical Screen Height", is,
+                "Not a Valid GIF File");
+
+        if (formatCompliance != null)
+        {
+            formatCompliance.checkBounds("Width", 1, Integer.MAX_VALUE,
+                    logicalScreenWidth);
+            formatCompliance.checkBounds("Height", 1, Integer.MAX_VALUE,
+                    logicalScreenHeight);
+        }
+
+        byte packedFields = readByte("Packed Fields", is,
+                "Not a Valid GIF File");
+        byte backgroundColorIndex = readByte("Background Color Index", is,
+                "Not a Valid GIF File");
+        byte pixelAspectRatio = readByte("Pixel Aspect Ratio", is,
+                "Not a Valid GIF File");
+
+        if (debug)
+            printByteBits("PackedFields bits", packedFields);
+
+        boolean globalColorTableFlag = ((packedFields & 128) > 0);
+        if (debug)
+            System.out.println("GlobalColorTableFlag: " + globalColorTableFlag);
+        byte colorResolution = (byte) ((packedFields >> 4) & 7);
+        if (debug)
+            System.out.println("ColorResolution: " + colorResolution);
+        boolean sortFlag = ((packedFields & 8) > 0);
+        if (debug)
+            System.out.println("SortFlag: " + sortFlag);
+        byte sizeofGlobalColorTable = (byte) (packedFields & 7);
+        if (debug)
+            System.out.println("SizeofGlobalColorTable: "
+                    + sizeofGlobalColorTable);
+
+        if (formatCompliance != null)
+        {
+            if (globalColorTableFlag && backgroundColorIndex != -1)
+                formatCompliance.checkBounds("Background Color Index", 0,
+                        convertColorTableSize(sizeofGlobalColorTable),
+                        backgroundColorIndex);
+        }
+
+        return new GIFHeaderInfo(identifier1, identifier2, identifier3,
+                version1, version2, version3, logicalScreenWidth,
+                logicalScreenHeight, packedFields, backgroundColorIndex,
+                pixelAspectRatio, globalColorTableFlag, colorResolution,
+                sortFlag, sizeofGlobalColorTable);
+    }
+
+    private GraphicControlExtension readGraphicControlExtension(int code,
+            InputStream is) throws ImageReadException, IOException
+    {
+        readByte("block_size", is, "GIF: corrupt GraphicControlExt");
+        int packed = readByte("packed fields", is,
+                "GIF: corrupt GraphicControlExt");
+
+        int dispose = (packed & 0x1c) >> 2; // disposal method
+        boolean transparency = (packed & 1) != 0;
+
+        int delay = read2Bytes("delay in milliseconds", is,
+                "GIF: corrupt GraphicControlExt");
+        int transparentColorIndex = 0xff & readByte("transparent color index",
+                is, "GIF: corrupt GraphicControlExt");
+        readByte("block terminator", is, "GIF: corrupt GraphicControlExt");
+
+        return new GraphicControlExtension(code, packed, dispose, transparency,
+                delay, transparentColorIndex);
+    }
+
+    private byte[] readSubBlock(InputStream is) throws ImageReadException,
+            IOException
+    {
+        int block_size = 0xff & readByte("block_size", is, "GIF: corrupt block");
+
+        byte bytes[] = readByteArray("block", block_size, is,
+                "GIF: corrupt block");
+
+        return bytes;
+    }
+
+    protected GenericGIFBlock readGenericGIFBlock(InputStream is, int code)
+            throws ImageReadException, IOException
+    {
+        return readGenericGIFBlock(is, code, null);
+    }
+
+    protected GenericGIFBlock readGenericGIFBlock(InputStream is, int code,
+            byte first[]) throws ImageReadException, IOException
+    {
+        ArrayList subblocks = new ArrayList();
+
+        if (first != null)
+            subblocks.add(first);
+
+        while (true)
+        {
+            byte bytes[] = readSubBlock(is);
+            if (bytes.length < 1)
+                break;
+            subblocks.add(bytes);
+        }
+
+        return new GenericGIFBlock(code, subblocks);
+    }
+
+    private final static int EXTENSION_CODE = 0x21;
+    private final static int IMAGE_SEPARATOR = 0x2C;
+    private final static int GRAPHIC_CONTROL_EXTENSION = (EXTENSION_CODE << 8) | 0xf9;
+    private final static int COMMENT_EXTENSION = 0xfe;
+    private final static int PLAIN_TEXT_EXTENSION = 0x01;
+    private final static int XMP_EXTENSION = 0xff;
+    private final static int TERMINATOR_BYTE = 0x3b;
+    private final static int APPLICATION_EXTENSION_LABEL = 0xff;
+    private final static int XMP_COMPLETE_CODE = (EXTENSION_CODE << 8)
+            | XMP_EXTENSION;
+
+    private ArrayList readBlocks(GIFHeaderInfo ghi, InputStream is,
+            boolean stopBeforeImageData, FormatCompliance formatCompliance)
+            throws ImageReadException, IOException
+    {
+        ArrayList result = new ArrayList();
+
+        while (true)
+        {
+            int code = is.read();
+
+            switch (code)
+            {
+            case -1:
+                throw new ImageReadException("GIF: unexpected end of data");
+
+            case IMAGE_SEPARATOR:
+                ImageDescriptor id = readImageDescriptor(ghi, code, is,
+                        stopBeforeImageData, formatCompliance);
+                result.add(id);
+                // if(stopBeforeImageData)
+                // return result;
+
+                break;
+
+            case EXTENSION_CODE: // extension
+            {
+                int extensionCode = is.read();
+                int completeCode = ((0xff & code) << 8)
+                        | (0xff & extensionCode);
+
+                switch (extensionCode)
+                {
+                case 0xf9:
+                    GraphicControlExtension gce = readGraphicControlExtension(
+                            completeCode, is);
+                    result.add(gce);
+                    break;
+
+                case COMMENT_EXTENSION:
+                case PLAIN_TEXT_EXTENSION: {
+                    GenericGIFBlock block = readGenericGIFBlock(is,
+                            completeCode);
+                    result.add(block);
+                    break;
+                }
+
+                case APPLICATION_EXTENSION_LABEL: // 255 (hex 0xFF) Application
+                    // Extension Label
+                {
+                    byte label[] = readSubBlock(is);
+
+                    if (formatCompliance != null)
+                        formatCompliance
+                                .addComment("Unknown Application Extension ("
+                                        + new String(label) + ")", completeCode);
+
+                    // if (label == new String("ICCRGBG1"))
+                    {
+                        // GIF's can have embedded ICC Profiles - who knew?
+                    }
+
+                    if ((label != null) && (label.length > 0))
+                    {
+                        GenericGIFBlock block = readGenericGIFBlock(is,
+                                completeCode, label);
+                        result.add(block);
+                    }
+                    break;
+                }
+
+                default: {
+
+                    if (formatCompliance != null)
+                        formatCompliance.addComment("Unknown block",
+                                completeCode);
+
+                    GenericGIFBlock block = readGenericGIFBlock(is,
+                            completeCode);
+                    result.add(block);
+                    break;
+                }
+                }
+            }
+                break;
+
+            case TERMINATOR_BYTE:
+                return result;
+
+            case 0x00: // bad byte, but keep going and see what happens
+                break;
+
+            default:
+                throw new ImageReadException("GIF: unknown code: " + code);
+            }
+        }
+    }
+
+    private ImageDescriptor readImageDescriptor(GIFHeaderInfo ghi,
+            int blockCode, InputStream is, boolean stopBeforeImageData,
+            FormatCompliance formatCompliance) throws ImageReadException,
+            IOException
+    {
+        int ImageLeftPosition = read2Bytes("Image Left Position", is,
+                "Not a Valid GIF File");
+        int ImageTopPosition = read2Bytes("Image Top Position", is,
+                "Not a Valid GIF File");
+        int imageWidth = read2Bytes("Image Width", is, "Not a Valid GIF File");
+        int imageHeight = read2Bytes("Image Height", is, "Not a Valid GIF File");
+        byte PackedFields = readByte("Packed Fields", is,
+                "Not a Valid GIF File");
+
+        if (formatCompliance != null)
+        {
+            formatCompliance.checkBounds("Width", 1, ghi.logicalScreenWidth,
+                    imageWidth);
+            formatCompliance.checkBounds("Height", 1, ghi.logicalScreenHeight,
+                    imageHeight);
+            formatCompliance.checkBounds("Left Position", 0,
+                    ghi.logicalScreenWidth - imageWidth, ImageLeftPosition);
+            formatCompliance.checkBounds("Top Position", 0,
+                    ghi.logicalScreenHeight - imageHeight, ImageTopPosition);
+        }
+
+        if (debug)
+            printByteBits("PackedFields bits", PackedFields);
+
+        boolean LocalColorTableFlag = (((PackedFields >> 7) & 1) > 0);
+        if (debug)
+            System.out.println("LocalColorTableFlag: " + LocalColorTableFlag);
+        boolean InterlaceFlag = (((PackedFields >> 6) & 1) > 0);
+        if (debug)
+            System.out.println("Interlace Flag: " + InterlaceFlag);
+        boolean SortFlag = (((PackedFields >> 5) & 1) > 0);
+        if (debug)
+            System.out.println("Sort  Flag: " + SortFlag);
+
+        byte SizeofLocalColorTable = (byte) (PackedFields & 7);
+        if (debug)
+            System.out.println("SizeofLocalColorTable: "
+                    + SizeofLocalColorTable);
+
+        byte LocalColorTable[] = null;
+        if (LocalColorTableFlag)
+            LocalColorTable = readColorTable(is, SizeofLocalColorTable,
+                    formatCompliance);
+
+        byte imageData[] = null;
+        if (!stopBeforeImageData)
+        {
+            int LZWMinimumCodeSize = is.read();
+
+            GenericGIFBlock block = readGenericGIFBlock(is, -1);
+            byte bytes[] = block.appendSubBlocks();
+            InputStream bais = new ByteArrayInputStream(bytes);
+
+            int size = imageWidth * imageHeight;
+            MyLZWDecompressor myLzwDecompressor = new MyLZWDecompressor(
+                    LZWMinimumCodeSize, BYTE_ORDER_LSB);
+            imageData = myLzwDecompressor.decompress(bais, size);
+        } else
+        {
+            int LZWMinimumCodeSize = is.read();
+            if (debug)
+                System.out.println("LZWMinimumCodeSize: " + LZWMinimumCodeSize);
+
+            readGenericGIFBlock(is, -1);
+        }
+
+        ImageDescriptor result = new ImageDescriptor(blockCode,
+                ImageLeftPosition, ImageTopPosition, imageWidth, imageHeight,
+                PackedFields, LocalColorTableFlag, InterlaceFlag, SortFlag,
+                SizeofLocalColorTable, LocalColorTable, imageData);
+
+        return result;
+    }
+
+    private int simple_pow(int base, int power)
+    {
+        int result = 1;
+
+        for (int i = 0; i < power; i++)
+            result *= base;
+
+        return result;
+    }
+
+    private int convertColorTableSize(int ct_size)
+    {
+        return 3 * simple_pow(2, ct_size + 1);
+    }
+
+    private byte[] readColorTable(InputStream is, int ct_size,
+            FormatCompliance formatCompliance) throws IOException
+    {
+        int actual_size = convertColorTableSize(ct_size);
+
+        byte bytes[] = readByteArray("block", actual_size, is,
+                "GIF: corrupt Color Table");
+
+        return bytes;
+    }
+
+    // TODO - unused
+    private GIFHeaderInfo readHeader(ByteSource byteSource)
+            throws ImageReadException, IOException
+    {
+        InputStream is = null;
+        try
+        {
+            is = byteSource.getInputStream();
+
+            return readHeader(is, FormatCompliance.getDefault());
+        } finally
+        {
+            try
+            {
+                if (is != null) {
+                    is.close();
+                }
+            } catch (Exception e)
+            {
+                Debug.debug(e);
+            }
+
+        }
+    }
+
+    private GIFBlock findBlock(ArrayList v, int code)
+    {
+        for (int i = 0; i < v.size(); i++)
+        {
+            GIFBlock gifBlock = (GIFBlock) v.get(i);
+            if (gifBlock.blockCode == code)
+                return gifBlock;
+        }
+        return null;
+    }
+
+    private ImageContents readFile(ByteSource byteSource,
+            boolean stopBeforeImageData) throws ImageReadException, IOException
+    {
+        return readFile(byteSource, stopBeforeImageData, FormatCompliance
+                .getDefault());
+    }
+
+    private ImageContents readFile(ByteSource byteSource,
+            boolean stopBeforeImageData, FormatCompliance formatCompliance)
+            throws ImageReadException, IOException
+    {
+        InputStream is = null;
+        try
+        {
+            is = byteSource.getInputStream();
+
+            GIFHeaderInfo ghi = readHeader(is, formatCompliance);
+
+            byte globalColorTable[] = null;
+            if (ghi.globalColorTableFlag)
+                globalColorTable = readColorTable(is,
+                        ghi.sizeOfGlobalColorTable, formatCompliance);
+
+            ArrayList blocks = readBlocks(ghi, is, stopBeforeImageData,
+                    formatCompliance);
+
+            ImageContents result = new ImageContents(ghi, globalColorTable,
+                    blocks);
+
+            return result;
+        } finally
+        {
+            try
+            {
+                if (is != null) {
+                    is.close();
+                }
+            } catch (Exception e)
+            {
+                Debug.debug(e);
+            }
+
+        }
+    }
+
+    public byte[] getICCProfileBytes(ByteSource byteSource, Map params)
+            throws ImageReadException, IOException
+    {
+        return null;
+    }
+
+    public Dimension getImageSize(ByteSource byteSource, Map params)
+            throws ImageReadException, IOException
+    {
+        ImageContents blocks = readFile(byteSource, false);
+
+        if (blocks == null)
+            throw new ImageReadException("GIF: Couldn't read blocks");
+
+        GIFHeaderInfo bhi = blocks.gifHeaderInfo;
+        if (bhi == null)
+            throw new ImageReadException("GIF: Couldn't read Header");
+
+        ImageDescriptor id = (ImageDescriptor) findBlock(blocks.blocks,
+                IMAGE_SEPARATOR);
+        if (id == null)
+            throw new ImageReadException("GIF: Couldn't read ImageDescriptor");
+
+        // Prefer the size information in the ImageDescriptor; it is more reliable
+        // than the size information in the header.
+        return new Dimension(id.imageWidth, id.imageHeight);
+    }
+
+    public byte[] embedICCProfile(byte image[], byte profile[])
+    {
+        return null;
+    }
+
+    public boolean embedICCProfile(File src, File dst, byte profile[])
+    {
+        return false;
+    }
+
+    public IImageMetadata getMetadata(ByteSource byteSource, Map params)
+            throws ImageReadException, IOException
+    {
+        return null;
+    }
+
+    private ArrayList getComments(ArrayList v) throws IOException
+    {
+        ArrayList result = new ArrayList();
+        int code = 0x21fe;
+
+        for (int i = 0; i < v.size(); i++)
+        {
+            GIFBlock block = (GIFBlock) v.get(i);
+            if (block.blockCode == code)
+            {
+                byte bytes[] = ((GenericGIFBlock) block).appendSubBlocks();
+                result.add(new String(bytes));
+            }
+        }
+
+        return result;
+    }
+
+    public ImageInfo getImageInfo(ByteSource byteSource, Map params)
+            throws ImageReadException, IOException
+    {
+        ImageContents blocks = readFile(byteSource, false);
+
+        if (blocks == null)
+            throw new ImageReadException("GIF: Couldn't read blocks");
+
+        GIFHeaderInfo bhi = blocks.gifHeaderInfo;
+        if (bhi == null)
+            throw new ImageReadException("GIF: Couldn't read Header");
+
+        ImageDescriptor id = (ImageDescriptor) findBlock(blocks.blocks,
+                IMAGE_SEPARATOR);
+        if (id == null)
+            throw new ImageReadException("GIF: Couldn't read ImageDescriptor");
+
+        GraphicControlExtension gce = (GraphicControlExtension) findBlock(
+                blocks.blocks, GRAPHIC_CONTROL_EXTENSION);
+
+        // Prefer the size information in the ImageDescriptor; it is more reliable
+        // than the size information in the header.
+        int height = id.imageWidth;
+        int width = id.imageHeight;
+
+        ArrayList Comments;
+
+        Comments = getComments(blocks.blocks);
+
+        int BitsPerPixel = (bhi.colorResolution + 1) * 3;
+        ImageFormat Format = ImageFormat.IMAGE_FORMAT_GIF;
+        String FormatName = "GIF Graphics Interchange Format";
+        String MimeType = "image/gif";
+        // we ought to count images, but don't yet.
+        int NumberOfImages = -1;
+
+        boolean isProgressive = id.interlaceFlag;
+
+        int PhysicalWidthDpi = 72;
+        float PhysicalWidthInch = (float) ((double) width / (double) PhysicalWidthDpi);
+        int PhysicalHeightDpi = 72;
+        float PhysicalHeightInch = (float) ((double) height / (double) PhysicalHeightDpi);
+
+        String FormatDetails = "Gif " + ((char) blocks.gifHeaderInfo.version1)
+                + ((char) blocks.gifHeaderInfo.version2)
+                + ((char) blocks.gifHeaderInfo.version3);
+
+        boolean isTransparent = false;
+        if (gce != null && gce.transparency)
+            isTransparent = true;
+
+        boolean usesPalette = true;
+        int colorType = ImageInfo.COLOR_TYPE_RGB;
+        String compressionAlgorithm = ImageInfo.COMPRESSION_ALGORITHM_LZW;
+
+        ImageInfo result = new ImageInfo(FormatDetails, BitsPerPixel, Comments,
+                Format, FormatName, height, MimeType, NumberOfImages,
+                PhysicalHeightDpi, PhysicalHeightInch, PhysicalWidthDpi,
+                PhysicalWidthInch, width, isProgressive, isTransparent,
+                usesPalette, colorType, compressionAlgorithm);
+
+        return result;
+    }
+
+    public boolean dumpImageFile(PrintWriter pw, ByteSource byteSource)
+            throws ImageReadException, IOException
+    {
+        pw.println("gif.dumpImageFile");
+
+        {
+            ImageInfo imageData = getImageInfo(byteSource);
+            if (imageData == null)
+                return false;
+
+            imageData.toString(pw, "");
+        }
+        {
+            ImageContents blocks = readFile(byteSource, false);
+
+            if (blocks == null)
+                return false;
+
+            pw.println("gif.blocks: " + blocks.blocks.size());
+            for (int i = 0; i < blocks.blocks.size(); i++)
+            {
+                GIFBlock gifBlock = (GIFBlock) blocks.blocks.get(i);
+                this.debugNumber(pw, "\t" + i + " ("
+                        + gifBlock.getClass().getName() + ")",
+                        gifBlock.blockCode, 4);
+            }
+
+        }
+
+        pw.println("");
+
+        return true;
+    }
+
+    private int[] getColorTable(byte bytes[]) throws ImageReadException
+    {
+        if ((bytes.length % 3) != 0)
+            throw new ImageReadException("Bad Color Table Length: "
+                    + bytes.length);
+        int length = bytes.length / 3;
+
+        int result[] = new int[length];
+
+        for (int i = 0; i < length; i++)
+        {
+            int red = 0xff & bytes[(i * 3) + 0];
+            int green = 0xff & bytes[(i * 3) + 1];
+            int blue = 0xff & bytes[(i * 3) + 2];
+
+            int alpha = 0xff;
+
+            int rgb = (alpha << 24) | (red << 16) | (green << 8) | (blue << 0);
+            result[i] = rgb;
+        }
+
+        return result;
+    }
+
+    public FormatCompliance getFormatCompliance(ByteSource byteSource)
+            throws ImageReadException, IOException
+    {
+        FormatCompliance result = new FormatCompliance(byteSource
+                .getDescription());
+
+        readFile(byteSource, false, result);
+
+        return result;
+    }
+
+    public BufferedImage getBufferedImage(ByteSource byteSource, Map params)
+            throws ImageReadException, IOException
+    {
+        ImageContents imageContents = readFile(byteSource, false);
+
+        if (imageContents == null)
+            throw new ImageReadException("GIF: Couldn't read blocks");
+
+        GIFHeaderInfo ghi = imageContents.gifHeaderInfo;
+        if (ghi == null)
+            throw new ImageReadException("GIF: Couldn't read Header");
+
+        ImageDescriptor id = (ImageDescriptor) findBlock(imageContents.blocks,
+                IMAGE_SEPARATOR);
+        if (id == null)
+            throw new ImageReadException("GIF: Couldn't read Image Descriptor");
+        GraphicControlExtension gce = (GraphicControlExtension) findBlock(
+                imageContents.blocks, GRAPHIC_CONTROL_EXTENSION);
+
+        // Prefer the size information in the ImageDescriptor; it is more reliable
+        // than the size information in the header.
+        int width = id.imageWidth;
+        int height = id.imageHeight;
+
+        boolean hasAlpha = false;
+        if (gce != null && gce.transparency)
+            hasAlpha = true;
+
+        BufferedImage result = getBufferedImageFactory(params)
+                .getColorBufferedImage(width, height, hasAlpha);
+
+        {
+            int colorTable[];
+            if (id.localColorTable != null)
+                colorTable = getColorTable(id.localColorTable);
+            else if (imageContents.globalColorTable != null)
+                colorTable = getColorTable(imageContents.globalColorTable);
+            else
+                throw new ImageReadException("Gif: No Color Table");
+
+            int transparentIndex = -1;
+            if (hasAlpha)
+                transparentIndex = gce.transparentColorIndex;
+
+            int counter = 0;
+
+            int rowsInPass1 = (height + 7) / 8;
+            int rowsInPass2 = (height + 3) / 8;
+            int rowsInPass3 = (height + 1) / 4;
+            int rowsInPass4 = (height) / 2;
+
+            DataBuffer db = result.getRaster().getDataBuffer();
+
+            for (int row = 0; row < height; row++)
+            {
+                int y;
+                if (id.interlaceFlag)
+                {
+                    int the_row = row;
+                    if (the_row < rowsInPass1)
+                        y = the_row * 8;
+                    else
+                    {
+                        the_row -= rowsInPass1;
+                        if (the_row < (rowsInPass2))
+                            y = 4 + (the_row * 8);
+                        else
+                        {
+                            the_row -= rowsInPass2;
+                            if (the_row < (rowsInPass3))
+                                y = 2 + (the_row * 4);
+                            else
+                            {
+                                the_row -= rowsInPass3;
+                                if (the_row < (rowsInPass4))
+                                    y = 1 + (the_row * 2);
+                                else
+                                    throw new ImageReadException(
+                                            "Gif: Strange Row");
+                            }
+                        }
+                    }
+                } else
+                    y = row;
+
+                for (int x = 0; x < width; x++)
+                {
+                    int index = 0xff & id.imageData[counter++];
+                    int rgb = colorTable[index];
+
+                    if (transparentIndex == index)
+                        rgb = 0x00;
+
+                    db.setElem(y * width + x, rgb);
+                }
+
+            }
+        }
+
+        return result;
+
+    }
+
+    private void writeAsSubBlocks(OutputStream os, byte bytes[])
+            throws IOException
+    {
+        int index = 0;
+
+        while (index < bytes.length)
+        {
+            int block_size = Math.min(bytes.length - index, 255);
+            os.write(block_size);
+            os.write(bytes, index, block_size);
+            index += block_size;
+        }
+        os.write(0); // last block
+    }
+
+    private static final int LOCAL_COLOR_TABLE_FLAG_MASK = 1 << 7;
+    private static final int INTERLACE_FLAG_MASK = 1 << 6;
+    private static final int SORT_FLAG_MASK = 1 << 5;
+
+    public void writeImage(BufferedImage src, OutputStream os, Map params)
+            throws ImageWriteException, IOException
+    {
+        // make copy of params; we'll clear keys as we consume them.
+        params = new HashMap(params);
+
+        boolean verbose = ParamMap.getParamBoolean(params, PARAM_KEY_VERBOSE,
+                false);
+
+        // clear format key.
+        if (params.containsKey(PARAM_KEY_FORMAT))
+            params.remove(PARAM_KEY_FORMAT);
+        if (params.containsKey(PARAM_KEY_VERBOSE))
+            params.remove(PARAM_KEY_VERBOSE);
+
+        String xmpXml = null;
+        if (params.containsKey(PARAM_KEY_XMP_XML))
+        {
+            xmpXml = (String) params.get(PARAM_KEY_XMP_XML);
+            params.remove(PARAM_KEY_XMP_XML);
+        }
+
+        if (params.size() > 0)
+        {
+            Object firstKey = params.keySet().iterator().next();
+            throw new ImageWriteException("Unknown parameter: " + firstKey);
+        }
+
+        int width = src.getWidth();
+        int height = src.getHeight();
+
+        boolean hasAlpha = new PaletteFactory().hasTransparency(src);
+
+        int max_colors = hasAlpha ? 255 : 256;
+
+        Palette palette2 = new PaletteFactory().makePaletteSimple(src,
+                max_colors);
+        // int palette[] = new PaletteFactory().makePaletteSimple(src, 256);
+        // Map palette_map = paletteToMap(palette);
+
+        if (palette2 == null)
+        {
+            palette2 = new PaletteFactory().makePaletteQuantized(src,
+                    max_colors);
+            if (verbose)
+                System.out.println("quantizing");
+        } else if (verbose)
+            System.out.println("exact palette");
+
+        if (palette2 == null)
+            throw new ImageWriteException(
+                    "Gif: can't write images with more than 256 colors");
+        int palette_size = palette2.length() + (hasAlpha ? 1 : 0);
+
+        BinaryOutputStream bos = new BinaryOutputStream(os, BYTE_ORDER_LSB);
+
+        {
+            // write Header
+            os.write(0x47); // G magic numbers
+            os.write(0x49); // I
+            os.write(0x46); // F
+
+            os.write(0x38); // 8 version magic numbers
+            os.write(0x39); // 9
+            os.write(0x61); // a
+
+            // Logical Screen Descriptor.
+
+            bos.write2Bytes(width);
+            bos.write2Bytes(height);
+
+            int colorTableScaleLessOne = (palette_size > 128) ? 7
+                    : (palette_size > 64) ? 6 : (palette_size > 32) ? 5
+                            : (palette_size > 16) ? 4 : (palette_size > 8) ? 3
+                                    : (palette_size > 4) ? 2
+                                            : (palette_size > 2) ? 1 : 0;
+
+            int colorTableSizeInFormat = 1 << (colorTableScaleLessOne + 1);
+            int actual_size = 3 * simple_pow(2, colorTableScaleLessOne + 1);
+            {
+                byte colorResolution = (byte) colorTableScaleLessOne; // TODO:
+
+                boolean globalColorTableFlag = false;
+                boolean sortFlag = false;
+                int globalColorTableFlagMask = 1 << 7;
+                int sortFlagMask = 8;
+                int sizeOfGlobalColorTable = 0;
+
+                int packedFields = ((globalColorTableFlag ? globalColorTableFlagMask
+                        : 0)
+                        | (sortFlag ? sortFlagMask : 0)
+                        | ((7 & colorResolution) << 4) | (7 & sizeOfGlobalColorTable));
+                bos.write(packedFields); // one byte
+            }
+            {
+                byte BackgroundColorIndex = 0;
+                bos.write(BackgroundColorIndex);
+            }
+            {
+                byte PixelAspectRatio = 0;
+                bos.write(PixelAspectRatio);
+            }
+
+            { // write Global Color Table.
+
+            }
+
+            { // ALWAYS write GraphicControlExtension
+                bos.write(EXTENSION_CODE);
+                bos.write((byte) 0xf9);
+                // bos.write(0xff & (kGraphicControlExtension >> 8));
+                // bos.write(0xff & (kGraphicControlExtension >> 0));
+
+                bos.write((byte) 4); // block size;
+                int packedFields = hasAlpha ? 1 : 0; // transparency flag
+                bos.write((byte) packedFields);
+                bos.write((byte) 0); // Delay Time
+                bos.write((byte) 0); // Delay Time
+                bos.write((byte) (hasAlpha ? palette2.length() : 0)); // Transparent
+                // Color
+                // Index
+                bos.write((byte) 0); // terminator
+            }
+
+            if (null != xmpXml)
+            {
+                bos.write(EXTENSION_CODE);
+                bos.write(APPLICATION_EXTENSION_LABEL);
+
+                bos.write(XMP_APPLICATION_ID_AND_AUTH_CODE.length); // 0x0B
+                bos.write(XMP_APPLICATION_ID_AND_AUTH_CODE);
+
+                byte xmpXmlBytes[] = xmpXml.getBytes("utf-8");
+                bos.write(xmpXmlBytes);
+
+                // write "magic trailer"
+                for (int magic = 0; magic <= 0xff; magic++)
+                    bos.write(0xff - magic);
+
+                bos.write((byte) 0); // terminator
+
+            }
+
+            { // Image Descriptor.
+                bos.write(IMAGE_SEPARATOR);
+                bos.write2Bytes(0); // Image Left Position
+                bos.write2Bytes(0); // Image Top Position
+                bos.write2Bytes(width); // Image Width
+                bos.write2Bytes(height); // Image Height
+
+                {
+                    boolean LocalColorTableFlag = true;
+                    // boolean LocalColorTableFlag = false;
+                    boolean InterlaceFlag = false;
+                    boolean SortFlag = false;
+                    int SizeOfLocalColorTable = colorTableScaleLessOne;
+
+                    // int SizeOfLocalColorTable = 0;
+
+                    int PackedFields = ((LocalColorTableFlag ? LOCAL_COLOR_TABLE_FLAG_MASK
+                            : 0)
+                            | (InterlaceFlag ? INTERLACE_FLAG_MASK : 0)
+                            | (SortFlag ? SORT_FLAG_MASK : 0) | (7 & SizeOfLocalColorTable));
+                    bos.write(PackedFields); // one byte
+                }
+            }
+
+            { // write Local Color Table.
+                for (int i = 0; i < colorTableSizeInFormat; i++)
+                {
+                    if (i < palette2.length())
+                    {
+                        int rgb = palette2.getEntry(i);
+
+                        int red = 0xff & (rgb >> 16);
+                        int green = 0xff & (rgb >> 8);
+                        int blue = 0xff & (rgb >> 0);
+
+                        bos.write(red);
+                        bos.write(green);
+                        bos.write(blue);
+                    } else
+                    {
+                        bos.write(0);
+                        bos.write(0);
+                        bos.write(0);
+                    }
+                }
+            }
+
+            { // get Image Data.
+                int image_data_total = 0;
+
+                int LZWMinimumCodeSize = colorTableScaleLessOne + 1;
+//                LZWMinimumCodeSize = Math.max(8, LZWMinimumCodeSize);
+                if (LZWMinimumCodeSize < 2)
+                    LZWMinimumCodeSize = 2;
+
+                // TODO:
+                // make
+                // better
+                // choice
+                // here.
+                bos.write(LZWMinimumCodeSize);
+
+                MyLZWCompressor compressor = new MyLZWCompressor(
+                        LZWMinimumCodeSize, BYTE_ORDER_LSB, false); // GIF
+                // Mode);
+
+                byte imagedata[] = new byte[width * height];
+                for (int y = 0; y < height; y++)
+                {
+                    for (int x = 0; x < width; x++)
+                    {
+                        int argb = src.getRGB(x, y);
+                        int rgb = 0xffffff & argb;
+                        int index;
+
+                        if (hasAlpha)
+                        {
+                            int alpha = 0xff & (argb >> 24);
+                            final int alphaThreshold = 255;
+                            if (alpha < alphaThreshold)
+                                index = palette2.length(); // is transparent
+                            else
+                                index = palette2.getPaletteIndex(rgb);
+                        } else
+                        {
+                            index = palette2.getPaletteIndex(rgb);
+                        }
+
+                        imagedata[y * width + x] = (byte) index;
+                    }
+                }
+
+                byte compressed[] = compressor.compress(imagedata);
+                writeAsSubBlocks(bos, compressed);
+                image_data_total += compressed.length;
+            }
+
+            // palette2.dump();
+
+            bos.write(TERMINATOR_BYTE);
+        }
+
+        bos.close();
+        os.close();
+    }
+
+    private static final byte XMP_APPLICATION_ID_AND_AUTH_CODE[] = { 0x58, // X
+            0x4D, // M
+            0x50, // P
+            0x20, //
+            0x44, // D
+            0x61, // a
+            0x74, // t
+            0x61, // a
+            0x58, // X
+            0x4D, // M
+            0x50, // P
+    };
+
+    /**
+     * 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
+    {
+
+        InputStream is = null;
+        try
+        {
+            is = byteSource.getInputStream();
+
+            FormatCompliance formatCompliance = null;
+            GIFHeaderInfo ghi = readHeader(is, formatCompliance);
+
+            if (ghi.globalColorTableFlag)
+                readColorTable(is, ghi.sizeOfGlobalColorTable, formatCompliance);
+
+            ArrayList blocks = readBlocks(ghi, is, true, formatCompliance);
+
+            List result = new ArrayList();
+            for (int i = 0; i < blocks.size(); i++)
+            {
+                GIFBlock block = (GIFBlock) blocks.get(i);
+                if (block.blockCode != XMP_COMPLETE_CODE)
+                    continue;
+
+                GenericGIFBlock genericBlock = (GenericGIFBlock) block;
+
+                byte blockBytes[] = genericBlock.appendSubBlocks(true);
+                if (blockBytes.length < XMP_APPLICATION_ID_AND_AUTH_CODE.length)
+                    continue;
+
+                if (!compareByteArrays(blockBytes, 0,
+                        XMP_APPLICATION_ID_AND_AUTH_CODE, 0,
+                        XMP_APPLICATION_ID_AND_AUTH_CODE.length))
+                    continue;
+
+                byte GIF_MAGIC_TRAILER[] = new byte[256];
+                for (int magic = 0; magic <= 0xff; magic++)
+                    GIF_MAGIC_TRAILER[magic] = (byte) (0xff - magic);
+
+                if (blockBytes.length < XMP_APPLICATION_ID_AND_AUTH_CODE.length
+                        + GIF_MAGIC_TRAILER.length)
+                    continue;
+                if (!compareByteArrays(blockBytes, blockBytes.length
+                        - GIF_MAGIC_TRAILER.length, GIF_MAGIC_TRAILER, 0,
+                        GIF_MAGIC_TRAILER.length))
+                    throw new ImageReadException(
+                            "XMP block in GIF missing magic trailer.");
+
+                try
+                {
+                    // XMP is UTF-8 encoded xml.
+                    String xml = new String(
+                            blockBytes,
+                            XMP_APPLICATION_ID_AND_AUTH_CODE.length,
+                            blockBytes.length
+                                    - (XMP_APPLICATION_ID_AND_AUTH_CODE.length + GIF_MAGIC_TRAILER.length),
+                            "utf-8");
+                    result.add(xml);
+                } catch (UnsupportedEncodingException e)
+                {
+                    throw new ImageReadException("Invalid XMP Block in GIF.");
+                }
+            }
+
+            if (result.size() < 1)
+                return null;
+            if (result.size() > 1)
+                throw new ImageReadException("More than one XMP Block in GIF.");
+            return (String) result.get(0);
+
+        } finally
+        {
+            try
+            {
+                if (is != null) {
+                    is.close();
+                }
+            } catch (Exception e)
+            {
+                Debug.debug(e);
+            }
 
-		}
-	}
+        }
+    }
 }
\ No newline at end of file

Modified: commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/gif/GraphicControlExtension.java
URL: http://svn.apache.org/viewvc/commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/gif/GraphicControlExtension.java?rev=995859&r1=995858&r2=995859&view=diff
==============================================================================
--- commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/gif/GraphicControlExtension.java (original)
+++ commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/gif/GraphicControlExtension.java Fri Sep 10 16:33:35 2010
@@ -19,23 +19,23 @@ package org.apache.sanselan.formats.gif;
 class GraphicControlExtension extends GIFBlock
 {
 
-	public final int packed;
-	public final int dispose;
-	public final boolean transparency;
-	public final int delay;
-	public final int transparentColorIndex;
+    public final int packed;
+    public final int dispose;
+    public final boolean transparency;
+    public final int delay;
+    public final int transparentColorIndex;
 
-	public GraphicControlExtension(int blockCode, int packed, int dispose,
-			boolean transparency, int delay, int transparentColorIndex)
-	{
-		super(blockCode);
+    public GraphicControlExtension(int blockCode, int packed, int dispose,
+            boolean transparency, int delay, int transparentColorIndex)
+    {
+        super(blockCode);
 
-		this.packed = packed;
-		this.dispose = dispose;
-		this.transparency = transparency;
-		this.delay = delay;
-		this.transparentColorIndex = transparentColorIndex;
+        this.packed = packed;
+        this.dispose = dispose;
+        this.transparency = transparency;
+        this.delay = delay;
+        this.transparentColorIndex = transparentColorIndex;
 
-	}
+    }
 
 }
\ No newline at end of file

Modified: commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/gif/ImageContents.java
URL: http://svn.apache.org/viewvc/commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/gif/ImageContents.java?rev=995859&r1=995858&r2=995859&view=diff
==============================================================================
--- commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/gif/ImageContents.java (original)
+++ commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/gif/ImageContents.java Fri Sep 10 16:33:35 2010
@@ -20,16 +20,16 @@ import java.util.ArrayList;
 
 class ImageContents
 {
-	public final GIFHeaderInfo gifHeaderInfo;
+    public final GIFHeaderInfo gifHeaderInfo;
 
-	public final ArrayList blocks;
-	public final byte globalColorTable[];
+    public final ArrayList blocks;
+    public final byte globalColorTable[];
 
-	public ImageContents(GIFHeaderInfo gifHeaderInfo, byte globalColorTable[],
-			ArrayList blocks)
-	{
-		this.gifHeaderInfo = gifHeaderInfo;
-		this.globalColorTable = globalColorTable;
-		this.blocks = blocks;
-	}
+    public ImageContents(GIFHeaderInfo gifHeaderInfo, byte globalColorTable[],
+            ArrayList blocks)
+    {
+        this.gifHeaderInfo = gifHeaderInfo;
+        this.globalColorTable = globalColorTable;
+        this.blocks = blocks;
+    }
 }
\ No newline at end of file

Modified: commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/gif/ImageDescriptor.java
URL: http://svn.apache.org/viewvc/commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/gif/ImageDescriptor.java?rev=995859&r1=995858&r2=995859&view=diff
==============================================================================
--- commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/gif/ImageDescriptor.java (original)
+++ commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/gif/ImageDescriptor.java Fri Sep 10 16:33:35 2010
@@ -19,39 +19,39 @@ package org.apache.sanselan.formats.gif;
 public class ImageDescriptor extends GIFBlock
 {
 
-	public final int imageLeftPosition;
-	public final int imageTopPosition;
-	public final int imageWidth;
-	public final int imageHeight;
-	public final byte packedFields;
-	public final boolean localColorTableFlag;
-	public final boolean interlaceFlag;
-	public final boolean sortFlag;
-	public final byte sizeOfLocalColorTable;
+    public final int imageLeftPosition;
+    public final int imageTopPosition;
+    public final int imageWidth;
+    public final int imageHeight;
+    public final byte packedFields;
+    public final boolean localColorTableFlag;
+    public final boolean interlaceFlag;
+    public final boolean sortFlag;
+    public final byte sizeOfLocalColorTable;
 
-	public final byte localColorTable[];
-	public final byte imageData[];
+    public final byte localColorTable[];
+    public final byte imageData[];
 
-	public ImageDescriptor(int blockCode, int ImageLeftPosition,
-			int ImageTopPosition, int ImageWidth, int ImageHeight,
-			byte PackedFields, boolean LocalColorTableFlag,
-			boolean InterlaceFlag, boolean SortFlag,
-			byte SizeofLocalColorTable, byte LocalColorTable[],
-			byte ImageData[])
-	{
-		super(blockCode);
+    public ImageDescriptor(int blockCode, int ImageLeftPosition,
+            int ImageTopPosition, int ImageWidth, int ImageHeight,
+            byte PackedFields, boolean LocalColorTableFlag,
+            boolean InterlaceFlag, boolean SortFlag,
+            byte SizeofLocalColorTable, byte LocalColorTable[],
+            byte ImageData[])
+    {
+        super(blockCode);
 
-		this.imageLeftPosition = ImageLeftPosition;
-		this.imageTopPosition = ImageTopPosition;
-		this.imageWidth = ImageWidth;
-		this.imageHeight = ImageHeight;
-		this.packedFields = PackedFields;
-		this.localColorTableFlag = LocalColorTableFlag;
-		this.interlaceFlag = InterlaceFlag;
-		this.sortFlag = SortFlag;
-		this.sizeOfLocalColorTable = SizeofLocalColorTable;
+        this.imageLeftPosition = ImageLeftPosition;
+        this.imageTopPosition = ImageTopPosition;
+        this.imageWidth = ImageWidth;
+        this.imageHeight = ImageHeight;
+        this.packedFields = PackedFields;
+        this.localColorTableFlag = LocalColorTableFlag;
+        this.interlaceFlag = InterlaceFlag;
+        this.sortFlag = SortFlag;
+        this.sizeOfLocalColorTable = SizeofLocalColorTable;
 
-		this.localColorTable = LocalColorTable;
-		this.imageData = ImageData;
-	}
+        this.localColorTable = LocalColorTable;
+        this.imageData = ImageData;
+    }
 }
\ No newline at end of file