| /******************************************************************************* |
| * Copyright (c) 2000, 2011 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 |
| *******************************************************************************/ |
| package org.eclipse.swt.internal.image; |
| |
| |
| import org.eclipse.swt.*; |
| import org.eclipse.swt.graphics.*; |
| |
| 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; |
| boolean 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()) != endCode) { |
| if (c == clearCode) { |
| codeSize = bitsPerPixel + 1; |
| codeMask = MASK_TABLE[bitsPerPixel]; |
| currentSlot = newCodes; |
| topSlot = 1 << codeSize; |
| while ((c = nextCode()) == clearCode) {} |
| if (c != endCode) { |
| oc = fc = c; |
| buf[bufIndex] = (byte)c; |
| bufIndex++; |
| if (bufIndex == 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 == imageWidth) { |
| nextPutPixels(buf); |
| bufIndex = 0; |
| } |
| } |
| } |
| } |
| if (bufIndex != 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, boolean 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 == 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] != 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(); |
| boolean found; |
| LZWNode node; |
| while (true) { |
| int currentPrefix = pixel; |
| node = nodeStack[currentPrefix]; |
| found = true; |
| pixel = nextPixel(); |
| if (pixel < 0) |
| return currentPrefix; |
| while (found && (node.children != null)) { |
| node = node.children; |
| while (found && (node.suffix != pixel)) { |
| if (pixel < node.suffix) { |
| if (node.left == null) { |
| node.left = new LZWNode(); |
| found = false; |
| } |
| node = node.left; |
| } else { |
| if (node.right == 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 == 0) { |
| if (blockIndex >= blockSize) { |
| blockSize = readBlock(); |
| blockIndex = 0; |
| if (blockSize == 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 == 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 == 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 == 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 == 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 == 1) { |
| copyRow(buf, 7); |
| line += 8; |
| } else if (pass == 2) { |
| copyRow(buf, 3); |
| line += 8; |
| } else if (pass == 3) { |
| copyRow(buf, 1); |
| line += 4; |
| } else if (pass == 4) { |
| line += 2; |
| } else if (pass == 5) { |
| line += 0; |
| } |
| if (line >= imageHeight) { |
| pass++; |
| if (pass == 2) line = 4; |
| else if (pass == 3) line = 2; |
| else if (pass == 4) line = 1; |
| else if (pass == 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 == -1) { |
| SWT.error(SWT.ERROR_INVALID_IMAGE); |
| } |
| block[0] = (byte)size; |
| size = inputStream.read(block, 1, size); |
| if (size == -1) { |
| SWT.error(SWT.ERROR_INVALID_IMAGE); |
| } |
| } catch (Exception e) { |
| SWT.error(SWT.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) { |
| SWT.error(SWT.ERROR_IO, e); |
| } |
| } |
| } |