package org.eclipse.swt.internal.image; | |
/* | |
* Copyright (c) 2000, 2002 IBM Corp. All rights reserved. | |
* This file is made available under the terms of the Common Public License v1.0 | |
* which accompanies this distribution, and is available at | |
* http://www.eclipse.org/legal/cpl-v10.html | |
*/ | |
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, buffer; | |
byte[] block, lineArray; | |
int[] maskTable, stack, suffix, prefix; | |
LZWNode[] nodeStack; | |
LEDataInputStream inputStream; | |
LEDataOutputStream outputStream; | |
ImageData image; | |
ImageLoader loader; | |
boolean interlaced; | |
/** | |
* 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 = maskTable[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) { | |
if (code > currentSlot) | |
SWT.error(SWT.ERROR_INVALID_IMAGE); | |
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 = maskTable[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 = maskTable[codeSize - 1]; | |
topSlot *= 2; | |
} | |
} else { | |
nextPutCode(clearCode); | |
for (int i = 0; i < nodeStack.length; i++) | |
nodeStack[i].children = null; | |
codeSize = bitsPerPixel + 1; | |
codeMask = maskTable[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; | |
maskTable = new int[] { | |
0x1, 0x3, 0x7, 0xF, 0x1F, 0x3F, 0x7F, | |
0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF | |
}; | |
codeMask = maskTable[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); | |
maskTable = new int[] { | |
0x1, 0x3, 0x7, 0xF, 0x1F, 0x3F, 0x7F, | |
0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF | |
}; | |
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 & maskTable[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); | |
} | |
} | |
} |