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);
+        }
+    }
+}