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