diff dwt/internal/image/LZWCodec.d @ 0:380af2bdd8e5

Upload of whole dwt tree
author Jacob Carlborg <doob@me.com> <jacob.carlborg@gmail.com>
date Sat, 09 Aug 2008 17:00:02 +0200
parents
children e831403a80a9
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/internal/image/LZWCodec.d	Sat Aug 09 17:00:02 2008 +0200
@@ -0,0 +1,479 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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
+ *******************************************************************************/
+module dwt.internal.image;
+
+
+import dwt.DWT;
+import dwt.graphics.ImageData;
+import dwt.graphics.ImageLoader;
+import dwt.graphics.ImageLoaderEvent;
+
+final class LZWCodec {
+    int bitsPerPixel, blockSize, blockIndex, currentByte, bitsLeft,
+        codeSize, clearCode, endCode, newCodes, topSlot, currentSlot,
+        imageWidth, imageHeight, imageX, imageY, pass, line, codeMask;
+    byte[] block, lineArray;
+    int[] stack, suffix, prefix;
+    LZWNode[] nodeStack;
+    LEDataInputStream inputStream;
+    LEDataOutputStream outputStream;
+    ImageData image;
+    ImageLoader loader;
+    bool interlaced;
+    static final int[] MASK_TABLE = new int[] {
+        0x1, 0x3, 0x7, 0xF, 0x1F, 0x3F, 0x7F,
+        0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF
+    };
+
+/**
+ * Decode the input.
+ */
+void decode() {
+    int code;
+    int oc = 0;
+    int fc = 0;
+    byte[] buf = new byte[imageWidth];
+    int stackIndex = 0;
+    int bufIndex = 0;
+    int c;
+    while ((c = nextCode()) !is endCode) {
+        if (c is clearCode) {
+            codeSize = bitsPerPixel + 1;
+            codeMask = MASK_TABLE[bitsPerPixel];
+            currentSlot = newCodes;
+            topSlot = 1 << codeSize;
+            while ((c = nextCode()) is clearCode) {}
+            if (c !is endCode) {
+                oc = fc = c;
+                buf[bufIndex] = (byte)c;
+                bufIndex++;
+                if (bufIndex is imageWidth) {
+                    nextPutPixels(buf);
+                    bufIndex = 0;
+                }
+            }
+        } else {
+            code = c;
+            if (code >= currentSlot) {
+                code = oc;
+                stack[stackIndex] = fc;
+                stackIndex++;
+            }
+            while (code >= newCodes) {
+                stack[stackIndex] = suffix[code];
+                stackIndex++;
+                code = prefix[code];
+            }
+            stack[stackIndex] = code;
+            stackIndex++;
+            if (currentSlot < topSlot) {
+                fc = code;
+                suffix[currentSlot] = fc;
+                prefix[currentSlot] = oc;
+                currentSlot++;
+                oc = c;
+            }
+            if (currentSlot >= topSlot) {
+                if (codeSize < 12) {
+                    codeMask = MASK_TABLE[codeSize];
+                    codeSize++;
+                    topSlot = topSlot + topSlot;
+                }
+            }
+            while (stackIndex > 0) {
+                stackIndex--;
+                buf[bufIndex] = (byte)stack[stackIndex];
+                bufIndex++;
+                if (bufIndex is imageWidth) {
+                    nextPutPixels(buf);
+                    bufIndex = 0;
+                }
+            }
+        }
+    }
+    if (bufIndex !is 0 && line < imageHeight) {
+        nextPutPixels(buf);
+    }
+}
+/**
+ * Decode the LZW-encoded bytes in the given byte stream
+ * into the given DeviceIndependentImage.
+ */
+public void decode(LEDataInputStream inputStream, ImageLoader loader, ImageData image, bool interlaced, int depth) {
+    this.inputStream = inputStream;
+    this.loader = loader;
+    this.image = image;
+    this.interlaced = interlaced;
+    this.bitsPerPixel = depth;
+    initializeForDecoding();
+    decode();
+}
+/**
+ * Encode the image.
+ */
+void encode() {
+    nextPutCode(clearCode);
+    int lastPrefix = encodeLoop();
+    nextPutCode(lastPrefix);
+    nextPutCode(endCode);
+
+    // Write out last partial block
+    if (bitsLeft is 8) {
+        block[0] = (byte)(blockIndex - 1); // Nothing in last byte
+    } else {
+        block[0] = (byte)(blockIndex); // Last byte has data
+    }
+    writeBlock();
+
+    // Write out empty block to indicate the end (if needed)
+    if (block[0] !is 0) {
+        block[0] = 0;
+        writeBlock();
+    }
+}
+/**
+ * Encode the bytes into the given byte stream
+ * from the given DeviceIndependentImage.
+ */
+public void encode(LEDataOutputStream byteStream, ImageData image) {
+    this.outputStream = byteStream;
+    this.image = image;
+    initializeForEncoding();
+    encode();
+}
+/**
+ * Encoding loop broken out to allow early return.
+ */
+int encodeLoop() {
+    int pixel = nextPixel();
+    bool found;
+    LZWNode node;
+    while (true) {
+        int currentPrefix = pixel;
+        node = nodeStack[currentPrefix];
+        found = true;
+        pixel = nextPixel();
+        if (pixel < 0)
+            return currentPrefix;
+        while (found && (node.children !is null)) {
+            node = node.children;
+            while (found && (node.suffix !is pixel)) {
+                if (pixel < node.suffix) {
+                    if (node.left is null) {
+                        node.left = new LZWNode();
+                        found = false;
+                    }
+                    node = node.left;
+                } else {
+                    if (node.right is null) {
+                        node.right = new LZWNode();
+                        found = false;
+                    }
+                    node = node.right;
+                }
+            }
+            if (found) {
+                currentPrefix = node.code;
+                pixel = nextPixel();
+                if (pixel < 0)
+                    return currentPrefix;
+            }
+        }
+        if (found) {
+            node.children = new LZWNode();
+            node = node.children;
+        }
+        node.children = null;
+        node.left = null;
+        node.right = null;
+        node.code = currentSlot;
+        node.prefix = currentPrefix;
+        node.suffix = pixel;
+        nextPutCode(currentPrefix);
+        currentSlot++;
+        // Off by one?
+        if (currentSlot < 4096) {
+            if (currentSlot > topSlot) {
+                codeSize++;
+                codeMask = MASK_TABLE[codeSize - 1];
+                topSlot *= 2;
+            }
+        } else {
+            nextPutCode(clearCode);
+            for (int i = 0; i < nodeStack.length; i++)
+                nodeStack[i].children = null;
+            codeSize = bitsPerPixel + 1;
+            codeMask = MASK_TABLE[codeSize - 1];
+            currentSlot = newCodes;
+            topSlot = 1 << codeSize;
+        }
+    }
+}
+/**
+ * Initialize the receiver for decoding the given
+ * byte array.
+ */
+void initializeForDecoding() {
+    pass = 1;
+    line = 0;
+    codeSize = bitsPerPixel + 1;
+    topSlot = 1 << codeSize;
+    clearCode = 1 << bitsPerPixel;
+    endCode = clearCode + 1;
+    newCodes = currentSlot = endCode + 1;
+    currentByte = -1;
+    blockSize = bitsLeft = 0;
+    blockIndex = 0;
+    codeMask = MASK_TABLE[codeSize - 1];
+    stack = new int[4096];
+    suffix = new int[4096];
+    prefix = new int[4096];
+    block = new byte[256];
+    imageWidth = image.width;
+    imageHeight = image.height;
+}
+/**
+ * Initialize the receiver for encoding the given
+ * byte array.
+ */
+void initializeForEncoding() {
+    interlaced = false;
+    bitsPerPixel = image.depth;
+    codeSize = bitsPerPixel + 1;
+    topSlot = 1 << codeSize;
+    clearCode = 1 << bitsPerPixel;
+    endCode = clearCode + 1;
+    newCodes = currentSlot = endCode + 1;
+    bitsLeft = 8;
+    currentByte = 0;
+    blockIndex = 1;
+    blockSize = 255;
+    block = new byte[blockSize];
+    block[0] = (byte)(blockSize - 1);
+    nodeStack = new LZWNode[1 << bitsPerPixel];
+    for (int i = 0; i < nodeStack.length; i++) {
+        LZWNode node = new LZWNode();
+        node.code = i + 1;
+        node.prefix = -1;
+        node.suffix = i + 1;
+        nodeStack[i] = node;
+    }
+    imageWidth = image.width;
+    imageHeight = image.height;
+    imageY = -1;
+    lineArray = new byte[imageWidth];
+    imageX = imageWidth + 1; // Force a read
+}
+/**
+ * Answer the next code from the input byte array.
+ */
+int nextCode() {
+    int code;
+    if (bitsLeft is 0) {
+        if (blockIndex >= blockSize) {
+            blockSize = readBlock();
+            blockIndex = 0;
+            if (blockSize is 0) return endCode;
+        }
+        blockIndex++;
+        currentByte = block[blockIndex] & 0xFF;
+        bitsLeft = 8;
+        code = currentByte;
+    } else {
+        int shift = bitsLeft - 8;
+        if (shift < 0)
+            code = currentByte >> (0 - shift);
+        else
+            code = currentByte << shift;
+    }
+    while (codeSize > bitsLeft) {
+        if (blockIndex >= blockSize) {
+            blockSize = readBlock();
+            blockIndex = 0;
+            if (blockSize is 0) return endCode;
+        }
+        blockIndex++;
+        currentByte = block[blockIndex] & 0xFF;
+        code += currentByte << bitsLeft;
+        bitsLeft += 8;
+    }
+    bitsLeft -= codeSize;
+    return code & codeMask;
+}
+/**
+ * Answer the next pixel to encode in the image
+ */
+int nextPixel() {
+    imageX++;
+    if (imageX > imageWidth) {
+        imageY++;
+        if (imageY >= imageHeight) {
+            return -1;
+        } else {
+            nextPixels(lineArray, imageWidth);
+        }
+        imageX = 1;
+    }
+    return this.lineArray[imageX - 1] & 0xFF;
+}
+/**
+ * Copy a row of pixel values from the image.
+ */
+void nextPixels(byte[] buf, int lineWidth) {
+    if (image.depth is 8) {
+        System.arraycopy(image.data, imageY * image.bytesPerLine, buf, 0, lineWidth);
+    } else {
+        image.getPixels(0, imageY, lineWidth, buf, 0);
+    }
+}
+/**
+ * Output aCode to the output stream.
+ */
+void nextPutCode(int aCode) {
+    int codeToDo = aCode;
+    int codeBitsToDo = codeSize;
+    // Fill in the remainder of the current byte with the
+    // *high-order* bits of the code.
+    int c = codeToDo & MASK_TABLE[bitsLeft - 1];
+    currentByte = currentByte | (c << (8 - bitsLeft));
+    block[blockIndex] = (byte)currentByte;
+    codeBitsToDo -= bitsLeft;
+    if (codeBitsToDo < 1) {
+        // The whole code fit in the first byte, so we are done.
+        bitsLeft -= codeSize;
+        if (bitsLeft is 0) {
+            // We used the whole last byte, so get ready
+            // for the next one.
+            bitsLeft = 8;
+            blockIndex++;
+            if (blockIndex >= blockSize) {
+                writeBlock();
+                blockIndex = 1;
+            }
+            currentByte = 0;
+        }
+        return;
+    }
+    codeToDo = codeToDo >> bitsLeft;
+
+    // Fill in any remaining whole bytes (i.e. not the last one!)
+    blockIndex++;
+    if (blockIndex >= blockSize) {
+        writeBlock();
+        blockIndex = 1;
+    }
+    while (codeBitsToDo >= 8) {
+        currentByte = codeToDo & 0xFF;
+        block[blockIndex] = (byte)currentByte;
+        codeToDo = codeToDo >> 8;
+        codeBitsToDo -= 8;
+        blockIndex++;
+        if (blockIndex >= blockSize) {
+            writeBlock();
+            blockIndex = 1;
+        }
+    }
+    // Fill the *low-order* bits of the last byte with the remainder
+    bitsLeft = 8 - codeBitsToDo;
+    currentByte = codeToDo;
+    block[blockIndex] = (byte)currentByte;
+}
+/**
+ * Copy a row of pixel values to the image.
+ */
+void nextPutPixels(byte[] buf) {
+    if (image.depth is 8) {
+        // Slight optimization for depth = 8.
+        int start = line * image.bytesPerLine;
+        for (int i = 0; i < imageWidth; i++)
+            image.data[start + i] = buf[i];
+    } else {
+        image.setPixels(0, line, imageWidth, buf, 0);
+    }
+    if (interlaced) {
+        if (pass is 1) {
+            copyRow(buf, 7);
+            line += 8;
+        } else if (pass is 2) {
+            copyRow(buf, 3);
+            line += 8;
+        } else if (pass is 3) {
+            copyRow(buf, 1);
+            line += 4;
+        } else if (pass is 4) {
+            line += 2;
+        } else if (pass is 5) {
+            line += 0;
+        }
+        if (line >= imageHeight) {
+            pass++;
+            if (pass is 2) line = 4;
+            else if (pass is 3) line = 2;
+            else if (pass is 4) line = 1;
+            else if (pass is 5) line = 0;
+            if (pass < 5) {
+                if (loader.hasListeners()) {
+                    ImageData imageCopy = (ImageData) image.clone();
+                    loader.notifyListeners(
+                        new ImageLoaderEvent(loader, imageCopy, pass - 2, false));
+                }
+            }
+        }
+        if (line >= imageHeight) line = 0;
+    } else {
+        line++;
+    }
+}
+/**
+ * Copy duplicate rows of pixel values to the image.
+ * This is to fill in rows if the image is interlaced.
+ */
+void copyRow(byte[] buf, int copies) {
+    for (int i = 1; i <= copies; i++) {
+        if (line + i < imageHeight) {
+            image.setPixels(0, line + i, imageWidth, buf, 0);
+        }
+    }
+}
+/**
+ * Read a block from the byte stream.
+ * Return the number of bytes read.
+ * Throw an exception if the block could not be read.
+ */
+int readBlock() {
+    int size = -1;
+    try {
+        size = inputStream.read();
+        if (size is -1) {
+            DWT.error(DWT.ERROR_INVALID_IMAGE);
+        }
+        block[0] = (byte)size;
+        size = inputStream.read(block, 1, size);
+        if (size is -1) {
+            DWT.error(DWT.ERROR_INVALID_IMAGE);
+        }
+    } catch (Exception e) {
+        DWT.error(DWT.ERROR_IO, e);
+    }
+    return size;
+}
+/**
+ * Write a block to the byte stream.
+ * Throw an exception if the block could not be written.
+ */
+void writeBlock() {
+    try {
+        outputStream.write(block, 0, (block[0] & 0xFF) + 1);
+    } catch (Exception e) {
+        DWT.error(DWT.ERROR_IO, e);
+    }
+}
+}