Mercurial > projects > dwt2
diff org.eclipse.swt.win32.win32.x86/src/org/eclipse/swt/internal/image/GIFFileFormat.d @ 0:6dd524f61e62
add dwt win and basic java stuff
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Mon, 02 Mar 2009 14:44:16 +0100 |
parents | |
children | f36c67707cb3 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.swt.win32.win32.x86/src/org/eclipse/swt/internal/image/GIFFileFormat.d Mon Mar 02 14:44:16 2009 +0100 @@ -0,0 +1,632 @@ +/******************************************************************************* + * Copyright (c) 2000, 2008 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + * Port to the D programming language: + * Frank Benoit <benoit@tionex.de> + *******************************************************************************/ +module org.eclipse.swt.internal.image.GIFFileFormat; + +public import org.eclipse.swt.internal.image.FileFormat; +public import org.eclipse.swt.graphics.PaletteData; +import org.eclipse.swt.internal.image.LEDataInputStream; +import org.eclipse.swt.internal.image.LZWCodec; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.ImageData; +import org.eclipse.swt.graphics.ImageLoaderEvent; +import org.eclipse.swt.graphics.ImageLoader; +import tango.core.Exception; +import java.lang.all; + + +final class GIFFileFormat : FileFormat { + String signature; + int screenWidth, screenHeight, backgroundPixel, bitsPerPixel, defaultDepth; + int disposalMethod = 0; + int delayTime = 0; + int transparentPixel = -1; + int repeatCount = 1; + + static final int GIF_APPLICATION_EXTENSION_BLOCK_ID = 0xFF; + static final int GIF_GRAPHICS_CONTROL_BLOCK_ID = 0xF9; + static final int GIF_PLAIN_TEXT_BLOCK_ID = 0x01; + static final int GIF_COMMENT_BLOCK_ID = 0xFE; + static final int GIF_EXTENSION_BLOCK_ID = 0x21; + static final int GIF_IMAGE_BLOCK_ID = 0x2C; + static final int GIF_TRAILER_ID = 0x3B; + static final byte[] GIF89a = cast(byte[])"GIF89a"; + static final byte[] NETSCAPE2_0 = cast(byte[])"NETSCAPE2.0"; + + /** + * Answer a palette containing numGrays + * shades of gray, ranging from black to white. + */ + static PaletteData grayRamp(int numGrays) { + int n = numGrays - 1; + RGB[] colors = new RGB[numGrays]; + for (int i = 0; i < numGrays; i++) { + int intensity = cast(byte)((i * 3) * 256 / n); + colors[i] = new RGB(intensity, intensity, intensity); + } + return new PaletteData(colors); + } + + override bool isFileFormat(LEDataInputStream stream) { + try { + byte[3] signature; + stream.read(signature); + stream.unread(signature); + return signature[0] is 'G' && signature[1] is 'I' && signature[2] is 'F'; + } catch (Exception e) { + return false; + } + } + + /** + * Load the GIF image(s) stored in the input stream. + * Return an array of ImageData representing the image(s). + */ + override ImageData[] loadFromByteStream() { + byte[3] signature; + byte[3] versionBytes; + byte[7] block; + try { + inputStream.read(signature); + if (!(signature[0] is 'G' && signature[1] is 'I' && signature[2] is 'F')) + SWT.error(SWT.ERROR_INVALID_IMAGE); + + inputStream.read(versionBytes); + + inputStream.read(block); + } catch (IOException e) { + SWT.error(SWT.ERROR_IO, e); + } + screenWidth = (block[0] & 0xFF) | ((block[1] & 0xFF) << 8); + loader.logicalScreenWidth = screenWidth; + screenHeight = (block[2] & 0xFF) | ((block[3] & 0xFF) << 8); + loader.logicalScreenHeight = screenHeight; + byte bitField = block[4]; + backgroundPixel = block[5] & 0xFF; + //aspect = block[6] & 0xFF; + bitsPerPixel = ((bitField >> 4) & 0x07) + 1; + defaultDepth = (bitField & 0x7) + 1; + PaletteData palette = null; + if ((bitField & 0x80) !is 0) { + // Global palette. + //sorted = (bitField & 0x8) !is 0; + palette = readPalette(1 << defaultDepth); + } else { + // No global palette. + //sorted = false; + backgroundPixel = -1; + defaultDepth = bitsPerPixel; + } + loader.backgroundPixel = backgroundPixel; + + getExtensions(); + int id = readID(); + ImageData[] images = new ImageData[0]; + while (id is GIF_IMAGE_BLOCK_ID) { + ImageData image = readImageBlock(palette); + if (loader.hasListeners()) { + loader.notifyListeners(new ImageLoaderEvent(loader, image, 3, true)); + } + ImageData[] oldImages = images; + images = new ImageData[oldImages.length + 1]; + System.arraycopy(oldImages, 0, images, 0, oldImages.length); + images[images.length - 1] = image; + //images ~= image; + try { + /* Read the 0-byte terminator at the end of the image. */ + id = inputStream.read(); + if (id > 0) { + /* We read the terminator earlier. */ + byte[1] arr; + arr[0] = id; + inputStream.unread( arr ); + } + } catch (IOException e) { + SWT.error(SWT.ERROR_IO, e); + } + getExtensions(); + id = readID(); + } + return images; + } + + /** + * Read and return the next block or extension identifier from the file. + */ + int readID() { + try { + return inputStream.read(); + } catch (IOException e) { + SWT.error(SWT.ERROR_IO, e); + } + return -1; + } + + /** + * Read extensions until an image descriptor appears. + * In the future, if we care about the extensions, they + * should be properly grouped with the image data before + * which they appeared. Right now, the interesting parts + * of some extensions are kept, but the rest is discarded. + * Throw an error if an error occurs. + */ + void getExtensions() { + int id = readID(); + while (id !is GIF_IMAGE_BLOCK_ID && id !is GIF_TRAILER_ID && id > 0) { + if (id is GIF_EXTENSION_BLOCK_ID) { + readExtension(); + } else { + SWT.error(SWT.ERROR_INVALID_IMAGE); + } + id = readID(); + } + if (id is GIF_IMAGE_BLOCK_ID || id is GIF_TRAILER_ID) { + try { + byte[1] arr; + arr[0] = id; + inputStream.unread(arr); + } catch (IOException e) { + SWT.error(SWT.ERROR_IO, e); + } + } + } + + /** + * Read a control extension. + * Return the extension block data. + */ + byte[] readExtension() { + int extensionID = readID(); + if (extensionID is GIF_COMMENT_BLOCK_ID) + return readCommentExtension(); + if (extensionID is GIF_PLAIN_TEXT_BLOCK_ID) + return readPlainTextExtension(); + if (extensionID is GIF_GRAPHICS_CONTROL_BLOCK_ID) + return readGraphicsControlExtension(); + if (extensionID is GIF_APPLICATION_EXTENSION_BLOCK_ID) + return readApplicationExtension(); + // Otherwise, we don't recognize the block. If the + // field size is correct, we can just skip over + // the block contents. + try { + int extSize = inputStream.read(); + if (extSize < 0) { + SWT.error(SWT.ERROR_INVALID_IMAGE); + } + byte[] ext = new byte[extSize]; + inputStream.read(ext, 0, extSize); + return ext; + } catch (IOException e) { + SWT.error(SWT.ERROR_IO, e); + return null; + } + } + + /** + * We have just read the Comment extension identifier + * from the input stream. Read in the rest of the comment + * and return it. GIF comment blocks are variable size. + */ + byte[] readCommentExtension() { + try { + byte[] comment = new byte[0]; + byte[] block = new byte[255]; + int size = inputStream.read(); + while ((size > 0) && (inputStream.read(block, 0, size) !is -1)) { + byte[] oldComment = comment; + comment = new byte[oldComment.length + size]; + System.arraycopy(oldComment, 0, comment, 0, oldComment.length); + System.arraycopy(block, 0, comment, oldComment.length, size); + //comment ~= block[ 0 .. size ]; + size = inputStream.read(); + } + return comment; + } catch (Exception e) { + SWT.error(SWT.ERROR_IO, e); + return null; + } + } + + /** + * We have just read the PlainText extension identifier + * from the input stream. Read in the plain text info and text, + * and return the text. GIF plain text blocks are variable size. + */ + byte[] readPlainTextExtension() { + try { + // Read size of block = 0x0C. + inputStream.read(); + // Read the text information (x, y, width, height, colors). + byte[] info = new byte[12]; + inputStream.read(info); + // Read the text. + byte[] text = new byte[0]; + byte[] block = new byte[255]; + int size = inputStream.read(); + while ((size > 0) && (inputStream.read(block, 0, size) !is -1)) { + byte[] oldText = text; + text = new byte[oldText.length + size]; + System.arraycopy(oldText, 0, text, 0, oldText.length); + System.arraycopy(block, 0, text, oldText.length, size); + //text ~= block[ 0 .. size ]; + size = inputStream.read(); + } + return text; + } catch (Exception e) { + SWT.error(SWT.ERROR_IO, e); + return null; + } + } + + /** + * We have just read the GraphicsControl extension identifier + * from the input stream. Read in the control information, store + * it, and return it. + */ + byte[] readGraphicsControlExtension() { + try { + // Read size of block = 0x04. + inputStream.read(); + // Read the control block. + byte[] controlBlock = new byte[4]; + inputStream.read(controlBlock); + byte bitField = controlBlock[0]; + // Store the user input field. + //userInput = (bitField & 0x02) !is 0; + // Store the disposal method. + disposalMethod = (bitField >> 2) & 0x07; + // Store the delay time. + delayTime = (controlBlock[1] & 0xFF) | ((controlBlock[2] & 0xFF) << 8); + // Store the transparent color. + if ((bitField & 0x01) !is 0) { + transparentPixel = controlBlock[3] & 0xFF; + } else { + transparentPixel = -1; + } + // Read block terminator. + inputStream.read(); + return controlBlock; + } catch (Exception e) { + SWT.error(SWT.ERROR_IO, e); + return null; + } + } + + /** + * We have just read the Application extension identifier + * from the input stream. Read in the rest of the extension, + * look for and store 'number of repeats', and return the data. + */ + byte[] readApplicationExtension() { + try { + // Read size of block = 0x0B. + inputStream.read(); + // Read application identifier. + byte[] application = new byte[8]; + inputStream.read(application); + // Read authentication code. + byte[] authentication = new byte[3]; + inputStream.read(authentication); + // Read application data. + byte[] data = new byte[0]; + byte[] block = new byte[255]; + int size = inputStream.read(); + while ((size > 0) && (inputStream.read(block, 0, size) !is -1)) { + byte[] oldData = data; + data = new byte[oldData.length + size]; + System.arraycopy(oldData, 0, data, 0, oldData.length); + System.arraycopy(block, 0, data, oldData.length, size); + //data ~= block[ 0 .. size ]; + size = inputStream.read(); + } + // Look for the NETSCAPE 'repeat count' field for an animated GIF. + bool netscape = + application[0] is 'N' && + application[1] is 'E' && + application[2] is 'T' && + application[3] is 'S' && + application[4] is 'C' && + application[5] is 'A' && + application[6] is 'P' && + application[7] is 'E'; + bool authentic = + authentication[0] is '2' && + authentication[1] is '.' && + authentication[2] is '0'; + if (netscape && authentic && data[0] is 01) { //$NON-NLS-1$ //$NON-NLS-2$ + repeatCount = (data[1] & 0xFF) | ((data[2] & 0xFF) << 8); + loader.repeatCount = repeatCount; + } + return data; + } catch (Exception e) { + SWT.error(SWT.ERROR_IO, e); + return null; + } + } + + /** + * Return a DeviceIndependentImage representing the + * image block at the current position in the input stream. + * Throw an error if an error occurs. + */ + ImageData readImageBlock(PaletteData defaultPalette) { + int depth; + PaletteData palette; + byte[] block = new byte[9]; + try { + inputStream.read(block); + } catch (IOException e) { + SWT.error(SWT.ERROR_IO, e); + } + int left = (block[0] & 0xFF) | ((block[1] & 0xFF) << 8); + int top = (block[2] & 0xFF) | ((block[3] & 0xFF) << 8); + int width = (block[4] & 0xFF) | ((block[5] & 0xFF) << 8); + int height = (block[6] & 0xFF) | ((block[7] & 0xFF) << 8); + byte bitField = block[8]; + bool interlaced = (bitField & 0x40) !is 0; + //bool sorted = (bitField & 0x20) !is 0; + if ((bitField & 0x80) !is 0) { + // Local palette. + depth = (bitField & 0x7) + 1; + palette = readPalette(1 << depth); + } else { + // No local palette. + depth = defaultDepth; + palette = defaultPalette; + } + /* Work around: Ignore the case where a GIF specifies an + * invalid index for the transparent pixel that is larger + * than the number of entries in the palette. */ + if (transparentPixel > 1 << depth) { + transparentPixel = -1; + } + // Promote depth to next highest supported value. + if (!(depth is 1 || depth is 4 || depth is 8)) { + if (depth < 4) + depth = 4; + else + depth = 8; + } + if (palette is null) { + palette = grayRamp(1 << depth); + } + int initialCodeSize = -1; + try { + initialCodeSize = inputStream.read(); + } catch (IOException e) { + SWT.error(SWT.ERROR_IO, e); + } + if (initialCodeSize < 0) { + SWT.error(SWT.ERROR_INVALID_IMAGE); + } + ImageData image = ImageData.internal_new( + width, + height, + depth, + palette, + 4, + null, + 0, + null, + null, + -1, + transparentPixel, + SWT.IMAGE_GIF, + left, + top, + disposalMethod, + delayTime); + LZWCodec codec = new LZWCodec(); + codec.decode(inputStream, loader, image, interlaced, initialCodeSize); + return image; + } + + /** + * Read a palette from the input stream. + */ + PaletteData readPalette(int numColors) { + byte[] bytes = new byte[numColors * 3]; + try { + if (inputStream.read(bytes) !is bytes.length) + SWT.error(SWT.ERROR_INVALID_IMAGE); + } catch (IOException e) { + SWT.error(SWT.ERROR_IO, e); + } + RGB[] colors = new RGB[numColors]; + for (int i = 0; i < numColors; i++) + colors[i] = new RGB(bytes[i*3] & 0xFF, + bytes[i*3+1] & 0xFF, bytes[i*3+2] & 0xFF); + return new PaletteData(colors); + } + + override void unloadIntoByteStream(ImageLoader loader) { + + /* Step 1: Acquire GIF parameters. */ + ImageData[] data = loader.data; + int frameCount = data.length; + bool multi = frameCount > 1; + ImageData firstImage = data[0]; + int logicalScreenWidth = multi ? loader.logicalScreenWidth : firstImage.width; + int logicalScreenHeight = multi ? loader.logicalScreenHeight : firstImage.height; + int backgroundPixel = loader.backgroundPixel; + int depth = firstImage.depth; + PaletteData palette = firstImage.palette; + RGB[] colors = palette.getRGBs(); + short globalTable = 1; + + /* Step 2: Check for validity and global/local color map. */ + if (!(depth is 1 || depth is 4 || depth is 8)) { + SWT.error(SWT.ERROR_UNSUPPORTED_DEPTH); + } + for (int i=0; i<frameCount; i++) { + if (data[i].palette.isDirect) { + SWT.error(SWT.ERROR_INVALID_IMAGE); + } + if (multi) { + if (!(data[i].height <= logicalScreenHeight && data[i].width <= logicalScreenWidth && data[i].depth is depth)) { + SWT.error(SWT.ERROR_INVALID_IMAGE); + } + if (globalTable is 1) { + RGB rgbs[] = data[i].palette.getRGBs(); + if (rgbs.length !is colors.length) { + globalTable = 0; + } else { + for (int j=0; j<colors.length; j++) { + if (!(rgbs[j].red is colors[j].red && + rgbs[j].green is colors[j].green && + rgbs[j].blue is colors[j].blue)) + globalTable = 0; + } + } + } + } + } + + try { + /* Step 3: Write the GIF89a Header and Logical Screen Descriptor. */ + outputStream.write(GIF89a); + int bitField = globalTable*128 + (depth-1)*16 + depth-1; + outputStream.writeShort(cast(short)logicalScreenWidth); + outputStream.writeShort(cast(short)logicalScreenHeight); + outputStream.write(bitField); + outputStream.write(backgroundPixel); + outputStream.write(0); // Aspect ratio is 1:1 + } catch (IOException e) { + SWT.error(SWT.ERROR_IO, e); + } + + /* Step 4: Write Global Color Table if applicable. */ + if (globalTable is 1) { + writePalette(palette, depth); + } + + /* Step 5: Write Application Extension if applicable. */ + if (multi) { + int repeatCount = loader.repeatCount; + try { + outputStream.write(GIF_EXTENSION_BLOCK_ID); + outputStream.write(GIF_APPLICATION_EXTENSION_BLOCK_ID); + outputStream.write(NETSCAPE2_0.length); + outputStream.write(NETSCAPE2_0); + outputStream.write(3); // Three bytes follow + outputStream.write(1); // Extension type + outputStream.writeShort(cast(short) repeatCount); + outputStream.write(0); // Block terminator + } catch (IOException e) { + SWT.error(SWT.ERROR_IO, e); + } + } + + for (int frame=0; frame<frameCount; frame++) { + + /* Step 6: Write Graphics Control Block for each frame if applicable. */ + if (multi || data[frame].transparentPixel !is -1) { + writeGraphicsControlBlock(data[frame]); + } + + /* Step 7: Write Image Header for each frame. */ + int x = data[frame].x; + int y = data[frame].y; + int width = data[frame].width; + int height = data[frame].height; + try { + outputStream.write(GIF_IMAGE_BLOCK_ID); + byte[] block = new byte[9]; + block[0] = cast(byte)(x & 0xFF); + block[1] = cast(byte)((x >> 8) & 0xFF); + block[2] = cast(byte)(y & 0xFF); + block[3] = cast(byte)((y >> 8) & 0xFF); + block[4] = cast(byte)(width & 0xFF); + block[5] = cast(byte)((width >> 8) & 0xFF); + block[6] = cast(byte)(height & 0xFF); + block[7] = cast(byte)((height >> 8) & 0xFF); + block[8] = cast(byte)(globalTable is 0 ? (depth-1) | 0x80 : 0x00); + outputStream.write(block); + } catch (IOException e) { + SWT.error(SWT.ERROR_IO, e); + } + + /* Step 8: Write Local Color Table for each frame if applicable. */ + if (globalTable is 0) { + writePalette(data[frame].palette, depth); + } + + /* Step 9: Write the actual data for each frame. */ + try { + outputStream.write(depth); // Minimum LZW Code size + } catch (IOException e) { + SWT.error(SWT.ERROR_IO, e); + } + (new LZWCodec()).encode(outputStream, data[frame]); + } + + /* Step 10: Write GIF terminator. */ + try { + outputStream.write(0x3B); + } catch (IOException e) { + SWT.error(SWT.ERROR_IO, e); + } + } + + /** + * Write out a GraphicsControlBlock to describe + * the specified device independent image. + */ + void writeGraphicsControlBlock(ImageData image) { + try { + outputStream.write(GIF_EXTENSION_BLOCK_ID); + outputStream.write(GIF_GRAPHICS_CONTROL_BLOCK_ID); + byte[] gcBlock = new byte[4]; + gcBlock[0] = 0; + gcBlock[1] = 0; + gcBlock[2] = 0; + gcBlock[3] = 0; + if (image.transparentPixel !is -1) { + gcBlock[0] = cast(byte)0x01; + gcBlock[3] = cast(byte)image.transparentPixel; + } + if (image.disposalMethod !is 0) { + gcBlock[0] |= cast(byte)((image.disposalMethod & 0x07) << 2); + } + if (image.delayTime !is 0) { + gcBlock[1] = cast(byte)(image.delayTime & 0xFF); + gcBlock[2] = cast(byte)((image.delayTime >> 8) & 0xFF); + } + outputStream.write(cast(byte)gcBlock.length); + outputStream.write(gcBlock); + outputStream.write(0); // Block terminator + } catch (IOException e) { + SWT.error(SWT.ERROR_IO, e); + } + } + + /** + * Write the specified palette to the output stream. + */ + void writePalette(PaletteData palette, int depth) { + byte[] bytes = new byte[(1 << depth) * 3]; + int offset = 0; + for (int i = 0; i < palette.colors.length; i++) { + RGB color = palette.colors[i]; + bytes[offset] = cast(byte)color.red; + bytes[offset + 1] = cast(byte)color.green; + bytes[offset + 2] = cast(byte)color.blue; + offset += 3; + } + try { + outputStream.write(bytes); + } catch (IOException e) { + SWT.error(SWT.ERROR_IO, e); + } + } +}